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 + prepareInputs(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,32 @@ return createELFFile(MB); } +static ErrorOr searchLibrary(StringRef Path) { + bool HasColonPrefix = Path[0] == ':'; + Twine StaticLibraryName = + HasColonPrefix ? Twine(Path.drop_front()) : (Twine("lib", Path) + ".a"); + Twine DynamicLibraryName = + HasColonPrefix ? StaticLibraryName : (Twine("lib", Path) + ".so"); + SmallString<128> FullPath; + for (StringRef Dir : Config->InputSearchPaths) { + // Search for dynamic library first + // TODO: add support for '-static' option + FullPath = Dir; + sys::path::append(FullPath, DynamicLibraryName); + if (sys::fs::exists(FullPath.str())) + return FullPath.str().str(); + + // Search for static libraries + FullPath = Dir; + sys::path::append(FullPath, StaticLibraryName); + if (sys::fs::exists(FullPath.str())) + return FullPath.str().str(); + } + if (HasColonPrefix && sys::fs::exists(StaticLibraryName)) + return StaticLibraryName.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 +107,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; @@ -92,12 +123,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 = prepareInputs(Args); if (Inputs.empty()) error("no input files."); @@ -129,3 +155,35 @@ return; } } + +std::vector +LinkerDriver::prepareInputs(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 @@ -29,6 +29,22 @@ Alias; //===----------------------------------------------------------------------===// +/// 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; + +//===----------------------------------------------------------------------===// /// Ignored options //===----------------------------------------------------------------------===// Index: test/elf2/Inputs/libsearch.s =================================================================== --- /dev/null +++ test/elf2/Inputs/libsearch.s @@ -0,0 +1,4 @@ +.globl _bar +.type _bar, @function +_bar: + ret Index: test/elf2/libsearch.s =================================================================== --- /dev/null +++ test/elf2/libsearch.s @@ -0,0 +1,24 @@ +// 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 + +// RUN: lld -flavor gnu2 -o %t3 %t.o -L%T -lls +// 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 +// RUN: lld -flavor gnu2 -o %t3 %t.o --library-path=%T --library=:libls.a +// RUN: lld -flavor gnu2 -o %t3 %t.o --library-path=%T --library=:libls.so + +.globl _start; +_start: + cal _bar + mov $60, %rax + mov $42, %rdi + syscall +