diff options
Diffstat (limited to 'lld/MachO')
| -rw-r--r-- | lld/MachO/Arch/ARM.cpp | 34 | ||||
| -rw-r--r-- | lld/MachO/Arch/ARM64.cpp | 42 | ||||
| -rw-r--r-- | lld/MachO/Arch/ARM64_32.cpp | 39 | ||||
| -rw-r--r-- | lld/MachO/Arch/X86_64.cpp | 36 | ||||
| -rw-r--r-- | lld/MachO/Config.h | 11 | ||||
| -rw-r--r-- | lld/MachO/Driver.cpp | 138 | ||||
| -rw-r--r-- | lld/MachO/ICF.cpp | 29 | ||||
| -rw-r--r-- | lld/MachO/ICF.h | 2 | ||||
| -rw-r--r-- | lld/MachO/InputFiles.cpp | 74 | ||||
| -rw-r--r-- | lld/MachO/InputFiles.h | 1 | ||||
| -rw-r--r-- | lld/MachO/InputSection.cpp | 5 | ||||
| -rw-r--r-- | lld/MachO/InputSection.h | 3 | ||||
| -rw-r--r-- | lld/MachO/LTO.cpp | 25 | ||||
| -rw-r--r-- | lld/MachO/Options.td | 1 | ||||
| -rw-r--r-- | lld/MachO/SectionPriorities.cpp | 2 | ||||
| -rw-r--r-- | lld/MachO/SymbolTable.cpp | 7 | ||||
| -rw-r--r-- | lld/MachO/SymbolTable.h | 2 | ||||
| -rw-r--r-- | lld/MachO/SyntheticSections.cpp | 233 | ||||
| -rw-r--r-- | lld/MachO/SyntheticSections.h | 23 | ||||
| -rw-r--r-- | lld/MachO/Target.h | 11 | ||||
| -rw-r--r-- | lld/MachO/UnwindInfoSection.cpp | 2 | ||||
| -rw-r--r-- | lld/MachO/Writer.cpp | 5 |
22 files changed, 490 insertions, 235 deletions
diff --git a/lld/MachO/Arch/ARM.cpp b/lld/MachO/Arch/ARM.cpp index fd215ed99b59..424df414229f 100644 --- a/lld/MachO/Arch/ARM.cpp +++ b/lld/MachO/Arch/ARM.cpp @@ -38,35 +38,27 @@ struct ARM : TargetInfo { uint64_t entryAddr) const override; void relaxGotLoad(uint8_t *loc, uint8_t type) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; uint64_t getPageSize() const override { return 4 * 1024; } void handleDtraceReloc(const Symbol *sym, const Reloc &r, uint8_t *loc) const override; }; - } // namespace -const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const { - static const std::array<RelocAttrs, 10> relocAttrsArray{{ +static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"VANILLA", /* FIXME populate this */ B(_0)}, - {"PAIR", /* FIXME populate this */ B(_0)}, - {"SECTDIFF", /* FIXME populate this */ B(_0)}, - {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)}, - {"PB_LA_PTR", /* FIXME populate this */ B(_0)}, - {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"32BIT_BRANCH", /* FIXME populate this */ B(_0)}, - {"HALF", /* FIXME populate this */ B(_0)}, - {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)}, + {"VANILLA", /* FIXME populate this */ B(_0)}, + {"PAIR", /* FIXME populate this */ B(_0)}, + {"SECTDIFF", /* FIXME populate this */ B(_0)}, + {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)}, + {"PB_LA_PTR", /* FIXME populate this */ B(_0)}, + {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"32BIT_BRANCH", /* FIXME populate this */ B(_0)}, + {"HALF", /* FIXME populate this */ B(_0)}, + {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, relocation_info rel) const { @@ -167,6 +159,8 @@ ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) { stubSize = 0 /* FIXME */; stubHelperHeaderSize = 0 /* FIXME */; stubHelperEntrySize = 0 /* FIXME */; + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; } TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) { diff --git a/lld/MachO/Arch/ARM64.cpp b/lld/MachO/Arch/ARM64.cpp index 5901a9e09b35..46e935aa1fd1 100644 --- a/lld/MachO/Arch/ARM64.cpp +++ b/lld/MachO/Arch/ARM64.cpp @@ -34,7 +34,6 @@ struct ARM64 : ARM64Common { void writeStubHelperHeader(uint8_t *buf) const override; void writeStubHelperEntry(uint8_t *buf, const Symbol &, uint64_t entryAddr) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; void populateThunk(InputSection *thunk, Symbol *funcSym) override; void applyOptimizationHints(uint8_t *, const ConcatInputSection *, ArrayRef<uint64_t>) const override; @@ -48,31 +47,24 @@ struct ARM64 : ARM64Common { // absolute version of this relocation. The semantics of the absolute relocation // are weird -- it results in the value of the GOT slot being written, instead // of the address. Let's not support it unless we find a real-world use case. - -const RelocAttrs &ARM64::getRelocAttrs(uint8_t type) const { - static const std::array<RelocAttrs, 11> relocAttrsArray{{ +static constexpr std::array<RelocAttrs, 11> relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"UNSIGNED", - B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, - {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, - {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, - {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, - {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, - {"GOT_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, - {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, - {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, - {"TLVP_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, - {"ADDEND", B(ADDEND)}, + {"UNSIGNED", + B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, + {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, + {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, + {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, + {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, + {"GOT_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, + {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, + {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, + {"TLVP_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, + {"ADDEND", B(ADDEND)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; static constexpr uint32_t stubCode[] = { 0x90000010, // 00: adrp x16, __la_symbol_ptr@page @@ -150,6 +142,8 @@ ARM64::ARM64() : ARM64Common(LP64()) { stubHelperHeaderSize = sizeof(stubHelperHeaderCode); stubHelperEntrySize = sizeof(stubHelperEntryCode); + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; } namespace { diff --git a/lld/MachO/Arch/ARM64_32.cpp b/lld/MachO/Arch/ARM64_32.cpp index 5be411e40342..4830c2629761 100644 --- a/lld/MachO/Arch/ARM64_32.cpp +++ b/lld/MachO/Arch/ARM64_32.cpp @@ -33,36 +33,29 @@ struct ARM64_32 : ARM64Common { void writeStubHelperHeader(uint8_t *buf) const override; void writeStubHelperEntry(uint8_t *buf, const Symbol &, uint64_t entryAddr) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; }; } // namespace // These are very similar to ARM64's relocation attributes, except that we don't // have the BYTE8 flag set. -const RelocAttrs &ARM64_32::getRelocAttrs(uint8_t type) const { - static const std::array<RelocAttrs, 11> relocAttrsArray{{ +static constexpr std::array<RelocAttrs, 11> relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4)}, - {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, - {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, - {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, - {"GOT_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, - {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, - {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, - {"TLVP_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, - {"ADDEND", B(ADDEND)}, + {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4)}, + {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, + {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, + {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, + {"GOT_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, + {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, + {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, + {"TLVP_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, + {"ADDEND", B(ADDEND)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; // The stub code is fairly similar to ARM64's, except that we load pointers into // 32-bit 'w' registers, instead of the 64-bit 'x' ones. @@ -112,6 +105,8 @@ ARM64_32::ARM64_32() : ARM64Common(ILP32()) { stubSize = sizeof(stubCode); stubHelperHeaderSize = sizeof(stubHelperHeaderCode); stubHelperEntrySize = sizeof(stubHelperEntryCode); + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; } TargetInfo *macho::createARM64_32TargetInfo() { diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp index d2efa5bb3451..b1c46cbab9d4 100644 --- a/lld/MachO/Arch/X86_64.cpp +++ b/lld/MachO/Arch/X86_64.cpp @@ -37,36 +37,28 @@ struct X86_64 : TargetInfo { uint64_t entryAddr) const override; void relaxGotLoad(uint8_t *loc, uint8_t type) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; uint64_t getPageSize() const override { return 4 * 1024; } void handleDtraceReloc(const Symbol *sym, const Reloc &r, uint8_t *loc) const override; }; - } // namespace -const RelocAttrs &X86_64::getRelocAttrs(uint8_t type) const { - static const std::array<RelocAttrs, 10> relocAttrsArray{{ +static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"UNSIGNED", - B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, - {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, - {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, - {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, - {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, + {"UNSIGNED", + B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, + {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, + {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, + {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, + {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; static int pcrelOffset(uint8_t type) { switch (type) { @@ -196,6 +188,8 @@ X86_64::X86_64() : TargetInfo(LP64()) { stubSize = sizeof(stub); stubHelperHeaderSize = sizeof(stubHelperHeader); stubHelperEntrySize = sizeof(stubHelperEntry); + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; } TargetInfo *macho::createX86_64TargetInfo() { diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h index ccf71b6535ea..c7e4b4f96782 100644 --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -109,7 +109,7 @@ struct Configuration { bool archMultiple = false; bool exportDynamic = false; bool forceLoadObjC = false; - bool forceLoadSwift = false; + bool forceLoadSwift = false; // Only applies to LC_LINKER_OPTIONs. bool staticLink = false; bool implicitDylibs = false; bool isPic = false; @@ -188,6 +188,8 @@ struct Configuration { SymbolPatterns unexportedSymbols; SymbolPatterns whyLive; + std::vector<std::pair<llvm::StringRef, llvm::StringRef>> aliasedSymbols; + SymtabPresence localSymbolsPresence = SymtabPresence::All; SymbolPatterns localSymbolPatterns; @@ -202,13 +204,6 @@ struct Configuration { } }; -// Whether to force-load an archive. -enum class ForceLoad { - Default, // Apply -all_load or -ObjC behaviors if those flags are enabled - Yes, // Always load the archive, regardless of other flags - No, // Never load the archive, regardless of other flags -}; - extern std::unique_ptr<Configuration> config; } // namespace macho diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index abfe381f41e0..454708fad4ef 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -247,9 +247,24 @@ static llvm::CachePruningPolicy getLTOCachePolicy(InputArgList &args) { return CHECK(parseCachePruningPolicy(ltoPolicy), "invalid LTO cache policy"); } -static DenseMap<StringRef, ArchiveFile *> loadedArchives; +// What caused a given library to be loaded. Only relevant for archives. +// Note that this does not tell us *how* we should load the library, i.e. +// whether we should do it lazily or eagerly (AKA force loading). The "how" is +// decided within addFile(). +enum class LoadType { + CommandLine, // Library was passed as a regular CLI argument + CommandLineForce, // Library was passed via `-force_load` + LCLinkerOption, // Library was passed via LC_LINKER_OPTIONS +}; + +struct ArchiveFileInfo { + ArchiveFile *file; + bool isCommandLineLoad; +}; + +static DenseMap<StringRef, ArchiveFileInfo> loadedArchives; -static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, +static InputFile *addFile(StringRef path, LoadType loadType, bool isLazy = false, bool isExplicit = true, bool isBundleLoader = false) { Optional<MemoryBufferRef> buffer = readFile(path); @@ -261,6 +276,7 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, file_magic magic = identify_magic(mbref.getBuffer()); switch (magic) { case file_magic::archive: { + bool isCommandLineLoad = loadType != LoadType::LCLinkerOption; // Avoid loading archives twice. If the archives are being force-loaded, // loading them twice would create duplicate symbol errors. In the // non-force-loading case, this is just a minor performance optimization. @@ -268,23 +284,45 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, // loadArchiveMember() call below may recursively call addFile() and // invalidate this reference. auto entry = loadedArchives.find(path); - if (entry != loadedArchives.end()) - return entry->second; - std::unique_ptr<object::Archive> archive = CHECK( - object::Archive::create(mbref), path + ": failed to parse archive"); + ArchiveFile *file; + if (entry == loadedArchives.end()) { + // No cached archive, we need to create a new one + std::unique_ptr<object::Archive> archive = CHECK( + object::Archive::create(mbref), path + ": failed to parse archive"); - if (!archive->isEmpty() && !archive->hasSymbolTable()) - error(path + ": archive has no index; run ranlib to add one"); + if (!archive->isEmpty() && !archive->hasSymbolTable()) + error(path + ": archive has no index; run ranlib to add one"); + file = make<ArchiveFile>(std::move(archive)); + } else { + file = entry->second.file; + // Command-line loads take precedence. If file is previously loaded via + // command line, or is loaded via LC_LINKER_OPTION and being loaded via + // LC_LINKER_OPTION again, using the cached archive is enough. + if (entry->second.isCommandLineLoad || !isCommandLineLoad) + return file; + } - auto *file = make<ArchiveFile>(std::move(archive)); - if ((forceLoadArchive == ForceLoad::Default && config->allLoad) || - forceLoadArchive == ForceLoad::Yes) { + bool isLCLinkerForceLoad = loadType == LoadType::LCLinkerOption && + config->forceLoadSwift && + path::filename(path).startswith("libswift"); + if ((isCommandLineLoad && config->allLoad) || + loadType == LoadType::CommandLineForce || isLCLinkerForceLoad) { if (Optional<MemoryBufferRef> buffer = readFile(path)) { Error e = Error::success(); for (const object::Archive::Child &c : file->getArchive().children(e)) { - StringRef reason = - forceLoadArchive == ForceLoad::Yes ? "-force_load" : "-all_load"; + StringRef reason; + switch (loadType) { + case LoadType::LCLinkerOption: + reason = "LC_LINKER_OPTION"; + break; + case LoadType::CommandLineForce: + reason = "-force_load"; + break; + case LoadType::CommandLine: + reason = "-all_load"; + break; + } if (Error e = file->fetch(c, reason)) error(toString(file) + ": " + reason + " failed to load archive member: " + toString(std::move(e))); @@ -293,8 +331,7 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, error(toString(file) + ": Archive::children failed: " + toString(std::move(e))); } - } else if (forceLoadArchive == ForceLoad::Default && - config->forceLoadObjC) { + } else if (isCommandLineLoad && config->forceLoadObjC) { for (const object::Archive::Symbol &sym : file->getArchive().symbols()) if (sym.getName().startswith(objc::klass)) file->fetch(sym); @@ -318,7 +355,8 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, } file->addLazySymbols(); - newFile = loadedArchives[path] = file; + loadedArchives[path] = ArchiveFileInfo{file, isCommandLineLoad}; + newFile = file; break; } case file_magic::macho_object: @@ -368,11 +406,10 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, } static void addLibrary(StringRef name, bool isNeeded, bool isWeak, - bool isReexport, bool isExplicit, - ForceLoad forceLoadArchive) { + bool isReexport, bool isExplicit, LoadType loadType) { if (Optional<StringRef> path = findLibrary(name)) { if (auto *dylibFile = dyn_cast_or_null<DylibFile>( - addFile(*path, forceLoadArchive, /*isLazy=*/false, isExplicit))) { + addFile(*path, loadType, /*isLazy=*/false, isExplicit))) { if (isNeeded) dylibFile->forceNeeded = true; if (isWeak) @@ -389,14 +426,13 @@ static void addLibrary(StringRef name, bool isNeeded, bool isWeak, static DenseSet<StringRef> loadedObjectFrameworks; static void addFramework(StringRef name, bool isNeeded, bool isWeak, - bool isReexport, bool isExplicit, - ForceLoad forceLoadArchive) { + bool isReexport, bool isExplicit, LoadType loadType) { if (Optional<StringRef> path = findFramework(name)) { if (loadedObjectFrameworks.contains(*path)) return; InputFile *file = - addFile(*path, forceLoadArchive, /*isLazy=*/false, isExplicit); + addFile(*path, loadType, /*isLazy=*/false, isExplicit, false); if (auto *dylibFile = dyn_cast_or_null<DylibFile>(file)) { if (isNeeded) dylibFile->forceNeeded = true; @@ -436,15 +472,14 @@ void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) { unsigned i = 0; StringRef arg = argv[i]; if (arg.consume_front("-l")) { - ForceLoad forceLoadArchive = - config->forceLoadSwift && arg.startswith("swift") ? ForceLoad::Yes - : ForceLoad::No; addLibrary(arg, /*isNeeded=*/false, /*isWeak=*/false, - /*isReexport=*/false, /*isExplicit=*/false, forceLoadArchive); + /*isReexport=*/false, /*isExplicit=*/false, + LoadType::LCLinkerOption); } else if (arg == "-framework") { StringRef name = argv[++i]; addFramework(name, /*isNeeded=*/false, /*isWeak=*/false, - /*isReexport=*/false, /*isExplicit=*/false, ForceLoad::No); + /*isReexport=*/false, /*isExplicit=*/false, + LoadType::LCLinkerOption); } else { error(arg + " is not allowed in LC_LINKER_OPTION"); } @@ -456,7 +491,7 @@ static void addFileList(StringRef path, bool isLazy) { return; MemoryBufferRef mbref = *buffer; for (StringRef path : args::getLines(mbref)) - addFile(rerootPath(path), ForceLoad::Default, isLazy); + addFile(rerootPath(path), LoadType::CommandLine, isLazy); } // We expect sub-library names of the form "libfoo", which will match a dylib @@ -468,8 +503,7 @@ static bool markReexport(StringRef searchName, ArrayRef<StringRef> extensions) { if (auto *dylibFile = dyn_cast<DylibFile>(file)) { StringRef filename = path::filename(dylibFile->getName()); if (filename.consume_front(searchName) && - (filename.empty() || - find(extensions, filename) != extensions.end())) { + (filename.empty() || llvm::is_contained(extensions, filename))) { dylibFile->reexport = true; return true; } @@ -552,7 +586,7 @@ static void initializeSectionRenameMap() { section_names::objcCatList, section_names::objcNonLazyCatList, section_names::objcProtoList, - section_names::objcImageInfo}; + section_names::objCImageInfo}; for (StringRef s : v) config->sectionRenameMap[{segment_names::data, s}] = { segment_names::dataConst, s}; @@ -976,30 +1010,30 @@ static void createFiles(const InputArgList &args) { switch (opt.getID()) { case OPT_INPUT: - addFile(rerootPath(arg->getValue()), ForceLoad::Default, isLazy); + addFile(rerootPath(arg->getValue()), LoadType::CommandLine, isLazy); break; case OPT_needed_library: if (auto *dylibFile = dyn_cast_or_null<DylibFile>( - addFile(rerootPath(arg->getValue()), ForceLoad::Default))) + addFile(rerootPath(arg->getValue()), LoadType::CommandLine))) dylibFile->forceNeeded = true; break; case OPT_reexport_library: if (auto *dylibFile = dyn_cast_or_null<DylibFile>( - addFile(rerootPath(arg->getValue()), ForceLoad::Default))) { + addFile(rerootPath(arg->getValue()), LoadType::CommandLine))) { config->hasReexports = true; dylibFile->reexport = true; } break; case OPT_weak_library: if (auto *dylibFile = dyn_cast_or_null<DylibFile>( - addFile(rerootPath(arg->getValue()), ForceLoad::Default))) + addFile(rerootPath(arg->getValue()), LoadType::CommandLine))) dylibFile->forceWeakImport = true; break; case OPT_filelist: addFileList(arg->getValue(), isLazy); break; case OPT_force_load: - addFile(rerootPath(arg->getValue()), ForceLoad::Yes); + addFile(rerootPath(arg->getValue()), LoadType::CommandLineForce); break; case OPT_l: case OPT_needed_l: @@ -1007,7 +1041,7 @@ static void createFiles(const InputArgList &args) { case OPT_weak_l: addLibrary(arg->getValue(), opt.getID() == OPT_needed_l, opt.getID() == OPT_weak_l, opt.getID() == OPT_reexport_l, - /*isExplicit=*/true, ForceLoad::Default); + /*isExplicit=*/true, LoadType::CommandLine); break; case OPT_framework: case OPT_needed_framework: @@ -1016,7 +1050,7 @@ static void createFiles(const InputArgList &args) { addFramework(arg->getValue(), opt.getID() == OPT_needed_framework, opt.getID() == OPT_weak_framework, opt.getID() == OPT_reexport_framework, /*isExplicit=*/true, - ForceLoad::Default); + LoadType::CommandLine); break; case OPT_start_lib: if (isLazy) @@ -1068,6 +1102,8 @@ static void gatherInputSections() { } } } + if (!file->objCImageInfo.empty()) + in.objCImageInfo->addFile(file); } assert(inputOrder <= UnspecifiedInputOrder); } @@ -1264,9 +1300,8 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, if (const Arg *arg = args.getLastArg(OPT_bundle_loader)) { if (config->outputType != MH_BUNDLE) error("-bundle_loader can only be used with MachO bundle output"); - addFile(arg->getValue(), ForceLoad::Default, /*isLazy=*/false, - /*isExplicit=*/false, - /*isBundleLoader=*/true); + addFile(arg->getValue(), LoadType::CommandLine, /*isLazy=*/false, + /*isExplicit=*/false, /*isBundleLoader=*/true); } if (const Arg *arg = args.getLastArg(OPT_umbrella)) { if (config->outputType != MH_DYLIB) @@ -1306,6 +1341,11 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true); config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); + for (const Arg *arg : args.filtered(OPT_alias)) { + config->aliasedSymbols.push_back( + std::make_pair(arg->getValue(0), arg->getValue(1))); + } + // FIXME: Add a commandline flag for this too. config->zeroModTime = getenv("ZERO_AR_DATE"); @@ -1558,6 +1598,18 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, createSyntheticSections(); createSyntheticSymbols(); + for (const auto &pair : config->aliasedSymbols) { + if (const auto &sym = symtab->find(pair.first)) { + if (const auto &defined = dyn_cast<Defined>(sym)) { + symtab->aliasDefined(defined, pair.second); + continue; + } + } + + warn("undefined base symbol '" + pair.first + "' for alias '" + + pair.second + "'\n"); + } + if (config->hasExplicitExports) { parallelForEach(symtab->getSymbols(), [](Symbol *sym) { if (auto *defined = dyn_cast<Defined>(sym)) { @@ -1616,7 +1668,9 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, if (config->icfLevel != ICFLevel::none) { if (config->icfLevel == ICFLevel::safe) markAddrSigSymbols(); - foldIdenticalSections(); + foldIdenticalSections(/*onlyCfStrings=*/false); + } else if (config->dedupLiterals) { + foldIdenticalSections(/*onlyCfStrings=*/true); } // Write to an output file. diff --git a/lld/MachO/ICF.cpp b/lld/MachO/ICF.cpp index ad029142681f..d06fbc6db840 100644 --- a/lld/MachO/ICF.cpp +++ b/lld/MachO/ICF.cpp @@ -384,23 +384,18 @@ void macho::markAddrSigSymbols() { continue; assert(addrSigSection->subsections.size() == 1); - Subsection *subSection = &addrSigSection->subsections[0]; - ArrayRef<unsigned char> &contents = subSection->isec->data; + const InputSection *isec = addrSigSection->subsections[0].isec; - const uint8_t *pData = contents.begin(); - while (pData != contents.end()) { - unsigned size; - const char *err; - uint32_t symIndex = decodeULEB128(pData, &size, contents.end(), &err); - if (err) - fatal(toString(file) + ": could not decode addrsig section: " + err); - markSymAsAddrSig(obj->symbols[symIndex]); - pData += size; + for (const Reloc &r : isec->relocs) { + if (auto *sym = r.referent.dyn_cast<Symbol *>()) + markSymAsAddrSig(sym); + else + error(toString(isec) + ": unexpected section relocation"); } } } -void macho::foldIdenticalSections() { +void macho::foldIdenticalSections(bool onlyCfStrings) { TimeTraceScope timeScope("Fold Identical Code Sections"); // The ICF equivalence-class segregation algorithm relies on pre-computed // hashes of InputSection::data for the ConcatOutputSection::inputs and all @@ -420,10 +415,12 @@ void macho::foldIdenticalSections() { uint64_t icfUniqueID = inputSections.size(); for (ConcatInputSection *isec : inputSections) { // FIXME: consider non-code __text sections as hashable? - bool isHashable = (isCodeSection(isec) || isCfStringSection(isec) || - isClassRefsSection(isec)) && - !isec->keepUnique && !isec->shouldOmitFromOutput() && - sectionType(isec->getFlags()) == MachO::S_REGULAR; + bool isHashable = + (!onlyCfStrings || isCfStringSection(isec)) && + (isCodeSection(isec) || isCfStringSection(isec) || + isClassRefsSection(isec) || isGccExceptTabSection(isec)) && + !isec->keepUnique && !isec->shouldOmitFromOutput() && + sectionType(isec->getFlags()) == MachO::S_REGULAR; if (isHashable) { hashable.push_back(isec); for (Defined *d : isec->symbols) diff --git a/lld/MachO/ICF.h b/lld/MachO/ICF.h index a287692d7ffa..b7e695d81d34 100644 --- a/lld/MachO/ICF.h +++ b/lld/MachO/ICF.h @@ -19,7 +19,7 @@ class Symbol; void markAddrSigSymbols(); void markSymAsAddrSig(Symbol *s); -void foldIdenticalSections(); +void foldIdenticalSections(bool onlyCfStrings); } // namespace macho } // namespace lld diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp index fda6900edabe..e3bf553e5334 100644 --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -263,11 +263,15 @@ static Optional<size_t> getRecordSize(StringRef segname, StringRef name) { if (segname == segment_names::ld) return target->wordSize == 8 ? 32 : 20; } - if (config->icfLevel == ICFLevel::none) + if (!config->dedupLiterals) return {}; if (name == section_names::cfString && segname == segment_names::data) return target->wordSize == 8 ? 32 : 16; + + if (config->icfLevel == ICFLevel::none) + return {}; + if (name == section_names::objcClassRefs && segname == segment_names::data) return target->wordSize; return {}; @@ -359,6 +363,9 @@ void ObjFile::parseSections(ArrayRef<SectionHeader> sectionHeaders) { // have the same name without causing duplicate symbol errors. To avoid // spurious duplicate symbol errors, we do not parse these sections. // TODO: Evaluate whether the bitcode metadata is needed. + } else if (name == section_names::objCImageInfo && + segname == segment_names::data) { + objCImageInfo = data; } else { if (name == section_names::addrSig) addrSigSection = sections.back(); @@ -556,7 +563,7 @@ void ObjFile::parseOptimizationHints(ArrayRef<uint8_t> data) { if (section == sections.end()) return; ++subsection; - if (subsection == (*section)->subsections.end()) { + while (subsection == (*section)->subsections.end()) { ++section; if (section == sections.end()) return; @@ -578,6 +585,7 @@ void ObjFile::parseOptimizationHints(ArrayRef<uint8_t> data) { if (section == sections.end()) break; updateAddr(); + assert(hintStart->offset0 >= subsectionBase); } } @@ -899,7 +907,6 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders, if (sym.n_type & N_STAB) continue; - StringRef name = strtab + sym.n_strx; if ((sym.n_type & N_TYPE) == N_SECT) { Subsections &subsections = sections[sym.n_sect - 1]->subsections; // parseSections() may have chosen not to parse this section. @@ -909,7 +916,7 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders, } else if (isUndef(sym)) { undefineds.push_back(i); } else { - symbols[i] = parseNonSectionSymbol(sym, name); + symbols[i] = parseNonSectionSymbol(sym, StringRef(strtab + sym.n_strx)); } } @@ -1186,14 +1193,27 @@ ArrayRef<data_in_code_entry> ObjFile::getDataInCode() const { void ObjFile::registerCompactUnwind(Section &compactUnwindSection) { for (const Subsection &subsection : compactUnwindSection.subsections) { ConcatInputSection *isec = cast<ConcatInputSection>(subsection.isec); - // Hack!! Since each CUE contains a different function address, if ICF - // operated naively and compared the entire contents of each CUE, entries - // with identical unwind info but belonging to different functions would - // never be considered equivalent. To work around this problem, we slice - // away the function address here. (Note that we do not adjust the offsets - // of the corresponding relocations.) We rely on `relocateCompactUnwind()` - // to correctly handle these truncated input sections. - isec->data = isec->data.slice(target->wordSize); + // Hack!! Each compact unwind entry (CUE) has its UNSIGNED relocations embed + // their addends in its data. Thus if ICF operated naively and compared the + // entire contents of each CUE, entries with identical unwind info but e.g. + // belonging to different functions would never be considered equivalent. To + // work around this problem, we remove some parts of the data containing the + // embedded addends. In particular, we remove the function address and LSDA + // pointers. Since these locations are at the start and end of the entry, + // we can do this using a simple, efficient slice rather than performing a + // copy. We are not losing any information here because the embedded + // addends have already been parsed in the corresponding Reloc structs. + // + // Removing these pointers would not be safe if they were pointers to + // absolute symbols. In that case, there would be no corresponding + // relocation. However, (AFAIK) MC cannot emit references to absolute + // symbols for either the function address or the LSDA. However, it *can* do + // so for the personality pointer, so we are not slicing that field away. + // + // Note that we do not adjust the offsets of the corresponding relocations; + // instead, we rely on `relocateCompactUnwind()` to correctly handle these + // truncated input sections. + isec->data = isec->data.slice(target->wordSize, 8 + target->wordSize); uint32_t encoding = read32le(isec->data.data() + sizeof(uint32_t)); // llvm-mc omits CU entries for functions that need DWARF encoding, but // `ld -r` doesn't. We can ignore them because we will re-synthesize these @@ -1240,11 +1260,23 @@ void ObjFile::registerCompactUnwind(Section &compactUnwindSection) { continue; } d->unwindEntry = isec; - // Since we've sliced away the functionAddress, we should remove the - // corresponding relocation too. Given that clang emits relocations in - // reverse order of address, this relocation should be at the end of the - // vector for most of our input object files, so this is typically an O(1) - // operation. + // Now that the symbol points to the unwind entry, we can remove the reloc + // that points from the unwind entry back to the symbol. + // + // First, the symbol keeps the unwind entry alive (and not vice versa), so + // this keeps dead-stripping simple. + // + // Moreover, it reduces the work that ICF needs to do to figure out if + // functions with unwind info are foldable. + // + // However, this does make it possible for ICF to fold CUEs that point to + // distinct functions (if the CUEs are otherwise identical). + // UnwindInfoSection takes care of this by re-duplicating the CUEs so that + // each one can hold a distinct functionAddress value. + // + // Given that clang emits relocations in reverse order of address, this + // relocation should be at the end of the vector for most of our input + // object files, so this erase() is typically an O(1) operation. it = isec->relocs.erase(it); } } @@ -1500,6 +1532,14 @@ void ObjFile::registerEhFrames(Section &ehFrameSection) { Defined *funcSym; if (funcAddrRelocIt != isec->relocs.end()) { funcSym = targetSymFromCanonicalSubtractor(isec, funcAddrRelocIt); + // Canonicalize the symbol. If there are multiple symbols at the same + // address, we want both `registerEhFrame` and `registerCompactUnwind` + // to register the unwind entry under same symbol. + // This is not particularly efficient, but we should run into this case + // infrequently (only when handling the output of `ld -r`). + if (funcSym->isec) + funcSym = findSymbolAtOffset(cast<ConcatInputSection>(funcSym->isec), + funcSym->value); } else { funcSym = findSymbolAtAddress(sections, funcAddr); ehRelocator.makePcRel(funcAddrOff, funcSym, target->p2WordSize); diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h index efddc1c46782..5deb05272a6b 100644 --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -120,6 +120,7 @@ public: std::vector<Symbol *> symbols; std::vector<Section *> sections; + ArrayRef<uint8_t> objCImageInfo; // If not empty, this stores the name of the archive containing this file. // We use this string for creating error messages. diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp index df312525df61..76b11d9da4f8 100644 --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -345,6 +345,11 @@ bool macho::isEhFrameSection(const InputSection *isec) { isec->getSegName() == segment_names::text; } +bool macho::isGccExceptTabSection(const InputSection *isec) { + return isec->getName() == section_names::gccExceptTab && + isec->getSegName() == segment_names::text; +} + std::string lld::toString(const InputSection *isec) { return (toString(isec->getFile()) + ":(" + isec->getName() + ")").str(); } diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h index e8710c25f534..afe76c56b536 100644 --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -281,6 +281,7 @@ bool isCodeSection(const InputSection *); bool isCfStringSection(const InputSection *); bool isClassRefsSection(const InputSection *); bool isEhFrameSection(const InputSection *); +bool isGccExceptTabSection(const InputSection *); extern std::vector<ConcatInputSection *> inputSections; @@ -320,7 +321,7 @@ constexpr const char objcCatList[] = "__objc_catlist"; constexpr const char objcClassList[] = "__objc_classlist"; constexpr const char objcClassRefs[] = "__objc_classrefs"; constexpr const char objcConst[] = "__objc_const"; -constexpr const char objcImageInfo[] = "__objc_imageinfo"; +constexpr const char objCImageInfo[] = "__objc_imageinfo"; constexpr const char objcNonLazyCatList[] = "__objc_nlcatlist"; constexpr const char objcNonLazyClassList[] = "__objc_nlclslist"; constexpr const char objcProtoList[] = "__objc_protolist"; diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp index e87c4c413106..0b76216d24b5 100644 --- a/lld/MachO/LTO.cpp +++ b/lld/MachO/LTO.cpp @@ -138,8 +138,22 @@ std::vector<ObjFile *> BitcodeCompiler::compile() { saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o"); } - if (!config->ltoObjPath.empty()) - fs::create_directories(config->ltoObjPath); + // In ThinLTO mode, Clang passes a temporary directory in -object_path_lto, + // while the argument is a single file in FullLTO mode. + bool objPathIsDir = true; + if (!config->ltoObjPath.empty()) { + if (std::error_code ec = fs::create_directories(config->ltoObjPath)) + fatal("cannot create LTO object path " + config->ltoObjPath + ": " + + ec.message()); + + if (!fs::is_directory(config->ltoObjPath)) { + objPathIsDir = false; + unsigned objCount = + count_if(buf, [](const SmallString<0> &b) { return !b.empty(); }); + if (objCount > 1) + fatal("-object_path_lto must specify a directory when using ThinLTO"); + } + } std::vector<ObjFile *> ret; for (unsigned i = 0; i != maxTasks; ++i) { @@ -149,9 +163,10 @@ std::vector<ObjFile *> BitcodeCompiler::compile() { uint32_t modTime = 0; if (!config->ltoObjPath.empty()) { filePath = config->ltoObjPath; - path::append(filePath, Twine(i) + "." + - getArchitectureName(config->arch()) + - ".lto.o"); + if (objPathIsDir) + path::append(filePath, Twine(i) + "." + + getArchitectureName(config->arch()) + + ".lto.o"); saveBuffer(buf[i], filePath); modTime = getModTime(filePath); } diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td index 9b57f8a0bd49..b3d74a83f582 100644 --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -505,7 +505,6 @@ def reexported_symbols_list : Separate<["-"], "reexported_symbols_list">, def alias : MultiArg<["-"], "alias", 2>, MetaVarName<"<symbol_name> <alternate_name>">, HelpText<"Create a symbol alias with default global visibility">, - Flags<[HelpHidden]>, Group<grp_resolve>; def alias_list : Separate<["-"], "alias_list">, MetaVarName<"<file>">, diff --git a/lld/MachO/SectionPriorities.cpp b/lld/MachO/SectionPriorities.cpp index ac4878343ac0..fbc7796c3f8a 100644 --- a/lld/MachO/SectionPriorities.cpp +++ b/lld/MachO/SectionPriorities.cpp @@ -370,7 +370,7 @@ macho::PriorityBuilder::buildInputSectionPriorities() { if (!symbolPriority) return; size_t &priority = sectionPriorities[sym->isec]; - priority = std::max(priority, symbolPriority.getValue()); + priority = std::max(priority, symbolPriority.value()); }; // TODO: Make sure this handles weak symbols correctly. diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp index 7bda1d13069f..3667a7137291 100644 --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -117,6 +117,13 @@ Defined *SymbolTable::addDefined(StringRef name, InputFile *file, return defined; } +Defined *SymbolTable::aliasDefined(Defined *src, StringRef target) { + return addDefined(target, src->getFile(), src->isec, src->value, src->size, + src->isWeakDef(), src->privateExtern, src->thumb, + src->referencedDynamically, src->noDeadStrip, + src->weakDefCanBeHidden); +} + Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file, bool isWeakRef) { Symbol *s; diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h index 0ecfa6dcd093..1b090105e0ca 100644 --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -43,6 +43,8 @@ public: bool isReferencedDynamically, bool noDeadStrip, bool isWeakDefCanBeHidden); + Defined *aliasDefined(Defined *src, StringRef target); + Symbol *addUndefined(StringRef name, InputFile *, bool isWeakRef); Symbol *addCommon(StringRef name, InputFile *, uint64_t size, uint32_t align, diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp index 0a57de319994..8e7ca520336c 100644 --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -57,7 +57,7 @@ static void sha256(const uint8_t *data, size_t len, uint8_t *output) { #else ArrayRef<uint8_t> block(data, len); std::array<uint8_t, 32> hash = SHA256::hash(block); - assert(hash.size() == CodeSignatureSection::hashSize); + static_assert(hash.size() == CodeSignatureSection::hashSize, ""); memcpy(output, hash.data(), hash.size()); #endif } @@ -164,62 +164,108 @@ RebaseSection::RebaseSection() : LinkEditSection(segment_names::linkEdit, section_names::rebase) {} namespace { -struct Rebase { - OutputSegment *segment = nullptr; - uint64_t offset = 0; - uint64_t consecutiveCount = 0; +struct RebaseState { + uint64_t sequenceLength; + uint64_t skipLength; }; } // namespace -// Rebase opcodes allow us to describe a contiguous sequence of rebase location -// using a single DO_REBASE opcode. To take advantage of it, we delay emitting -// `DO_REBASE` until we have reached the end of a contiguous sequence. -static void encodeDoRebase(Rebase &rebase, raw_svector_ostream &os) { - assert(rebase.consecutiveCount != 0); - if (rebase.consecutiveCount <= REBASE_IMMEDIATE_MASK) { - os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_IMM_TIMES | - rebase.consecutiveCount); +static void emitIncrement(uint64_t incr, raw_svector_ostream &os) { + assert(incr != 0); + + if ((incr >> target->p2WordSize) <= REBASE_IMMEDIATE_MASK && + (incr % target->wordSize) == 0) { + os << static_cast<uint8_t>(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | + (incr >> target->p2WordSize)); } else { - os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); - encodeULEB128(rebase.consecutiveCount, os); + os << static_cast<uint8_t>(REBASE_OPCODE_ADD_ADDR_ULEB); + encodeULEB128(incr, os); } - rebase.consecutiveCount = 0; } -static void encodeRebase(const OutputSection *osec, uint64_t outSecOff, - Rebase &lastRebase, raw_svector_ostream &os) { - OutputSegment *seg = osec->parent; - uint64_t offset = osec->getSegmentOffset() + outSecOff; - if (lastRebase.segment != seg || lastRebase.offset != offset) { - if (lastRebase.consecutiveCount != 0) - encodeDoRebase(lastRebase, os); +static void flushRebase(const RebaseState &state, raw_svector_ostream &os) { + assert(state.sequenceLength > 0); - if (lastRebase.segment != seg) { - os << static_cast<uint8_t>(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | - seg->index); - encodeULEB128(offset, os); - lastRebase.segment = seg; - lastRebase.offset = offset; + if (state.skipLength == target->wordSize) { + if (state.sequenceLength <= REBASE_IMMEDIATE_MASK) { + os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_IMM_TIMES | + state.sequenceLength); } else { - assert(lastRebase.offset != offset); - uint64_t delta = offset - lastRebase.offset; - // For unknown reasons, ld64 checks if the scaled offset is strictly less - // than REBASE_IMMEDIATE_MASK instead of allowing equality. We match this - // behavior as a precaution. - if ((delta % target->wordSize == 0) && - (delta / target->wordSize < REBASE_IMMEDIATE_MASK)) { - os << static_cast<uint8_t>(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | - (delta / target->wordSize)); - } else { - os << static_cast<uint8_t>(REBASE_OPCODE_ADD_ADDR_ULEB); - encodeULEB128(delta, os); - } - lastRebase.offset = offset; + os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + encodeULEB128(state.sequenceLength, os); } + } else if (state.sequenceLength == 1) { + os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); + encodeULEB128(state.skipLength - target->wordSize, os); + } else { + os << static_cast<uint8_t>( + REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); + encodeULEB128(state.sequenceLength, os); + encodeULEB128(state.skipLength - target->wordSize, os); } - ++lastRebase.consecutiveCount; - // DO_REBASE causes dyld to both perform the binding and increment the offset - lastRebase.offset += target->wordSize; +} + +// Rebases are communicated to dyld using a bytecode, whose opcodes cause the +// memory location at a specific address to be rebased and/or the address to be +// incremented. +// +// Opcode REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB is the most generic +// one, encoding a series of evenly spaced addresses. This algorithm works by +// splitting up the sorted list of addresses into such chunks. If the locations +// are consecutive or the sequence consists of a single location, flushRebase +// will use a smaller, more specialized encoding. +static void encodeRebases(const OutputSegment *seg, + MutableArrayRef<Location> locations, + raw_svector_ostream &os) { + // dyld operates on segments. Translate section offsets into segment offsets. + for (Location &loc : locations) + loc.offset = + loc.isec->parent->getSegmentOffset() + loc.isec->getOffset(loc.offset); + // The algorithm assumes that locations are unique. + Location *end = + llvm::unique(locations, [](const Location &a, const Location &b) { + return a.offset == b.offset; + }); + size_t count = end - locations.begin(); + + os << static_cast<uint8_t>(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | + seg->index); + assert(!locations.empty()); + uint64_t offset = locations[0].offset; + encodeULEB128(offset, os); + + RebaseState state{1, target->wordSize}; + + for (size_t i = 1; i < count; ++i) { + offset = locations[i].offset; + + uint64_t skip = offset - locations[i - 1].offset; + assert(skip != 0 && "duplicate locations should have been weeded out"); + + if (skip == state.skipLength) { + ++state.sequenceLength; + } else if (state.sequenceLength == 1) { + ++state.sequenceLength; + state.skipLength = skip; + } else if (skip < state.skipLength) { + // The address is lower than what the rebase pointer would be if the last + // location would be part of a sequence. We start a new sequence from the + // previous location. + --state.sequenceLength; + flushRebase(state, os); + + state.sequenceLength = 2; + state.skipLength = skip; + } else { + // The address is at some positive offset from the rebase pointer. We + // start a new sequence which begins with the current location. + flushRebase(state, os); + emitIncrement(skip - state.skipLength, os); + state.sequenceLength = 1; + state.skipLength = target->wordSize; + } + } + flushRebase(state, os); } void RebaseSection::finalizeContents() { @@ -227,19 +273,20 @@ void RebaseSection::finalizeContents() { return; raw_svector_ostream os{contents}; - Rebase lastRebase; - os << static_cast<uint8_t>(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER); llvm::sort(locations, [](const Location &a, const Location &b) { return a.isec->getVA(a.offset) < b.isec->getVA(b.offset); }); - for (const Location &loc : locations) - encodeRebase(loc.isec->parent, loc.isec->getOffset(loc.offset), lastRebase, - os); - if (lastRebase.consecutiveCount != 0) - encodeDoRebase(lastRebase, os); + for (size_t i = 0, count = locations.size(); i < count;) { + const OutputSegment *seg = locations[i].isec->parent->parent; + size_t j = i + 1; + while (j < count && locations[j].isec->parent->parent == seg) + ++j; + encodeRebases(seg, {locations.data() + i, locations.data() + j}, os); + i = j; + } os << static_cast<uint8_t>(REBASE_OPCODE_DONE); } @@ -1574,6 +1621,86 @@ void WordLiteralSection::writeTo(uint8_t *buf) const { memcpy(buf + p.second * 4, &p.first, 4); } +ObjCImageInfoSection::ObjCImageInfoSection() + : SyntheticSection(segment_names::data, section_names::objCImageInfo) {} + +ObjCImageInfoSection::ImageInfo +ObjCImageInfoSection::parseImageInfo(const InputFile *file) { + ImageInfo info; + ArrayRef<uint8_t> data = file->objCImageInfo; + // The image info struct has the following layout: + // struct { + // uint32_t version; + // uint32_t flags; + // }; + if (data.size() < 8) { + warn(toString(file) + ": invalid __objc_imageinfo size"); + return info; + } + + auto *buf = reinterpret_cast<const uint32_t *>(data.data()); + if (read32le(buf) != 0) { + warn(toString(file) + ": invalid __objc_imageinfo version"); + return info; + } + + uint32_t flags = read32le(buf + 1); + info.swiftVersion = (flags >> 8) & 0xff; + info.hasCategoryClassProperties = flags & 0x40; + return info; +} + +static std::string swiftVersionString(uint8_t version) { + switch (version) { + case 1: + return "1.0"; + case 2: + return "1.1"; + case 3: + return "2.0"; + case 4: + return "3.0"; + case 5: + return "4.0"; + default: + return ("0x" + Twine::utohexstr(version)).str(); + } +} + +// Validate each object file's __objc_imageinfo and use them to generate the +// image info for the output binary. Only two pieces of info are relevant: +// 1. The Swift version (should be identical across inputs) +// 2. `bool hasCategoryClassProperties` (true only if true for all inputs) +void ObjCImageInfoSection::finalizeContents() { + assert(files.size() != 0); // should have already been checked via isNeeded() + + info.hasCategoryClassProperties = true; + const InputFile *firstFile; + for (auto file : files) { + ImageInfo inputInfo = parseImageInfo(file); + info.hasCategoryClassProperties &= inputInfo.hasCategoryClassProperties; + + // swiftVersion 0 means no Swift is present, so no version checking required + if (inputInfo.swiftVersion == 0) + continue; + + if (info.swiftVersion != 0 && info.swiftVersion != inputInfo.swiftVersion) { + error("Swift version mismatch: " + toString(firstFile) + " has version " + + swiftVersionString(info.swiftVersion) + " but " + toString(file) + + " has version " + swiftVersionString(inputInfo.swiftVersion)); + } else { + info.swiftVersion = inputInfo.swiftVersion; + firstFile = file; + } + } +} + +void ObjCImageInfoSection::writeTo(uint8_t *buf) const { + uint32_t flags = info.hasCategoryClassProperties ? 0x40 : 0x0; + flags |= info.swiftVersion << 8; + write32le(buf + 4, flags); +} + void macho::createSyntheticSymbols() { auto addHeaderSymbol = [](const char *name) { symtab->addSynthetic(name, in.header->isec, /*value=*/0, diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h index 4f7d5288c9dc..afdd46d8a7de 100644 --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -19,6 +19,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SetVector.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/MathExtras.h" @@ -600,6 +601,27 @@ private: std::unordered_map<uint32_t, uint64_t> literal4Map; }; +class ObjCImageInfoSection final : public SyntheticSection { +public: + ObjCImageInfoSection(); + bool isNeeded() const override { return !files.empty(); } + uint64_t getSize() const override { return 8; } + void addFile(const InputFile *file) { + assert(!file->objCImageInfo.empty()); + files.push_back(file); + } + void finalizeContents(); + void writeTo(uint8_t *buf) const override; + +private: + struct ImageInfo { + uint8_t swiftVersion = 0; + bool hasCategoryClassProperties = false; + } info; + static ImageInfo parseImageInfo(const InputFile *); + std::vector<const InputFile *> files; // files with image info +}; + struct InStruct { const uint8_t *bufferStart = nullptr; MachHeaderSection *header = nullptr; @@ -616,6 +638,7 @@ struct InStruct { StubsSection *stubs = nullptr; StubHelperSection *stubHelper = nullptr; UnwindInfoSection *unwindInfo = nullptr; + ObjCImageInfoSection *objCImageInfo = nullptr; ConcatInputSection *imageLoaderCache = nullptr; }; diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h index 597502275dee..ef54dbb80408 100644 --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -65,14 +65,19 @@ public: // on a level of address indirection. virtual void relaxGotLoad(uint8_t *loc, uint8_t type) const = 0; - virtual const RelocAttrs &getRelocAttrs(uint8_t type) const = 0; - virtual uint64_t getPageSize() const = 0; virtual void populateThunk(InputSection *thunk, Symbol *funcSym) { llvm_unreachable("target does not use thunks"); } + const RelocAttrs &getRelocAttrs(uint8_t type) const { + assert(type < relocAttrs.size() && "invalid relocation type"); + if (type >= relocAttrs.size()) + return invalidRelocAttrs; + return relocAttrs[type]; + } + bool hasAttr(uint8_t type, RelocAttrBits bit) const { return getRelocAttrs(type).hasAttr(bit); } @@ -111,6 +116,8 @@ public: uint8_t subtractorRelocType; uint8_t unsignedRelocType; + llvm::ArrayRef<RelocAttrs> relocAttrs; + // We contrive this value as sufficiently far from any valid address that it // will always be out-of-range for any architecture. UINT64_MAX is not a // good choice because it is (a) only 1 away from wrapping to 0, and (b) the diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp index 8c3425a17459..322057947a3d 100644 --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -211,7 +211,7 @@ void UnwindInfoSection::addSymbol(const Defined *d) { // we use that as the key here. auto p = symbols.insert({{d->isec, d->value}, d}); // If we have multiple symbols at the same address, only one of them can have - // an associated CUE. + // an associated unwind entry. if (!p.second && d->unwindEntry) { assert(!p.first->second->unwindEntry); p.first->second = d; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index 9395e1a068a3..7fad9f5564ce 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -1164,6 +1164,10 @@ template <class LP> void Writer::run() { if (in.stubHelper->isNeeded()) in.stubHelper->setup(); + + if (in.objCImageInfo->isNeeded()) + in.objCImageInfo->finalizeContents(); + // At this point, we should know exactly which output sections are needed, // courtesy of scanSymbols() and scanRelocations(). createOutputSections<LP>(); @@ -1210,6 +1214,7 @@ void macho::createSyntheticSections() { in.stubs = make<StubsSection>(); in.stubHelper = make<StubHelperSection>(); in.unwindInfo = makeUnwindInfoSection(); + in.objCImageInfo = make<ObjCImageInfoSection>(); // This section contains space for just a single word, and will be used by // dyld to cache an address to the image loader it uses. |
