Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -19,6 +19,7 @@ llvm::StringRef OutputFile; llvm::StringRef DynamicLinker; std::string RPath; + std::vector InputSearchPaths; bool Shared = false; bool DiscardAll = false; bool DiscardLocals = false; Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -35,6 +35,15 @@ void link(ArrayRef Args); private: + // Create a list of input files. + std::vector + openInputFiles(const llvm::opt::InputArgList &Args); + + // Handle -l and --library command line options + void onOptLibrary(std::vector &Inputs, StringRef Path); + // Hanlde input file + void onOptInput(std::vector &Inputs, StringRef Path); + ArgParser Parser; // Opens a file. Path has to be resolved already. Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -15,7 +15,9 @@ #include "Writer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" using namespace llvm; @@ -59,6 +61,34 @@ return createELFFile(MB); } +/// \brief Searches a given library from input search paths. +/// +/// Search paths are filled from -L command line switches. +/// Uses exact file name if \p Path starts with ':' +/// or searches for 'libXXX.so' or 'libXXX.a' files otherwise. +/// +/// \returns a file path on success or an error code on failure. +static ErrorOr searchLibrary(StringRef Path) { + SmallVector, 2> Names; + if (Path[0] == ':') { + Names.emplace_back(Path.drop_front()); + } else { + Names.emplace_back((Twine("lib", Path) + ".a").str()); + // TODO: add support for '-static' option + Names.emplace_back((Twine("lib", Path) + ".so").str()); + } + SmallString<128> FullPath; + for (StringRef Dir : Config->InputSearchPaths) { + for (const auto &Name : Names) { + FullPath = Dir; + sys::path::append(FullPath, Name); + if (sys::fs::exists(FullPath.str())) + return FullPath.str().str(); + } + } + return make_error_code(errc::no_such_file_or_directory); +} + void LinkerDriver::link(ArrayRef ArgsArr) { // Parse command line options. opt::InputArgList Args = Parser.parse(ArgsArr); @@ -79,6 +109,9 @@ if (!RPaths.empty()) Config->RPath = llvm::join(RPaths.begin(), RPaths.end(), ":"); + for (auto *Arg : Args.filtered(OPT_L)) + Config->InputSearchPaths.push_back(Arg->getValue()); + if (Args.hasArg(OPT_shared)) Config->Shared = true; @@ -95,12 +128,7 @@ Config->ExportDynamic = true; // Create a list of input files. - std::vector Inputs; - - for (auto *Arg : Args.filtered(OPT_INPUT)) { - StringRef Path = Arg->getValue(); - Inputs.push_back(openFile(Path)); - } + std::vector Inputs = openInputFiles(Args); if (Inputs.empty()) error("no input files."); @@ -132,3 +160,35 @@ return; } } + +std::vector +LinkerDriver::openInputFiles(const llvm::opt::InputArgList &Args) { + std::vector Inputs; + + for (auto *Arg : Args) { + switch (Arg->getOption().getID()) { + case OPT_l: + onOptLibrary(Inputs, Arg->getValue()); + break; + case OPT_INPUT: + onOptInput(Inputs, Arg->getValue()); + break; + } + } + + return Inputs; +} + +void LinkerDriver::onOptLibrary(std::vector &Inputs, + StringRef Path) { + const ErrorOr PathOrErr = searchLibrary(Path); + error(PathOrErr, Twine("Unable to find library -l", Path)); + assert(sys::fs::exists(PathOrErr.get())); + onOptInput(Inputs, PathOrErr.get()); +} + +void LinkerDriver::onOptInput(std::vector &Inputs, + StringRef Path) { + // TODO: Add support for linker scripts. + Inputs.push_back(openFile(Path)); +} Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -33,3 +33,19 @@ def export_dynamic : Flag<["--"], "export-dynamic">, HelpText<"Put symbols in the dynamic symbol table">; + +//===----------------------------------------------------------------------===// +/// Executable Options +//===----------------------------------------------------------------------===// + +def L : Joined<["-"], "L">, MetaVarName<"">, + HelpText<"Directory to search for libraries">; + +def alias_L : Joined<["--"], "library-path=">, + Alias; + +def l : Joined<["-"], "l">, MetaVarName<"">, + HelpText<"Root name of library to use">; + +def alias_l : Joined<["--"], "library=">, + Alias; Index: test/elf2/Inputs/libsearch.s =================================================================== --- /dev/null +++ test/elf2/Inputs/libsearch.s @@ -0,0 +1,2 @@ +.globl _bar +_bar: Index: test/elf2/libsearch.s =================================================================== --- /dev/null +++ test/elf2/libsearch.s @@ -0,0 +1,22 @@ +// 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/libsearch.s -o %t2.o +// RUN: lld -flavor gnu2 -shared %t2.o -o %T/libls.so +// RUN: llvm-ar rcs %T/libls.a %t2.o +// REQUIRES: x86 + +// Should not link because of undefined symbol _bar +// RUN: not lld -flavor gnu2 -o %t3 %t.o 2>&1 | FileCheck --check-prefix=UNDEFINED %s +// UNDEFINED: undefined symbol: _bar + +// Should fail if cannot find specified library +// RUN: not lld -flavor gnu2 -o %t3 %t.o -lls 2>&1 | FileCheck --check-prefix=NOLIB %s +// NOLIB: Unable to find library -lls + +// RUN: lld -flavor gnu2 -o %t3 %t.o -L%T -lls +// RUN: lld -flavor gnu2 -o %t3 %t.o -lls -L%T +// RUN: lld -flavor gnu2 -o %t3 %t.o -L%T -l:libls.a +// RUN: lld -flavor gnu2 -o %t3 %t.o -L%T -l:libls.so +// RUN: lld -flavor gnu2 -o %t3 %t.o --library-path=%T --library=ls + +.globl _start,_bar; +_start: