diff --git a/include/swift/RemoteInspection/ReflectionContext.h b/include/swift/RemoteInspection/ReflectionContext.h index 957654cba4ac1..48bcdc842b2fe 100644 --- a/include/swift/RemoteInspection/ReflectionContext.h +++ b/include/swift/RemoteInspection/ReflectionContext.h @@ -102,17 +102,25 @@ template struct ELFTraits; template <> struct ELFTraits { using Header = const struct llvm::ELF::Elf32_Ehdr; - using Section = const struct llvm::ELF::Elf32_Shdr; + using SectionHeader = const struct llvm::ELF::Elf32_Shdr; + using ProgramHeader = const struct llvm::ELF::Elf32_Phdr; using Offset = llvm::ELF::Elf32_Off; using Size = llvm::ELF::Elf32_Word; + using Word = llvm::ELF::Elf32_Word; + using Half = llvm::ELF::Elf32_Half; + using Pointer = uint32_t; static constexpr unsigned char ELFClass = llvm::ELF::ELFCLASS32; }; template <> struct ELFTraits { using Header = const struct llvm::ELF::Elf64_Ehdr; - using Section = const struct llvm::ELF::Elf64_Shdr; + using SectionHeader = const struct llvm::ELF::Elf64_Shdr; + using ProgramHeader = const struct llvm::ELF::Elf64_Phdr; using Offset = llvm::ELF::Elf64_Off; using Size = llvm::ELF::Elf64_Xword; + using Word = llvm::ELF::Elf64_Word; + using Half = llvm::ELF::Elf64_Half; + using Pointer = uint64_t; static constexpr unsigned char ELFClass = llvm::ELF::ELFCLASS64; }; @@ -530,255 +538,181 @@ class ReflectionContext RemoteAddress ImageStart, std::optional FileBuffer, llvm::SmallVector PotentialModuleNames = {}) { - // When reading from the FileBuffer we can simply return a pointer to - // the underlying data. - // When reading from the process, we need to keep the memory around - // until the end of the function, so we store it inside ReadDataBuffer. - // We do this so in both cases we can return a simple pointer. - std::vector ReadDataBuffer; - auto readData = [&](uint64_t Offset, uint64_t Size) -> const void * { - if (FileBuffer.has_value()) { - auto Buffer = FileBuffer.value(); - if (Offset + Size > Buffer.allocatedSize()) - return nullptr; - return (const void *)((uint64_t)Buffer.base() + Offset); - } else { - MemoryReader::ReadBytesResult Buf = - this->getReader().readBytes(ImageStart + Offset, Size); - if (!Buf) - return nullptr; - ReadDataBuffer.push_back(std::move(Buf)); - return ReadDataBuffer.back().get(); - } - }; - const void *Buf = readData(0, sizeof(typename T::Header)); - if (!Buf) - return {}; - auto Hdr = reinterpret_cast(Buf); - assert(Hdr->getFileClass() == T::ELFClass && "invalid ELF file class"); - - // From the header, grab information about the section header table. - uint64_t SectionHdrAddress = Hdr->e_shoff; - uint16_t SectionHdrNumEntries = Hdr->e_shnum; - uint16_t SectionEntrySize = Hdr->e_shentsize; + // ELF headers contain "pointers" from different address spaces. + // One address space refers to the object file at rest. These are "offsets". + // `e_shoff`, `p_phoff`, `sh_offset`, etc... + // + // The other address space refers to where the data is located after reading + // the value into memory. These are "addresses". `sh_addr`, `p_vaddr`. + // + // NOTE: Headers that have both an offset and an address field may place + // the data in a location that is different than simply adding the image + // base offset to the offset. If both are present, the address field should + // be preferred. + std::vector readBuffer; + auto readData = [&](uint64_t offset, uint64_t size) -> const void * { + MemoryReader::ReadBytesResult buf = + this->getReader().readBytes(ImageStart + offset, size); + if (!buf) + return nullptr; + readBuffer.push_back(std::move(buf)); + return readBuffer.back().get(); + }; - if (sizeof(typename T::Section) > SectionEntrySize) + const typename T::Header *ehdr = + reinterpret_cast( + readData(0, sizeof(typename T::Header))); + if (!ehdr) return {}; + assert(ehdr->getFileClass() == T::ELFClass && "incorrect ELF file class"); - // Special handling for large amount of sections. - // From the elf man page, describing e_shnum: + // If e_phnum is PN_XNUM(0xffff), the actual number of program headers is + // stored in the `sh_info` field of the first section header. // - // If the number of entries in the section header table is - // larger than or equal to SHN_LORESERVE (0xff00), e_shnum - // holds the value zero and the real number of entries in the - // section header table is held in the sh_size member of the - // initial entry in section header table. Otherwise, the - // sh_size member of the initial entry in the section header - // table holds the value zero. - if (SectionHdrNumEntries == 0 && SectionEntrySize > 0) { - auto SecBuf = readData(SectionHdrAddress, sizeof(typename T::Section)); - if (!SecBuf) + // Section headers are not guaranteed to be visible after loading an ELF + // object, making it difficult to reliably access that info. + // + // This is an out-of-spec extension to ELF implemented in Linux and FreeBSD. + // Realistically, there are very few binaries with even 100 program headers, + // so hitting 65k is highly unlikely. If we find we need to deal with them, + // we will need to hunt down the original object file, parse out the first + // section header, and read the `sh_info` field from the raw object file. + typename T::Word ePhnum = ehdr->e_phnum; + if (ePhnum == /*PN_XNUM*/ 0xffff) { + if (ehdr->e_shoff == 0 || ehdr->e_shnum == 0) { + assert(false && "Too many program headers to read safely"); + return {}; + } + const typename T::SectionHeader *sectionHdr = + reinterpret_cast( + readData(ehdr->e_shoff + llvm::ELF::SHN_UNDEF, + sizeof(typename T::SectionHeader))); + if (sectionHdr == nullptr) return {}; - const typename T::Section *FirstSectHdr = - reinterpret_cast(SecBuf); - SectionHdrNumEntries = FirstSectHdr->sh_size; + ePhnum = sectionHdr->sh_info; } - if (SectionHdrNumEntries == 0) - return {}; + struct SectionRange { + typename T::Pointer start; + typename T::Pointer stop; + }; - // Collect all the section headers, we need them to look up the - // reflection sections (by name) and the string table. - // We read the section headers from the FileBuffer, since they are - // not mapped in the child process. - std::vector SecHdrVec; - for (unsigned I = 0; I < SectionHdrNumEntries; ++I) { - uint64_t Offset = SectionHdrAddress + (I * SectionEntrySize); - auto SecBuf = readData(Offset, sizeof(typename T::Section)); - if (!SecBuf) - return {}; - const typename T::Section *SecHdr = - reinterpret_cast(SecBuf); + struct MetadataTable { + typename T::Pointer version; + typename T::Pointer base; + typename T::Pointer unused0; + typename T::Pointer unused1; + + SectionRange protocols; + SectionRange protocol_conformances; + SectionRange type_metadata; + SectionRange typeref; + SectionRange reflstr; + SectionRange fieldmd; + SectionRange assocty; + SectionRange replace; + SectionRange replac2; + SectionRange builtin; + SectionRange capture; + SectionRange mpenum; + SectionRange accessible_functions; + SectionRange runtime_attributes; + SectionRange tests; + }; - SecHdrVec.push_back(SecHdr); - } + const MetadataTable *metadataTable = nullptr; + const typename T::ProgramHeader *phdrs = + reinterpret_cast(readData( + ehdr->e_phoff, sizeof(typename T::ProgramHeader) * ePhnum)); + for (unsigned idx = 0; !metadataTable && idx < ePhnum; ++idx) { + const typename T::ProgramHeader *phdr = phdrs + idx; + assert(phdr && "Failed to load program header"); + if (phdr->p_type != llvm::ELF::PT_NOTE) + continue; - // This provides quick access to the section header string table index. - // We also here handle the unlikely even where the section index overflows - // and it's just a pointer to secondary storage (SHN_XINDEX). - uint32_t SecIdx = Hdr->e_shstrndx; - if (SecIdx == llvm::ELF::SHN_XINDEX) { - assert(!SecHdrVec.empty() && "malformed ELF object"); - SecIdx = SecHdrVec[0]->sh_link; - } + // PT_NOTE layout + // namesz (4 bytes) + // descsz (4 bytes) + // type (4 bytes) + // name (namesz bytes) + // desc (descsz bytes) + struct PT_NOTE_HEADER { + uint32_t namesz; + uint32_t descsz; + uint32_t type; + }; - assert(SecIdx < SecHdrVec.size() && "malformed ELF object"); + // Looking for the note with the name "swift6" and the type "0", + // representing the metadata table. + // Each note segment can contain multiple notes. The Swift metadata table + // (currently) only contains one, so we only need to check the first one. + // Looking for name: 'swift6\0', and type: 0x73356d64 ('s5md') + const PT_NOTE_HEADER *note_hdr = reinterpret_cast( + readData(phdr->p_vaddr, sizeof(PT_NOTE_HEADER))); + if (!note_hdr || note_hdr->namesz == 0) + continue; - const typename T::Section *SecHdrStrTab = SecHdrVec[SecIdx]; - typename T::Offset StrTabOffset = SecHdrStrTab->sh_offset; - typename T::Size StrTabSize = SecHdrStrTab->sh_size; + const char *name = reinterpret_cast( + readData(phdr->p_vaddr + sizeof(PT_NOTE_HEADER), note_hdr->namesz)); - auto StrTabBuf = readData(StrTabOffset, StrTabSize); - if (!StrTabBuf) - return {}; - auto StrTab = reinterpret_cast(StrTabBuf); - bool Error = false; - - // GNU ld and lld both merge sections regardless of the - // `SHF_GNU_RETAIN` flag. gold, presently, does not. The Swift - // compiler has a couple of switches that control whether or not - // the reflection sections are stripped; when these are enabled, - // it will _not_ set `SHF_GNU_RETAIN` on the reflection metadata - // sections. However, `swiftrt.o` contains declarations of the - // sections _with_ the `SHF_GNU_RETAIN` flag set, which makes - // sense since at runtime we will only expect to be able to access - // reflection metadata that we said we wanted to exist at runtime. - // - // The upshot is that when linking with gold, we can end up with - // two sets of reflection metadata sections. In a normal build - // where the compiler flags are the same for every linked object, - // we'll have *either* all retained *or* all un-retained sections - // (the retained sections will still exist because of `swiftrt.o`, - // but will be empty). The only time we'd expect to have a mix is - // where some code was compiled with a different setting of the - // metadata stripping flags. If that happens, the code below will - // simply add both sets of reflection sections, with the retained - // ones added first. - // - // See also https://sourceware.org/bugzilla/show_bug.cgi?id=31415. - auto findELFSectionByName = - [&](llvm::StringRef Name, bool Retained) -> std::pair, uint64_t> { - if (Error) - return {nullptr, 0}; - // Now for all the sections, find their name. - for (const typename T::Section *Hdr : SecHdrVec) { - // Skip unused headers - if (Hdr->sh_type == llvm::ELF::SHT_NULL) - continue; - uint32_t Offset = Hdr->sh_name; - const char *Start = (const char *)StrTab + Offset; - uint64_t StringSize = strnlen(Start, StrTabSize - Offset); - if (StringSize > StrTabSize - Offset) { - Error = true; - break; - } - std::string SecName(Start, StringSize); - if (SecName != Name) - continue; - if (Retained != bool(Hdr->sh_flags & llvm::ELF::SHF_GNU_RETAIN)) - continue; - RemoteAddress SecStart = ImageStart + Hdr->sh_addr; - auto SecSize = Hdr->sh_size; - MemoryReader::ReadBytesResult SecBuf; - if (FileBuffer.has_value()) { - // sh_offset gives us the offset to the section in the file, - // while sh_addr gives us the offset in the process. - auto Offset = Hdr->sh_offset; - if (FileBuffer->allocatedSize() < Offset + SecSize) { - Error = true; - break; - } - auto *Buf = malloc(SecSize); - SecBuf = MemoryReader::ReadBytesResult( - Buf, [](const void *ptr) { free(const_cast(ptr)); }); - memcpy((void *)Buf, - (const void *)((uint64_t)FileBuffer->base() + Offset), - SecSize); - } else { - SecBuf = this->getReader().readBytes(SecStart, SecSize); - } - if (!SecBuf) - return {nullptr, 0}; - auto SecContents = RemoteRef(SecStart, SecBuf.get()); - savedBuffers.push_back(std::move(SecBuf)); - return {SecContents, SecSize}; - } - return {nullptr, 0}; - }; - - SwiftObjectFileFormatELF ObjectFileFormat; - auto FieldMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::fieldmd), true); - auto AssocTySec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::assocty), true); - auto BuiltinTySec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::builtin), true); - auto CaptureSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::capture), true); - auto TypeRefMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::typeref), true); - auto ReflStrMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::reflstr), true); - auto ConformMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::conform), true); - auto MPEnumMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::mpenum), true); - - if (Error) - return {}; + if (!name || *name == '\0') + continue; - std::optional result = {}; - - // We succeed if at least one of the sections is present in the - // ELF executable. - if (FieldMdSec.first || AssocTySec.first || BuiltinTySec.first || - CaptureSec.first || TypeRefMdSec.first || ReflStrMdSec.first || - ConformMdSec.first || MPEnumMdSec.first) { - ReflectionInfo info = {{FieldMdSec.first, FieldMdSec.second}, - {AssocTySec.first, AssocTySec.second}, - {BuiltinTySec.first, BuiltinTySec.second}, - {CaptureSec.first, CaptureSec.second}, - {TypeRefMdSec.first, TypeRefMdSec.second}, - {ReflStrMdSec.first, ReflStrMdSec.second}, - {ConformMdSec.first, ConformMdSec.second}, - {MPEnumMdSec.first, MPEnumMdSec.second}, - PotentialModuleNames}; - result = this->addReflectionInfo(info); + if (strncmp(name, "swift6", 6) != 0) + continue; + if (note_hdr->type != 0x73356d64) + continue; + assert(note_hdr->descsz == sizeof(MetadataTable) && + "Note format mismatched\n"); + /// Offset past the section header, the name, and the 1 alignment byte + const uint64_t tableOffset = + sizeof(PT_NOTE_HEADER) + note_hdr->namesz + 1; + metadataTable = reinterpret_cast( + readData(phdr->p_vaddr + tableOffset, note_hdr->descsz)); } - - // Also check for the non-retained versions of the sections; we'll - // only return a single reflection info ID if both are found (and it'll - // be the one for the retained sections if we have them), but we'll - // still add all the reflection information. - FieldMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::fieldmd), false); - AssocTySec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::assocty), false); - BuiltinTySec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::builtin), false); - CaptureSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::capture), false); - TypeRefMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::typeref), false); - ReflStrMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::reflstr), false); - ConformMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::conform), false); - MPEnumMdSec = findELFSectionByName( - ObjectFileFormat.getSectionName(ReflectionSectionKind::mpenum), false); - - if (Error) + if (metadataTable == nullptr) return {}; - if (FieldMdSec.first || AssocTySec.first || BuiltinTySec.first || - CaptureSec.first || TypeRefMdSec.first || ReflStrMdSec.first || - ConformMdSec.first || MPEnumMdSec.first) { - ReflectionInfo info = {{FieldMdSec.first, FieldMdSec.second}, - {AssocTySec.first, AssocTySec.second}, - {BuiltinTySec.first, BuiltinTySec.second}, - {CaptureSec.first, CaptureSec.second}, - {TypeRefMdSec.first, TypeRefMdSec.second}, - {ReflStrMdSec.first, ReflStrMdSec.second}, - {ConformMdSec.first, ConformMdSec.second}, - {MPEnumMdSec.first, MPEnumMdSec.second}, - PotentialModuleNames}; - auto rid = this->addReflectionInfo(info); - if (!result) - result = rid; - } + auto readSection = + [&](const SectionRange &range) -> std::pair, uint64_t> { + const RemoteAddress secStart = ImageStart + range.start; + const uint64_t secSize = range.stop - range.start; + + MemoryReader::ReadBytesResult section = + this->getReader().readBytes(secStart, secSize); + if (!section) + return {nullptr, 0}; + auto sectionContentRef = RemoteRef(secStart, section.get()); + savedBuffers.push_back(std::move(section)); + return {sectionContentRef, secSize}; + }; + +#define INSERT_METADATA_SECTION(range) \ + auto range = readSection(metadataTable->range) + INSERT_METADATA_SECTION(fieldmd); + INSERT_METADATA_SECTION(assocty); + INSERT_METADATA_SECTION(builtin); + INSERT_METADATA_SECTION(capture); + INSERT_METADATA_SECTION(typeref); + INSERT_METADATA_SECTION(reflstr); + INSERT_METADATA_SECTION(protocol_conformances); + INSERT_METADATA_SECTION(mpenum); +#undef INSERT_METADATA_SECTION +#define INSERT_METADATA_SECTION(range) {range.first, range.second} + + ReflectionInfo info = {INSERT_METADATA_SECTION(fieldmd), + INSERT_METADATA_SECTION(assocty), + INSERT_METADATA_SECTION(builtin), + INSERT_METADATA_SECTION(capture), + INSERT_METADATA_SECTION(typeref), + INSERT_METADATA_SECTION(reflstr), + INSERT_METADATA_SECTION(protocol_conformances), + INSERT_METADATA_SECTION(mpenum), + PotentialModuleNames}; - return result; +#undef INSERT_METADATA_SECTION + return this->addReflectionInfo(info); } /// Parses metadata information from an ELF image. Because the Section diff --git a/include/swift/StaticMirror/ObjectFileContext.h b/include/swift/StaticMirror/ObjectFileContext.h index 5b53b3670aa82..0bd0e318a95dc 100644 --- a/include/swift/StaticMirror/ObjectFileContext.h +++ b/include/swift/StaticMirror/ObjectFileContext.h @@ -47,6 +47,8 @@ class Image { uint64_t OffsetOrAddress; }; llvm::DenseMap DynamicRelocations; + // Storage for relocated segment data (ELF only) + std::vector RelocatedSegmentStorage; void scanMachO(const llvm::object::MachOObjectFile *O); diff --git a/lib/StaticMirror/ObjectFileContext.cpp b/lib/StaticMirror/ObjectFileContext.cpp index ed8239ebb183d..ad6148c683c08 100644 --- a/lib/StaticMirror/ObjectFileContext.cpp +++ b/lib/StaticMirror/ObjectFileContext.cpp @@ -140,6 +140,8 @@ void Image::scanELFType(const llvm::object::ELFObjectFile *O) { llvm::consumeError(phdrs.takeError()); } + // Create mutable copies of segment data to apply relocations + std::vector relocatedData; for (auto &ph : *phdrs) { if (ph.p_filesz == 0) continue; @@ -148,7 +150,9 @@ void Image::scanELFType(const llvm::object::ELFObjectFile *O) { if (contents.empty() || contents.size() != ph.p_filesz) continue; - Segments.push_back({ph.p_vaddr, contents}); + // Create mutable copy for relocation application + relocatedData.emplace_back(contents.data(), contents.size()); + Segments.push_back({ph.p_vaddr, StringRef()}); HeaderAddress = std::min(HeaderAddress, (uint64_t)ph.p_vaddr); } @@ -157,13 +161,39 @@ void Image::scanELFType(const llvm::object::ELFObjectFile *O) { auto resolverSupports = resolver.first; auto resolve = resolver.second; - if (!resolverSupports || !resolve) + if (!resolverSupports || !resolve) { + // Even without a resolver, update segments to point to our copies + for (size_t i = 0; i < Segments.size(); ++i) { + RelocatedSegmentStorage.push_back(std::move(relocatedData[i])); + Segments[i].Contents = RelocatedSegmentStorage.back(); + } return; + } auto machine = O->getELFFile().getHeader().e_machine; auto relativeRelocType = llvm::object::getELFRelativeRelocationType(machine); auto globDatRelocType = getELFGlobDatRelocationType(machine); + // Helper to find which segment contains an address and apply relocation + auto applyRelocationToSegment = [&](uint64_t addr, uint64_t value) -> bool { + for (size_t i = 0; i < Segments.size(); ++i) { + uint64_t segStart = Segments[i].Addr; + uint64_t segEnd = segStart + relocatedData[i].size(); + + if (addr >= segStart && addr < segEnd) { + uint64_t offset = addr - segStart; + // Ensure we have enough space for the pointer + if (offset + sizeof(typename ELFT::uint) <= relocatedData[i].size()) { + // Write the relocated value in target endianness + typename ELFT::uint relocValue = value; + memcpy(&relocatedData[i][offset], &relocValue, sizeof(relocValue)); + return true; + } + } + } + return false; + }; + for (auto &S : static_cast(O) ->dynamic_relocation_sections()) { bool isRela = @@ -174,8 +204,11 @@ void Image::scanELFType(const llvm::object::ELFObjectFile *O) { // have to do that ourselves. if (isRela && R.getType() == relativeRelocType) { auto rela = O->getRela(R.getRawDataRefImpl()); - DynamicRelocations.insert( - {R.getOffset(), {{}, HeaderAddress + rela->r_addend}}); + uint64_t relocatedValue = HeaderAddress + rela->r_addend; + + // Apply the relocation to the segment + applyRelocationToSegment(R.getOffset(), relocatedValue); + DynamicRelocations.insert({R.getOffset(), {{}, relocatedValue}}); continue; } @@ -213,6 +246,12 @@ void Image::scanELFType(const llvm::object::ELFObjectFile *O) { DynamicRelocations.insert({R.getOffset(), {*name, offset}}); } } + + // Move relocated data to persistent storage and update segment StringRefs + for (size_t i = 0; i < Segments.size(); ++i) { + RelocatedSegmentStorage.push_back(std::move(relocatedData[i])); + Segments[i].Contents = RelocatedSegmentStorage.back(); + } } void Image::scanELF(const llvm::object::ELFObjectFileBase *O) { @@ -234,12 +273,6 @@ void Image::scanELF(const llvm::object::ELFObjectFileBase *O) { } else { return; } - - // FIXME: ReflectionContext tries to read bits of the ELF structure that - // aren't normally mapped by a phdr. Until that's fixed, - // allow access to the whole file 1:1 in address space that isn't otherwise - // mapped. - Segments.push_back({HeaderAddress, O->getData()}); } void Image::scanCOFF(const llvm::object::COFFObjectFile *O) { diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index d5cc0e0baa780..a2b790be57f48 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -99,7 +99,8 @@ set(swift_runtime_backtracing_sources # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES SwiftRT-COFF.cpp - SwiftRT-ELF-WASM.cpp + SwiftRT-ELF.cpp + SwiftRT-WASM.cpp ${swift_runtime_sources} ${swift_runtime_objc_sources} ${swift_runtime_leaks_sources} @@ -179,7 +180,7 @@ endforeach() # with LTO, force swift runtime to compile without LTO for Linux. add_swift_target_library(swiftImageRegistrationObjectELF OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE - SwiftRT-ELF-WASM.cpp + SwiftRT-ELF.cpp C_COMPILE_FLAGS ${SWIFT_RUNTIME_CORE_CXX_FLAGS} ${swift_enable_backtracing} @@ -203,7 +204,7 @@ add_swift_target_library(swiftImageRegistrationObjectCOFF add_swift_target_library(swiftImageRegistrationObjectWASM OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE - SwiftRT-ELF-WASM.cpp + SwiftRT-WASM.cpp C_COMPILE_FLAGS ${SWIFT_RUNTIME_CORE_CXX_FLAGS} ${swift_enable_backtracing} @@ -224,9 +225,10 @@ foreach(sdk ${SWIFT_SDKS}) # to a version which supports it. # set(swiftrtObject "$") set(swiftrtSourceName SwiftRT-${SWIFT_SDK_${sdk}_OBJECT_FORMAT}.cpp) - if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF" OR - "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") - set(swiftrtSourceName SwiftRT-ELF-WASM.cpp) + if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") + set(swiftrtSourceName SwiftRT-ELF.cpp) + elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") + set(swiftrtSourceName SwiftRT-WASM.cpp) endif() set(swiftrtObject ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/swiftImageRegistrationObject${SWIFT_SDK_${sdk}_OBJECT_FORMAT}-${arch_suffix}.dir/${swiftrtSourceName}${CMAKE_C_OUTPUT_EXTENSION}) diff --git a/stdlib/public/runtime/MetadataSectionNames.def b/stdlib/public/runtime/MetadataSectionNames.def new file mode 100644 index 0000000000000..63592eaaa9fa0 --- /dev/null +++ b/stdlib/public/runtime/MetadataSectionNames.def @@ -0,0 +1,34 @@ +//===--- MetadataSectionNames.def -----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef HANDLE_SWIFT_SECTION +#error "HANDLE_SWIFT_SECTION(elfname, coffname) not defined" +#endif + +// NOTE: This table is ABI. Don't rearrange it! +// NOTE: If adding fields, remember to add the field to ReflectionContext.h + +HANDLE_SWIFT_SECTION(swift5_protocols, sw5prt) +HANDLE_SWIFT_SECTION(swift5_protocol_conformances, sw5prtc) +HANDLE_SWIFT_SECTION(swift5_type_metadata, sw5tymd) +HANDLE_SWIFT_SECTION(swift5_typeref, sw5tyrf) +HANDLE_SWIFT_SECTION(swift5_reflstr, sw5rfst) +HANDLE_SWIFT_SECTION(swift5_fieldmd, sw5flmd) +HANDLE_SWIFT_SECTION(swift5_assocty, sw5asty) +HANDLE_SWIFT_SECTION(swift5_replace, sw5repl) +HANDLE_SWIFT_SECTION(swift5_replac2, sw5reps) +HANDLE_SWIFT_SECTION(swift5_builtin, sw5bltn) +HANDLE_SWIFT_SECTION(swift5_capture, sw5cptr) +HANDLE_SWIFT_SECTION(swift5_mpenum, sw5mpen) +HANDLE_SWIFT_SECTION(swift5_accessible_functions, sw5acfn) +HANDLE_SWIFT_SECTION(swift5_runtime_attributes, sw5ratt) +HANDLE_SWIFT_SECTION(swift5_tests, sw5test) diff --git a/stdlib/public/runtime/SwiftRT-ELF.cpp b/stdlib/public/runtime/SwiftRT-ELF.cpp new file mode 100644 index 0000000000000..6ed54b9168c8f --- /dev/null +++ b/stdlib/public/runtime/SwiftRT-ELF.cpp @@ -0,0 +1,188 @@ +//===--- SwiftRT-ELF.cpp --------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "ImageInspectionCommon.h" +#include "swift/Runtime/Backtrace.h" +#include "swift/Runtime/Config.h" +#include "swift/shims/MetadataSections.h" + +#include +#include + +#define SWIFT_METADATA_SEGMENT_TYPE 0x73356d64 // s5md or dms5 on little-endian + +extern "C" const char __ehdr_start[] __attribute__((__weak__)); + +#if SWIFT_ENABLE_BACKTRACING +// Drag in a symbol from the backtracer, to force the static linker to include +// the code. +static const void *__backtraceRef __attribute__((used, retain)) = + (const void *)swift::runtime::backtrace::_swift_backtrace_isThunkFunction; +#endif + +// Create empty sections to ensure that the start/stop symbols are synthesized +// by the linker. Otherwise, we may end up with undefined symbol references as +// the linker table section was never constructed. +#define DECLARE_EMPTY_METADATA_SECTION(name) \ + __asm__("\t.section " #name ",\"aR\"\n"); + +#define BOUNDS_VISIBILITY \ + __attribute__((__visibility__("hidden"), __aligned__(1))) + +#define DECLARE_BOUNDS(name) \ + BOUNDS_VISIBILITY extern const char __start_##name; \ + BOUNDS_VISIBILITY extern const char __stop_##name; + +#define DECLARE_SWIFT_SECTION(name) \ + DECLARE_EMPTY_METADATA_SECTION(name) \ + DECLARE_BOUNDS(name) + +// These may or may not be present, depending on compiler switches; it's +// worth calling them out as a result. +#define DECLARE_SWIFT_REFLECTION_SECTION(name) DECLARE_SWIFT_SECTION(name) + +extern "C" { +DECLARE_SWIFT_SECTION(swift5_protocols) +DECLARE_SWIFT_SECTION(swift5_protocol_conformances) +DECLARE_SWIFT_SECTION(swift5_type_metadata) + +DECLARE_SWIFT_REFLECTION_SECTION(swift5_fieldmd) +DECLARE_SWIFT_REFLECTION_SECTION(swift5_builtin) +DECLARE_SWIFT_REFLECTION_SECTION(swift5_assocty) +DECLARE_SWIFT_REFLECTION_SECTION(swift5_capture) +DECLARE_SWIFT_REFLECTION_SECTION(swift5_reflstr) +DECLARE_SWIFT_REFLECTION_SECTION(swift5_typeref) +DECLARE_SWIFT_REFLECTION_SECTION(swift5_mpenum) + +DECLARE_SWIFT_SECTION(swift5_replace) +DECLARE_SWIFT_SECTION(swift5_replac2) +DECLARE_SWIFT_SECTION(swift5_accessible_functions) +DECLARE_SWIFT_SECTION(swift5_runtime_attributes) + +DECLARE_SWIFT_SECTION(swift5_tests) +} + +#undef DECLARE_SWIFT_SECTION + +namespace { +struct ElfNoteHeader { + uint32_t namesz; + uint32_t descsz; + uint32_t type; +}; + +struct SectionRange { + uintptr_t start; + uintptr_t stop; +}; + +// If you update this table, remember to update the reader in +// ReflectionContext.h! +struct MetadataSections { + uintptr_t version; + uintptr_t base; + uintptr_t unused0; + uintptr_t unused1; + +#define HANDLE_SWIFT_SECTION(name, coff) SectionRange name; +#include "MetadataSectionNames.def" +#undef HANDLE_SWIFT_SECTION +}; + +struct __attribute__((__packed__)) SwiftMetadataNote { + ElfNoteHeader header; + char name[8]; // "swift6\0" + 1 byte(s) of padding + + MetadataSections sections; +}; + +/// .note.swift_metadata section stores the table of start/stop symbols for +/// ingestion into the runtime and for static tool access. +// TODO: We should replace addNewDSOImage with something that takes a pointer to +// the table directly, removing the need for the additional global constructor +// that converts the start/stop symbol pointer ranges to start/byte-count, +// before stabilizing any ELF platform ABIs. This will help reduce program +// launch times. +// This will require changes to Swift Testing and the runtime metadata +// registration. +__asm__(".section .note.swift_metadata, \"a\", @note"); + +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN +__attribute__((section(".note.swift_metadata"), used, aligned(4))) +const static SwiftMetadataNote swiftMetadataNote = { + // header + .header = + { + .namesz = 7, // namesz: "swift6\0" + .descsz = sizeof(MetadataSections), + .type = SWIFT_METADATA_SEGMENT_TYPE, + }, // 4 + 4 + 4 = 12, 0x0c + .name = + { + 's', 'w', 'i', 'f', 't', '6', '\0', + static_cast(0xff), // padding + }, // 12 + 7 + 1 = 20, 0x14 + .sections = + { + .version = 5, + .base = reinterpret_cast(&__ehdr_start), + .unused0 = 0xa1a1a1a1, + .unused1 = 0xb2b2b2b2, + +#define HANDLE_SWIFT_SECTION(elfname, coffname) \ + .elfname = { \ + reinterpret_cast(&__start_##elfname), \ + reinterpret_cast(&__stop_##elfname), \ + }, + +#include "MetadataSectionNames.def" + +#undef HANDLE_SWIFT_SECTION + + }, +}; +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END + +} // anonymous namespace + +namespace { +static swift::MetadataSections sections{}; +} + +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN +__attribute__((__constructor__)) static void swift_image_constructor() { +#define SWIFT_SECTION_RANGE(name) \ + { \ + swiftMetadataNote.sections.name.start, \ + swiftMetadataNote.sections.name.stop - \ + swiftMetadataNote.sections.name.start, \ + } + + ::new (§ions) swift::MetadataSections{ + swiftMetadataNote.sections.version, + reinterpret_cast(swiftMetadataNote.sections.base), + + reinterpret_cast(swiftMetadataNote.sections.unused0), + reinterpret_cast(swiftMetadataNote.sections.unused1), + +#define HANDLE_SWIFT_SECTION(elfname, coffname) SWIFT_SECTION_RANGE(elfname), + +#include "MetadataSectionNames.def" + +#undef HANDLE_SWIFT_SECTION + }; + +#undef SWIFT_SECTION_RANGE + + swift_addNewDSOImage(§ions); +} +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END diff --git a/stdlib/public/runtime/SwiftRT-ELF-WASM.cpp b/stdlib/public/runtime/SwiftRT-WASM.cpp similarity index 66% rename from stdlib/public/runtime/SwiftRT-ELF-WASM.cpp rename to stdlib/public/runtime/SwiftRT-WASM.cpp index 7cbde16180a5d..095056011329a 100644 --- a/stdlib/public/runtime/SwiftRT-ELF-WASM.cpp +++ b/stdlib/public/runtime/SwiftRT-WASM.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2026 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -11,48 +11,40 @@ //===----------------------------------------------------------------------===// #include "ImageInspectionCommon.h" -#include "swift/shims/MetadataSections.h" #include "swift/Runtime/Backtrace.h" #include "swift/Runtime/Config.h" +#include "swift/shims/MetadataSections.h" #include #include -#if defined(__ELF__) -extern "C" const char __ehdr_start[] __attribute__((__weak__)); -#endif - #if SWIFT_ENABLE_BACKTRACING // Drag in a symbol from the backtracer, to force the static linker to include // the code. -static const void *__backtraceRef __attribute__((used, retain)) - = (const void *)swift::runtime::backtrace::_swift_backtrace_isThunkFunction; +static const void *__backtraceRef __attribute__((used, retain)) = + (const void *)swift::runtime::backtrace::_swift_backtrace_isThunkFunction; #endif // Create empty sections to ensure that the start/stop symbols are synthesized // by the linker. Otherwise, we may end up with undefined symbol references as // the linker table section was never constructed. -#if defined(__ELF__) -# define DECLARE_EMPTY_METADATA_SECTION(name, attrs) __asm__("\t.section " #name ",\"" attrs "\"\n"); -#elif defined(__wasm__) -# define DECLARE_EMPTY_METADATA_SECTION(name, attrs) __asm__("\t.section " #name ",\"R\",@\n"); -#endif +#define DECLARE_EMPTY_METADATA_SECTION(name) \ + __asm__("\t.section " #name ",\"R\",@\n"); -#define BOUNDS_VISIBILITY __attribute__((__visibility__("hidden"), \ - __aligned__(1))) +#define BOUNDS_VISIBILITY \ + __attribute__((__visibility__("hidden"), __aligned__(1))) -#define DECLARE_BOUNDS(name) \ - BOUNDS_VISIBILITY extern const char __start_##name; \ +#define DECLARE_BOUNDS(name) \ + BOUNDS_VISIBILITY extern const char __start_##name; \ BOUNDS_VISIBILITY extern const char __stop_##name; -#define DECLARE_SWIFT_SECTION(name) \ - DECLARE_EMPTY_METADATA_SECTION(name, "aR") \ +#define DECLARE_SWIFT_SECTION(name) \ + DECLARE_EMPTY_METADATA_SECTION(name) \ DECLARE_BOUNDS(name) // These may or may not be present, depending on compiler switches; it's // worth calling them out as a result. -#define DECLARE_SWIFT_REFLECTION_SECTION(name) \ - DECLARE_SWIFT_SECTION(name) +#define DECLARE_SWIFT_REFLECTION_SECTION(name) DECLARE_SWIFT_SECTION(name) extern "C" { DECLARE_SWIFT_SECTION(swift5_protocols) @@ -82,25 +74,19 @@ static swift::MetadataSections sections{}; } SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN -__attribute__((__constructor__)) -static void swift_image_constructor() { +__attribute__((__constructor__)) static void swift_image_constructor() { #define SWIFT_SECTION_RANGE(name) \ - { reinterpret_cast(&__start_##name), \ - static_cast(&__stop_##name - &__start_##name) } - - const void *baseAddress = nullptr; -#if defined(__ELF__) - if (&__ehdr_start != nullptr) { - baseAddress = __ehdr_start; + { \ + reinterpret_cast(&__start_##name), \ + static_cast(&__stop_##name - &__start_##name) \ } -#elif defined(__wasm__) - // NOTE: Multi images in a single process is not yet stabilized in WebAssembly - // toolchain outside of Emscripten. -#endif - ::new (§ions) swift::MetadataSections { + ::new (§ions) swift::MetadataSections{ swift::CurrentSectionMetadataVersion, - baseAddress, + + // NOTE: Multi images in a single process is not yet stabilized in + // WebAssembly toolchain outside of Emscripten. + nullptr, nullptr, nullptr, diff --git a/test/Reflection/capture_descriptors.sil b/test/Reflection/capture_descriptors.sil index 41d98537a8eee..3d30b3ec01e96 100644 --- a/test/Reflection/capture_descriptors.sil +++ b/test/Reflection/capture_descriptors.sil @@ -9,12 +9,6 @@ // RUN: %target-build-swift %s -emit-module -emit-library -module-name capture_descriptors -o %t/capture_descriptors%{target-shared-library-suffix} -L%t/../../.. -lBlocksRuntime // RUN: %target-swift-reflection-dump %t/capture_descriptors%{target-shared-library-suffix} | %FileCheck %s -// lld on FreeBSD relocates some of the metadata sections resulting in invalid -// offsets in section headers, breaking the Swift reflection data ELF parser -// resulting in missing metadata. -// rdar://159139154 -// UNSUPPORTED: OS=freebsd - sil_stage canonical import Builtin diff --git a/test/Reflection/typeref_decoding_imported.swift b/test/Reflection/typeref_decoding_imported.swift index a06656f71e7d5..82efcf7b9b1ab 100644 --- a/test/Reflection/typeref_decoding_imported.swift +++ b/test/Reflection/typeref_decoding_imported.swift @@ -23,12 +23,6 @@ // UNSUPPORTED: OS=linux-android, OS=linux-androideabi -// lld on FreeBSD relocates some of the metadata sections resulting in invalid -// offsets in section headers, breaking the Swift reflection data ELF parser -// resulting in missing metadata. -// rdar://159139154 -// XFAIL: OS=freebsd - // CHECK-32: FIELDS: // CHECK-32: ======= // CHECK-32: TypesToReflect.HasCTypes