Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -11,11 +11,18 @@ #define LLD_ELF_DRIVER_H #include "lld/Core/LLVM.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Option/ArgList.h" namespace lld { namespace elf2 { +class SymbolTable; + +// The owner of all opened files. +extern std::vector> *MemoryBufferPool; +MemoryBufferRef openFile(StringRef Path); + // Entry point of the ELF linker. void link(ArrayRef Args); @@ -34,13 +41,6 @@ private: ArgParser Parser; - - // Opens a file. Path has to be resolved already. - MemoryBufferRef openFile(StringRef Path); - - // Driver is the owner of all opened files. - // InputFiles have MemoryBufferRefs to them. - std::vector> OwningMBs; }; // Create enum with OPT_xxx values for each option in Options.td @@ -51,6 +51,9 @@ #undef OPTION }; +// Parses a linker script. Calling this function updates the Symtab and Config. +void readLinkerScript(SymbolTable *Symtab, MemoryBufferRef MB); + } // namespace elf2 } // namespace lld Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -25,39 +25,30 @@ namespace lld { namespace elf2 { + Configuration *Config; +std::vector> *MemoryBufferPool; void link(ArrayRef Args) { Configuration C; Config = &C; + std::vector> V; + MemoryBufferPool = &V; LinkerDriver().link(Args.slice(1)); } -} -} - // Opens a file. Path has to be resolved already. // Newly created memory buffers are owned by this driver. -MemoryBufferRef LinkerDriver::openFile(StringRef Path) { +MemoryBufferRef openFile(StringRef Path) { ErrorOr> MBOrErr = MemoryBuffer::getFile(Path); error(MBOrErr, Twine("cannot open ") + Path); std::unique_ptr &MB = *MBOrErr; MemoryBufferRef MBRef = MB->getMemBufferRef(); - OwningMBs.push_back(std::move(MB)); // take ownership + MemoryBufferPool->push_back(std::move(MB)); // transfer ownership return MBRef; } -static std::unique_ptr createFile(MemoryBufferRef MB) { - using namespace llvm::sys::fs; - file_magic Magic = identify_magic(MB.getBuffer()); - - if (Magic == file_magic::archive) - return make_unique(MB); - - if (Magic == file_magic::elf_shared_object) - return createELFFile(MB); - - return createELFFile(MB); +} } // Makes a path by concatenating Dir and File. @@ -95,6 +86,13 @@ error(Twine("Unable to find library -l") + Path); } +// Returns true if MB looks like a linker script. +static bool isLinkerScript(MemoryBufferRef MB) { + using namespace llvm::sys::fs; + file_magic Magic = identify_magic(MB.getBuffer()); + return Magic == file_magic::unknown; +} + void LinkerDriver::link(ArrayRef ArgsArr) { // Parse command line options. opt::InputArgList Args = Parser.parse(ArgsArr); @@ -128,31 +126,25 @@ Config->NoInhibitExec = Args.hasArg(OPT_noinhibit_exec); Config->Shared = Args.hasArg(OPT_shared); - // Create a list of input files. - std::vector Inputs; + // Create a symbol table. + SymbolTable Symtab; for (auto *Arg : Args.filtered(OPT_l, OPT_INPUT)) { - StringRef Path = Arg->getValue(); - if (Arg->getOption().getID() == OPT_l) { - Inputs.push_back(openFile(searchLibrary(Path))); + std::string Path = Arg->getValue(); + if (Arg->getOption().getID() == OPT_l) + Path = searchLibrary(Path); + MemoryBufferRef MB = openFile(Path); + if (isLinkerScript(MB)) { + // readLinkerScript may add files to the symbol table. + readLinkerScript(&Symtab, MB); continue; } - Inputs.push_back(openFile(Path)); + Symtab.addFile(createFile(MB)); } - if (Inputs.empty()) + if (Symtab.getObjectFiles().empty()) error("no input files."); - // Create a symbol table. - SymbolTable Symtab; - - // Parse all input files and put all symbols to the symbol table. - // The symbol table will take care of name resolution. - for (MemoryBufferRef MB : Inputs) { - std::unique_ptr File = createFile(MB); - Symtab.addFile(std::move(File)); - } - // Write the result. const ELFFileBase *FirstObj = Symtab.getFirstELF(); switch (FirstObj->getELFKind()) { Index: ELF/DriverUtils.cpp =================================================================== --- ELF/DriverUtils.cpp +++ ELF/DriverUtils.cpp @@ -13,10 +13,14 @@ // //===----------------------------------------------------------------------===// +#include "Config.h" #include "Driver.h" #include "Error.h" +#include "SymbolTable.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/StringSaver.h" using namespace llvm; @@ -75,3 +79,124 @@ return Args; } + +// Parser and evaluator of the linker script. +// Results are directly written to the Config object. +namespace { +class LinkerScript { +public: + LinkerScript(SymbolTable *T, StringRef S) : Symtab(T), Tokens(tokenize(S)) {} + void run(); + +private: + static std::vector tokenize(StringRef S); + static StringRef skipSpace(StringRef S); + StringRef next(); + bool atEOF() { return Tokens.size() == Pos; } + void expect(StringRef Expect); + + void readAsNeeded(); + void readGroup(); + void readOutputFormat(); + + SymbolTable *Symtab; + std::vector Tokens; + size_t Pos = 0; +}; +} + +void LinkerScript::run() { + while (!atEOF()) { + StringRef Tok = next(); + if (Tok == "GROUP") { + readGroup(); + } else if (Tok == "OUTPUT_FORMAT") { + readOutputFormat(); + } else { + error("unknown directive: " + Tok); + } + } +} + +// Split S into linker script tokens. +std::vector LinkerScript::tokenize(StringRef S) { + std::vector Ret; + for (;;) { + S = skipSpace(S); + if (S.empty()) + return Ret; + size_t Pos = S.find_first_not_of( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789_.$/\\~=+[]*?-:"); + // A character that cannot start a word (which is usually a + // punctuation) forms a single character token. + if (Pos == 0) + Pos = 1; + Ret.push_back(S.substr(0, Pos)); + S = S.substr(Pos); + } +} + +// Skip leading whitespace characters or /**/-style comments. +StringRef LinkerScript::skipSpace(StringRef S) { + for (;;) { + if (S.startswith("/*")) { + size_t E = S.find("*/", 2); + if (E == StringRef::npos) + error("unclosed comment in a linker script"); + S = S.substr(E + 2); + continue; + } + size_t Size = S.size(); + S = S.ltrim(); + if (S.size() == Size) + return S; + } +} + +StringRef LinkerScript::next() { + if (Pos == Tokens.size()) + error("unexpected EOF"); + return Tokens[Pos++]; +} + +void LinkerScript::expect(StringRef Expect) { + StringRef Tok = next(); + if (Tok != Expect) + error(Expect + " expected, but got " + Tok); +} + +void LinkerScript::readAsNeeded() { + expect("("); + for (;;) { + StringRef Tok = next(); + if (Tok == ")") + return; + Symtab->addFile(createFile(openFile(Tok))); + } +} + +void LinkerScript::readGroup() { + expect("("); + for (;;) { + StringRef Tok = next(); + if (Tok == ")") + return; + if (Tok == "AS_NEEDED") { + readAsNeeded(); + continue; + } + Symtab->addFile(createFile(openFile(Tok))); + } +} + +void LinkerScript::readOutputFormat() { + // Error checking only for now. + expect("("); + next(); + expect(")"); +} + +void lld::elf2::readLinkerScript(SymbolTable *Symtab, MemoryBufferRef MB) { + LinkerScript(Symtab, MB.getBuffer()).run(); +} Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -25,9 +25,12 @@ using llvm::object::Archive; +class InputFile; class Lazy; class SymbolBody; +std::unique_ptr createFile(MemoryBufferRef MB); + // The root class of input files. class InputFile { public: Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -16,10 +16,20 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace llvm::sys::fs; using namespace lld; using namespace lld::elf2; +std::unique_ptr lld::elf2::createFile(MemoryBufferRef MB) { + file_magic Magic = identify_magic(MB.getBuffer()); + if (Magic == file_magic::archive) + return make_unique(MB); + if (Magic == file_magic::elf_shared_object) + return createELFFile(MB); + return createELFFile(MB); +} + template static uint16_t getEMachine(const ELFFileBase &B) { bool IsShared = isa(B); if (IsShared) Index: test/elf2/basic.s =================================================================== --- test/elf2/basic.s +++ test/elf2/basic.s @@ -177,11 +177,24 @@ # CHECK-NEXT: } # CHECK-NEXT: ] + +# Test for the response file # RUN: echo " -o %t2" > %t.responsefile # RUN: lld -flavor gnu2 %t @%t.responsefile # RUN: llvm-readobj -file-headers -sections -program-headers -symbols %t2 \ # RUN: | FileCheck %s +# Test for the linker script +# RUN: echo "GROUP(" %t ")" > %t.script +# RUN: lld -flavor gnu2 -o %t2 %t.script +# RUN: llvm-readobj -file-headers -sections -program-headers -symbols %t2 \ +# RUN: | FileCheck %s + +# RUN: echo "OUTPUT_FORMAT(elf64-x86-64) /*/*/ GROUP(" %t ")" > %t.script +# RUN: lld -flavor gnu2 -o %t2 %t.script +# RUN: llvm-readobj -file-headers -sections -program-headers -symbols %t2 \ +# RUN: | FileCheck %s + # RUN: not lld -flavor gnu2 %t.foo -o %t2 2>&1 | \ # RUN: FileCheck --check-prefix=MISSING %s # MISSING: cannot open {{.*}}.foo: {{[Nn]}}o such file or directory Index: test/elf2/linkerscript.test =================================================================== --- /dev/null +++ test/elf2/linkerscript.test @@ -0,0 +1,5 @@ +# RUN: echo "FOO(BAR)" > %t.script +# RUN: not lld -flavor gnu2 -o foo %t.script > %t.log 2>&1 +# RUN: FileCheck -check-prefix=ERR1 %s < %t.log + +ERR1: unknown directive: FOO