Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -137,6 +137,7 @@ bool Cref; bool DefineCommon; bool Demangle = true; + bool DependentLibraries; bool DisableVerify; bool EhFrameHdr; bool EmitLLVM; Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -63,6 +63,7 @@ llvm::Optional findFromSearchPaths(StringRef Path); llvm::Optional searchScript(StringRef Path); +llvm::Optional searchLibraryBaseName(StringRef Path); llvm::Optional searchLibrary(StringRef Path); } // namespace elf Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -790,6 +790,7 @@ Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common, !Args.hasArg(OPT_relocatable)); Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true); + Config->DependentLibraries = Args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); Config->Discard = getDiscard(Args); Config->DwoDir = Args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); @@ -1548,9 +1549,11 @@ Symtab->trace(Arg->getValue()); // Add all files to the symbol table. This will add almost all - // symbols that we need to the symbol table. - for (InputFile *F : Files) - parseFile(F); + // symbols that we need to the symbol table. This process might + // add files to the link, via autolinking, these files are always + // appended to the Files vector. + for (size_t I = 0; I < Files.size(); ++I) + parseFile(Files[I]); // Now that we have every file, we can decide if we will need a // dynamic symbol table. Index: ELF/DriverUtils.cpp =================================================================== --- ELF/DriverUtils.cpp +++ ELF/DriverUtils.cpp @@ -223,12 +223,9 @@ return None; } -// This is for -lfoo. We'll look for libfoo.so or libfoo.a from +// This is for -l. We'll look for lib.so or lib.a from // search paths. -Optional elf::searchLibrary(StringRef Name) { - if (Name.startswith(":")) - return findFromSearchPaths(Name.substr(1)); - +Optional elf::searchLibraryBaseName(StringRef Name) { for (StringRef Dir : Config->SearchPaths) { if (!Config->Static) if (Optional S = findFile(Dir, "lib" + Name + ".so")) @@ -239,6 +236,13 @@ return None; } +// This is for -l. +Optional elf::searchLibrary(StringRef Name) { + if (Name.startswith(":")) + return findFromSearchPaths(Name.substr(1)); + return searchLibraryBaseName (Name); +} + // If a linker/version script doesn't exist in the current directory, we also // look for the script in the '-L' search paths. This matches the behaviour of // '-T', --version-script=, and linker script INPUT() command in ld.bfd. Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "Driver.h" #include "InputSection.h" #include "LinkerScript.h" #include "SymbolTable.h" @@ -499,6 +500,27 @@ } } +// An ELF object file may contain a `.deplibs` section. If it exists, the +// section contains a list of library specifiers such as `m` for libm. This +// function resolves a given name by finding the first matching library checking +// the various ways that a library can be specified to LLD. This ELF extension +// is a form of autolinking and is called `dependent libraries`. It is currently +// unique to LLVM and lld. +static void addDependentLibrary(StringRef Specifier, const InputFile *F) { + if (!Config->DependentLibraries) + return; + if (fs::exists(Specifier)) + Driver->addFile(Specifier, /*WithLOption=*/false); + else if (Optional S = findFromSearchPaths(Specifier)) + Driver->addFile(*S, /*WithLOption=*/true); + else if (Optional S = searchLibraryBaseName(Specifier)) + Driver->addFile(*S, /*WithLOption=*/true); + else + error(toString(F) + + ": unable to find library from dependent library specifier: " + + Specifier); +} + template void ObjFile::initializeSections( DenseSet &ComdatGroups) { @@ -740,6 +762,24 @@ } return &InputSection::Discarded; } + case SHT_LLVM_DEPENDENT_LIBRARIES: { + if (Config->Relocatable) + break; + 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); + return &InputSection::Discarded; + } + for (const char *D = Data.begin(), *E = Data.end(); D < E;) { + StringRef S(D); + addDependentLibrary(S, this); + D += S.size() + 1; + } + return &InputSection::Discarded; + } case SHT_RELA: case SHT_REL: { // Find a relocation target section and associate this section with that. @@ -1302,6 +1342,9 @@ for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) Symbols.push_back(createBitcodeSymbol(KeptComdats, ObjSym, *this)); + + for (auto L : Obj->getDependentLibraries()) + addDependentLibrary(L, this); } static ELFKind getELFKind(MemoryBufferRef MB, StringRef ArchiveName) { Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -71,6 +71,10 @@ "Apply link-time values for dynamic relocations", "Do not apply link-time values for dynamic relocations (default)">; +defm dependent_libraries: B<"dependent-libraries", + "Process dependent library specifiers from input files (default)", + "Ignore dependent library specifiers from input files">; + defm as_needed: B<"as-needed", "Only set DT_NEEDED for shared libraries if used", "Always set DT_NEEDED for shared libraries (default)">; Index: test/ELF/Inputs/deplibs-lib_bar.s =================================================================== --- test/ELF/Inputs/deplibs-lib_bar.s +++ test/ELF/Inputs/deplibs-lib_bar.s @@ -0,0 +1,2 @@ + .global bar +bar: Index: test/ELF/Inputs/deplibs-lib_foo.s =================================================================== --- test/ELF/Inputs/deplibs-lib_foo.s +++ test/ELF/Inputs/deplibs-lib_foo.s @@ -0,0 +1,2 @@ + .global foo +foo: Index: test/ELF/deplibs-colon-prefix.s =================================================================== --- test/ELF/deplibs-colon-prefix.s +++ test/ELF/deplibs-colon-prefix.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/deplibs-lib_foo.s -o %tfoo.o +# RUN: rm -rf %t.dir +# RUN: mkdir -p %t.dir +# RUN: llvm-ar rc %t.dir/foo.a %tfoo.o +# RUN: not ld.lld %t.o -o /dev/null -L %t.dir 2>&1 | FileCheck %s -DOBJ=%t.o +# CHECK: error: [[OBJ]]: unable to find library from dependent library specifier: :foo.a + + .global _start +_start: + call foo + .section ".deplibs","MS",@llvm_dependent_libraries,1 + .asciz ":foo.a" Index: test/ELF/deplibs-corrupt.s =================================================================== --- test/ELF/deplibs-corrupt.s +++ test/ELF/deplibs-corrupt.s @@ -0,0 +1,8 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: not ld.lld %t.o -o /dev/null -L %t.dir 2>&1 | FileCheck %s -DOBJ=%t.o +# CHECK: error: [[OBJ]]: corrupted dependent libraries section (unterminated string): .deplibs + +.section ".deplibs","MS",@llvm_dependent_libraries,1 + .ascii ":foo.a" Index: test/ELF/deplibs.s =================================================================== --- test/ELF/deplibs.s +++ test/ELF/deplibs.s @@ -0,0 +1,56 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/deplibs-lib_foo.s -o %tfoo.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/deplibs-lib_bar.s -o %tbar.o +# RUN: rm -rf %t.dir %t.cwd +# RUN: mkdir -p %t.dir + +# error if dependent libraries cannot be found +# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s -DOBJ=%t.o --check-prefix MISSING +# MISSING: error: [[OBJ]]: unable to find library from dependent library specifier: foo.a +# MISSING-NEXT: error: [[OBJ]]: unable to find library from dependent library specifier: bar + +# can ignore dependent libraries +# RUN: not ld.lld %t.o -o /dev/null --no-dependent-libraries 2>&1 | FileCheck %s --check-prefix IGNORE +# IGNORE: error: undefined symbol: foo +# IGNORE: error: undefined symbol: bar + +# -r links preserve dependent libraries +# RUN: ld.lld %t.o %t.o -r -o %t-r.o +# RUN: not ld.lld %t-r.o -o /dev/null 2>&1 | sort | FileCheck %s -DOBJ=%t-r.o --check-prefixes MINUSR +# MINUSR: error: [[OBJ]]: unable to find library from dependent library specifier: bar +# MINUSR-NEXT: error: [[OBJ]]: unable to find library from dependent library specifier: foo.a +# MINUSR-NOT: unable to find library from dependent library specifier + +# static archives located relative to library search paths +# RUN: llvm-ar rc %t.dir/foo.a %tfoo.o +# RUN: llvm-ar rc %t.dir/libbar.a %tbar.o +# RUN: ld.lld %t.o -o /dev/null -L %t.dir + +# shared objects located relative to library search paths +# RUN: rm %t.dir/libbar.a +# RUN: ld.lld -shared -o %t.dir/libbar.so %tbar.o +# RUN: ld.lld -Bdynamic %t.o -o /dev/null -L %t.dir + +# dependent libraries searched for symbols after libraries on the command line +# RUN: mkdir -p %t.cwd +# RUN: cd %t.cwd +# RUN: cp %t.dir/foo.a %t.cwd/libcmdline.a +# RUN: ld.lld %t.o libcmdline.a -o /dev/null -L %t.dir --trace 2>&1 | FileCheck %s -DOBJ=%t.o -DSO=%t.dir --check-prefix CMDLINE --implicit-check-not foo.a +# CMDLINE: [[OBJ]] +# CMDLINE-NEXT: {{^libcmdline\.a}} +# CMDLINE-NEXT: [[SO]]{{[\\/]}}libbar.so + +# libraries can be found from specifiers as if the specifiers were listed on on the command-line. +# RUN: cp %t.dir/foo.a %t.cwd/foo.a +# RUN: ld.lld %t.o -o /dev/null -L %t.dir --trace 2>&1 | FileCheck %s -DOBJ=%t.o -DSO=%t.dir --check-prefix ASIFCMDLINE --implicit-check-not foo.a +# ASIFCMDLINE: [[OBJ]] +# ASIFCMDLINE-NEXT: {{^foo\.a}} +# ASIFCMDLINE-NEXT: [[SO]]{{[\\/]}}libbar.so + + call foo + call bar +.section ".deplibs","MS",@llvm_dependent_libraries,1 + .asciz "foo.a" + .asciz "bar" Index: test/ELF/lto/deplibs.s =================================================================== --- test/ELF/lto/deplibs.s +++ test/ELF/lto/deplibs.s @@ -0,0 +1,15 @@ +; REQUIRES: x86 + +; RUN: llvm-as %s -o %t.o +; RUN: not ld.lld -shared %t.o -o /dev/null 2>&1 | FileCheck %s -DOBJ=%t.o + +; CHECK: error: [[OBJ]]: unable to find library from dependent library specifier: foo +; CHECK: error: [[OBJ]]: unable to find library from dependent library specifier: bar + +target triple = "x86_64-unknown-linux-gnu" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +!llvm.dependent-libraries = !{!0, !1} + +!0 = !{!"foo"} +!1 = !{!"bar"}