diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -10,6 +10,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" @@ -53,6 +54,7 @@ uint64_t val) const override; void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + ArrayRef libcalls() const override; }; } // namespace @@ -867,6 +869,36 @@ memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData)); } +#define LCALLNAMES(A, N) \ + #A #N "_relax", \ + #A #N "_acq", \ + #A #N "_rel", \ + #A #N "_acq_rel", +#define LCALLNAME4(A) \ + LCALLNAMES(A, 1) \ + LCALLNAMES(A, 2) LCALLNAMES(A, 4) LCALLNAMES(A, 8) +#define LCALLNAME5(A) \ + LCALLNAMES(A, 1) \ + LCALLNAMES(A, 2) \ + LCALLNAMES(A, 4) LCALLNAMES(A, 8) LCALLNAMES(A, 16) + +static const char *aarch64Libcalls[] = { + LCALLNAME5(__aarch64_cas) + LCALLNAME4(__aarch64_swp) + LCALLNAME4(__aarch64_ldadd) + LCALLNAME4(__aarch64_ldset) + LCALLNAME4(__aarch64_ldclr) + LCALLNAME4(__aarch64_ldeor) +}; + +#undef LCALLNAMES +#undef LCALLNAME4 +#undef LCALLNAME5 + +ArrayRef AArch64::libcalls() const { + return makeArrayRef(aarch64Libcalls); +} + static TargetInfo *getTargetInfo() { if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1788,6 +1788,12 @@ handleUndefined(sym, "--undefined-glob"); } +template static void parseDeplibs(InputFile *file) { + assert(file->lazy); + if (auto *f = dyn_cast>(file)) + f->parseDeplibs(); +} + static void handleLibcall(StringRef name) { Symbol *sym = symtab->find(name); if (!sym || !sym->isLazy()) @@ -1798,6 +1804,8 @@ if (isBitcode(mb)) sym->extract(); + else if (config->dependentLibraries && !config->relocatable) + invokeELFT(parseDeplibs, sym->file); } void LinkerDriver::writeArchiveStats() const { @@ -2454,15 +2462,6 @@ } } - // Now that we have every file, we can decide if we will need a - // dynamic symbol table. - // We need one if we were asked to export dynamic symbols or if we are - // producing a shared library. - // We also need one if any shared libraries are used and for pie executables - // (probably because the dynamic linker needs it). - config->hasDynSymTab = - !sharedFiles.empty() || config->isPic || config->exportDynamic; - // Some symbols (such as __ehdr_start) are defined lazily only when there // are undefined symbols for them, so we add these to trigger that logic. for (StringRef name : script->referencedSymbols) { @@ -2504,12 +2503,37 @@ // use 64-bit atomics. // // Therefore, we only add libcall symbols to the link before LTO if we have - // to, i.e. if the symbol's definition is in bitcode. Any other required - // libcall symbols will be added to the link after LTO when we add the LTO - // object file to the link. - if (!bitcodeFiles.empty()) + // to, i.e. if the symbol's definition is in bitcode. If the definition's + // object files contain deplibs, these are summarily added to the link as + // well, since the set of input files must be fixed by the time LTO occurs. + // Any other required libcall symbols will be added to the link after LTO when + // we add the LTO object file to the link. + const size_t numFilesBeforeLibcalls = files.size(); + if (!bitcodeFiles.empty()) { for (auto *s : lto::LTO::getRuntimeLibcallSymbols()) handleLibcall(s); + for (auto *s : target->libcalls()) + handleLibcall(s); + } + + // Handling libcalls may have added new input files to the link via deplibs, + // so parse them. + { + llvm::TimeTraceScope timeScope("Parse input files"); + for (size_t i = numFilesBeforeLibcalls; i < files.size(); ++i) { + llvm::TimeTraceScope timeScope("Parse input files", files[i]->getName()); + parseFile(files[i]); + } + } + + // Now that we have every file, we can decide if we will need a + // dynamic symbol table. + // We need one if we were asked to export dynamic symbols or if we are + // producing a shared library. + // We also need one if any shared libraries are used and for pie executables + // (probably because the dynamic linker needs it). + config->hasDynSymTab = + !sharedFiles.empty() || config->isPic || config->exportDynamic; // Archive members defining __wrap symbols may be extracted. std::vector wrapped = addWrappedSymbols(args); diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -228,6 +228,11 @@ void parse(bool ignoreComdats = false); void parseLazy(); + // Parse only the dependent library sections and add the referenced libraries. + // Needed to finalize the set of input files possibly referenced by libcalls + // before LTO. + void parseDeplibs(); + StringRef getShtGroupSignature(ArrayRef sections, const Elf_Shdr &sec); @@ -271,6 +276,10 @@ // but had one or more functions with the no_split_stack attribute. bool someNoSplitStack = false; + // True if the dependent library sections have already been parsed and the + // contents added to the link. + bool parsedDeplibs = false; + // Get cached DWARF information. DWARFCache *getDwarf(); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -20,6 +20,7 @@ #include "lld/Common/DWARF.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Support/ARMAttributeParser.h" @@ -383,6 +384,8 @@ else initializeSections(ignoreComdats, obj); + parsedDeplibs = true; + // Read a symbol table. initializeSymbols(obj); } @@ -919,6 +922,9 @@ } if (sec.sh_type == SHT_LLVM_DEPENDENT_LIBRARIES && !config->relocatable) { + if (parsedDeplibs) + return &InputSection::discarded; + ArrayRef data = CHECK(this->getObj().template getSectionContentsAsArray(sec), this); if (!data.empty() && data.back() != '\0') { @@ -1769,6 +1775,42 @@ } } +template void ObjFile::parseDeplibs() { + if (parsedDeplibs) + return; + parsedDeplibs = true; + + ArrayRef objSections = getELFShdrs(); + StringRef shstrtab = + CHECK(this->getObj().getSectionStringTable(objSections), this); + for (size_t i = 0, e = objSections.size(); i != e; ++i) { + const Elf_Shdr &sec = objSections[i]; + if (sec.sh_type != SHT_LLVM_DEPENDENT_LIBRARIES || + sec.sh_flags & SHF_EXCLUDE) + continue; + + StringRef name = check(this->getObj().getSectionName(sec, shstrtab)); + ArrayRef data = CHECK( + this->getObj().template getSectionContentsAsArray(sec), this); + if (!data.empty() && data.back() != '\0') { + error( + toString(this) + + ": corrupted dependent libraries section (unterminated string): " + + name); + } + // Shared libraries found through this process may not be needed if LTO + // doesn't emit the corresponding libcalls. + auto resetAsNeeded = make_scope_exit( + [oldAsNeeded = config->asNeeded] { config->asNeeded = oldAsNeeded; }); + config->asNeeded = true; + for (const char *d = data.begin(), *e = data.end(); d < e;) { + StringRef s(d); + addDependentLibrary(s, this); + d += s.size() + 1; + } + } +} + bool InputFile::shouldExtractForCommon(StringRef name) { if (isBitcode(mb)) return isBitcodeNonCommonDef(mb, name, archiveName); diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -92,6 +92,10 @@ virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} + // Returns a list of target-specific runtime libcalls, not including + // target-independent libcalls. + virtual ArrayRef libcalls() const { return {}; } + virtual ~TargetInfo(); // This deletes a jump insn at the end of the section if it is a fall thru to diff --git a/lld/test/ELF/deplibs-lto-shared.s b/lld/test/ELF/deplibs-lto-shared.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/deplibs-lto-shared.s @@ -0,0 +1,43 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t.inputs %t.dir +# RUN: mkdir -p %t.inputs %t.dir +# RUN: split-file %s %t.inputs +# RUN: llvm-mc -filetype=obj -triple=aarch64 %t.inputs/deplibs.s -o %tdeplibs.o +# RUN: llvm-mc -filetype=obj -triple=aarch64 %t.inputs/foo.s -o %tfoo.o +# RUN: llvm-as %t.inputs/lto.ll -o %t.o +# RUN: llvm-ar rc %t.dir/libdeplibs.a %tdeplibs.o +# RUN: ld.lld %tfoo.o -o %t.dir/libfoo.so -shared + +# LTO can emit libcalls that are resolved using libraries that contain .deplibs. +# Such libraries must be handled as if they were input files, and any referenced +# deplibs must be added to the link. Otherwise, symbols in the library may go +# undefined. Shared libraries added this way should be as-needed, since the +# libcalls may not actually be emitted. + +# RUN: ld.lld %t.o -u a -o %t -L %t.dir -ldeplibs --trace 2>&1 | \ +# RUN: FileCheck %s -DOBJ=%t.o -DDIR=%t.dir +# RUN: llvm-readobj -d %t | FileCheck --check-prefix=OBJ %s + +# CHECK: [[OBJ]] +# CHECK-NEXT: [[DIR]]{{[\\/]}}libfoo.so +# OBJ-NOT: NEEDED + +#--- foo.s +.global foo +foo: +#--- deplibs.s +.global __aarch64_ldadd4_relax +__aarch64_ldadd4_relax: + b foo +.section ".deplibs","MS",@llvm_dependent_libraries,1 + .asciz "foo" +.section ".deplibs.excluded","eMS",@llvm_dependent_libraries,1 + .asciz "bar" +#--- lto.ll +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64" + +define void @_start() { + ret void +} diff --git a/lld/test/ELF/deplibs-lto-static.s b/lld/test/ELF/deplibs-lto-static.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/deplibs-lto-static.s @@ -0,0 +1,46 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t.inputs %t.dir +# RUN: mkdir -p %t.inputs %t.dir +# RUN: split-file %s %t.inputs +# RUN: llvm-mc -filetype=obj -triple=aarch64 %t.inputs/deplibs.s -o %tdeplibs.o +# RUN: llvm-mc -filetype=obj -triple=aarch64 %t.inputs/foo.s -o %tfoo.o +# RUN: llvm-as %t.inputs/lto.ll -o %t.o +# RUN: llvm-ar rc %t.dir/libdeplibs.a %tdeplibs.o +# RUN: llvm-ar rc %t.dir/libfoo.a %tfoo.o + +# LTO can emit libcalls that are resolved using libraries that contain .deplibs. +# Such libraries must be handled as if they were input files, and any referenced +# deplibs must be added to the link. Otherwise, symbols in the library may go +# undefined. + +# RUN: ld.lld %t.o -u a -o /dev/null -L %t.dir -ldeplibs --trace 2>&1 | \ +# RUN: FileCheck %s -DOBJ=%t.o -DDIR=%t.dir + +# CHECK: [[OBJ]] +# CHECK-NEXT: [[DIR]]{{[\\/]}}libdeplibs.a +# CHECK-NEXT: [[DIR]]{{[\\/]}}libfoo.a + +#--- foo.s +.global foo +foo: +#--- deplibs.s +.global __aarch64_ldadd4_relax +__aarch64_ldadd4_relax: + b foo +.section ".deplibs","MS",@llvm_dependent_libraries,1 + .asciz "foo" +#--- lto.ll +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64" + +define void @a(i32* nocapture %0) #0 { + %2 = atomicrmw add i32* %0, i32 1 monotonic, align 4 + ret void +} + +define void @_start() { + ret void +} + +attributes #0 = { "target-features"="+outline-atomics" }