Index: ELF/CMakeLists.txt =================================================================== --- ELF/CMakeLists.txt +++ ELF/CMakeLists.txt @@ -5,6 +5,7 @@ add_lld_library(lldELF Driver.cpp DriverUtils.cpp + DynamicList.cpp Error.cpp ICF.cpp InputFiles.cpp Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -48,6 +48,7 @@ llvm::StringRef SoName; llvm::StringRef Sysroot; std::string RPath; + std::vector DynamicList; std::vector SearchPaths; std::vector Undefined; bool AllowMultipleDefinition; Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -12,6 +12,7 @@ #include "SymbolTable.h" #include "lld/Core/LLVM.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/raw_ostream.h" @@ -29,7 +30,9 @@ private: std::vector getArchiveMembers(MemoryBufferRef MB); + llvm::Optional readFile(StringRef Path); void readConfigs(llvm::opt::InputArgList &Args); + void readDynamicList(StringRef Path); void createFiles(llvm::opt::InputArgList &Args); template void link(llvm::opt::InputArgList &Args); Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Driver.h" +#include "DynamicList.h" #include "Config.h" #include "Error.h" #include "ICF.h" @@ -100,14 +101,10 @@ using namespace llvm::sys::fs; if (Config->Verbose || Config->Trace) llvm::outs() << Path << "\n"; - auto MBOrErr = MemoryBuffer::getFile(Path); - if (!MBOrErr) { - error(MBOrErr, "cannot open " + Path); + Optional Buffer = readFile(Path); + if (!Buffer.hasValue()) return; - } - std::unique_ptr &MB = *MBOrErr; - MemoryBufferRef MBRef = MB->getMemBufferRef(); - OwningMBs.push_back(std::move(MB)); // take MB ownership + MemoryBufferRef MBRef = *Buffer; switch (identify_magic(MBRef.getBuffer())) { case file_magic::unknown: @@ -136,6 +133,23 @@ } } +Optional LinkerDriver::readFile(StringRef Path) { + auto MBOrErr = MemoryBuffer::getFile(Path); + if (std::error_code EC = MBOrErr.getError()) { + error(MBOrErr, "cannot open " + Path); + return None; + } + std::unique_ptr &MB = *MBOrErr; + MemoryBufferRef MBRef = MB->getMemBufferRef(); + OwningMBs.push_back(std::move(MB)); // take MB ownership + return MBRef; +} + +void LinkerDriver::readDynamicList(StringRef Path) { + if (Optional Buffer = readFile(Path)) + parseDynamicList(*Buffer); +} + // Add a given library by searching it from input search paths. void LinkerDriver::addLibrary(StringRef Name) { std::string Path = searchLibrary(Name); @@ -351,6 +365,9 @@ for (auto *Arg : Args.filtered(OPT_undefined)) Config->Undefined.push_back(Arg->getValue()); + + if (Args.hasArg(OPT_dynamic_list)) + readDynamicList(getString(Args, OPT_dynamic_list)); } void LinkerDriver::createFiles(opt::InputArgList &Args) { @@ -437,6 +454,7 @@ // Write the result to the file. Symtab.scanShlibUndefined(); + Symtab.scanDynamicList(); if (Config->GcSections) markLive(&Symtab); if (Config->ICF) Index: ELF/DynamicList.h =================================================================== --- /dev/null +++ ELF/DynamicList.h @@ -0,0 +1,25 @@ +//===- DynamicList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_DYNAMIC_LIST_H +#define LLD_ELF_DYNAMIC_LIST_H + +#include "lld/Core/LLVM.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace lld { +namespace elf { + +void parseDynamicList(MemoryBufferRef MB); + +} // namespace elf +} // namespace lld + +#endif Index: ELF/DynamicList.cpp =================================================================== --- /dev/null +++ ELF/DynamicList.cpp @@ -0,0 +1,68 @@ +//===- LinkerScript.cpp ---------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the parser/evaluator of the linker script. +// It does not construct an AST but consume linker script directives directly. +// Results are written to Driver or Config object. +// +//===----------------------------------------------------------------------===// + +#include "DynamicList.h" +#include "Config.h" +#include "Driver.h" +#include "ScriptParser.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/StringSaver.h" + +using namespace llvm; +using namespace llvm::object; +using namespace lld; +using namespace lld::elf; + +// Parse the --dynamic-list argument. A dynamic list is in the form +// +// { symbol1; symbol2; [...]; symbolN }; +// +// Multiple groups can be defined in the same file and they are merged +// in only one definition. + +class DynamicListParser final : public ScriptParserBase { +public: + DynamicListParser(StringRef S) : ScriptParserBase(S) {} + + void run() override; + +private: + void readGroup(); +}; + +// Parse the default group definition using C language symbol name. +void DynamicListParser::readGroup() { + expect("{"); + while (!Error) { + Config->DynamicList.push_back(next()); + expect(";"); + if (peek() == "}") { + next(); + break; + } + } + expect(";"); +} + +void DynamicListParser::run() { + while (!atEOF()) + readGroup(); +} + +void elf::parseDynamicList(MemoryBufferRef MB) { + DynamicListParser(MB.getBuffer()).run(); +} Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -44,6 +44,9 @@ def dynamic_linker : Separate<["--", "-"], "dynamic-linker">, HelpText<"Which dynamic linker to use">; +def dynamic_list : Separate<["--", "-"], "dynamic-list">, + HelpText<"Dynamic list to use">; + def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">, HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">; @@ -175,6 +178,7 @@ def alias_L__library_path : Joined<["--"], "library-path=">, Alias; def alias_discard_all_x: Flag<["-"], "x">, Alias; def alias_discard_locals_X: Flag<["-"], "X">, Alias; +def alias_dynamic_list: Joined<["--", "-"], "dynamic-list=">, Alias; def alias_entry_e : JoinedOrSeparate<["-"], "e">, Alias; def alias_export_dynamic_E: Flag<["-"], "E">, Alias; def alias_fini_fini : Joined<["-"], "fini=">, Alias; Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -60,6 +60,7 @@ uint8_t Visibility = llvm::ELF::STV_HIDDEN); void scanShlibUndefined(); + void scanDynamicList(); SymbolBody *find(StringRef Name); void wrap(StringRef Name); InputFile *findFile(SymbolBody *B); Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -336,6 +336,14 @@ Sym->MustBeInDynSym = true; } +// This function process the dynamic list option by marking all the symbols +// to be exported in the dynamic table. +template void SymbolTable::scanDynamicList() { + for (StringRef S : Config->DynamicList) + if (SymbolBody *B = find(S)) + B->MustBeInDynSym = true; +} + template class elf::SymbolTable; template class elf::SymbolTable; template class elf::SymbolTable; Index: test/ELF/dynamic-list.s =================================================================== --- /dev/null +++ test/ELF/dynamic-list.s @@ -0,0 +1,104 @@ +## There is some bad quoting interaction between lit's internal shell, which is +## implemented in Python, and the Cygwin implementations of the Unix utilities. +## Avoid running these tests on Windows for now by requiring a real shell. + +# REQUIRES: shell + +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o +# RUN: ld.lld -shared %t2.o -soname shared -o %t2.so +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +## Check exporting only one symbol. +# RUN: echo "{ foo1; };" > %t.list +# RUN: ld.lld --dynamic-list %t.list %t %t2.so -o %t.exe +# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck %s + +## And now using quoted strings (the output is the same since it does +## use any wildcard character). +# RUN: echo "{ \"foo1\"; };" > %t.list +# RUN: ld.lld --dynamic-list %t.list %t %t2.so -o %t.exe +# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck %s + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: foo1@ (1) +# CHECK-NEXT: Value: 0x11000 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x4) +# CHECK-NEXT: } +# CHECK-NEXT: ] + + +## Now export all the foo1, foo2, and foo31 symbols +# RUN: echo "{ foo1; foo2; foo31; };" > %t.list +# RUN: ld.lld --dynamic-list %t.list %t %t2.so -o %t.exe +# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=CHECK2 %s + +# CHECK2: DynamicSymbols [ +# CHECK2-NEXT: Symbol { +# CHECK2-NEXT: Name: @ (0) +# CHECK2-NEXT: Value: 0x0 +# CHECK2-NEXT: Size: 0 +# CHECK2-NEXT: Binding: Local +# CHECK2-NEXT: Type: None +# CHECK2-NEXT: Other: 0 +# CHECK2-NEXT: Section: Undefined +# CHECK2-NEXT: } +# CHECK2-NEXT: Symbol { +# CHECK2-NEXT: Name: foo1@ (1) +# CHECK2-NEXT: Value: 0x11000 +# CHECK2-NEXT: Size: 0 +# CHECK2-NEXT: Binding: Global (0x1) +# CHECK2-NEXT: Type: None (0x0) +# CHECK2-NEXT: Other: 0 +# CHECK2-NEXT: Section: .text (0x4) +# CHECK2-NEXT: } +# CHECK2-NEXT: Symbol { +# CHECK2-NEXT: Name: foo2@ (6) +# CHECK2-NEXT: Value: 0x11001 +# CHECK2-NEXT: Size: 0 +# CHECK2-NEXT: Binding: Global (0x1) +# CHECK2-NEXT: Type: None (0x0) +# CHECK2-NEXT: Other: 0 +# CHECK2-NEXT: Section: .text (0x4) +# CHECK2-NEXT: } +# CHECK2-NEXT: Symbol { +# CHECK2-NEXT: Name: foo31@ (11) +# CHECK2-NEXT: Value: 0x11002 +# CHECK2-NEXT: Size: 0 +# CHECK2-NEXT: Binding: Global (0x1) +# CHECK2-NEXT: Type: None (0x0) +# CHECK2-NEXT: Other: 0 +# CHECK2-NEXT: Section: .text (0x4) +# CHECK2-NEXT: } +# CHECK2-NEXT: ] + +.globl foo1 +foo1: + ret + +.globl foo2 +foo2: + ret + +.globl foo31 +foo31: + ret + +.globl _start +_start: + retq Index: test/ELF/invalid-dynamic-list.test =================================================================== --- /dev/null +++ test/ELF/invalid-dynamic-list.test @@ -0,0 +1,37 @@ +## Different "echo" commands on Windows interpret quoted strings and +## wildcards in similar but different way (On Windows, ARGV tokenization +## and wildcard expansion are not done by the shell but by each command.) +## Because of that reason, this test fails on some Windows environment. +## We can't write quoted strings that are interpreted the same way +## by all echo commands. So, we don't want to run this on Windows. + +# REQUIRES: shell + +# RUN: mkdir -p %t.dir + +# RUN: echo foobar > %t1 +# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR1 %s +# ERR1: line 1: { expected, but got foobar + +# RUN: echo "{ foobar;" > %t1 +# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR2 %s +# ERR2: line 1: unexpected EOF + +## Missing ';' before '}' +# RUN: echo "{ foobar }" > %t1 +# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR3 %s +# ERR3: line 1: ; expected, but got } + +## Missing final ';' +# RUN: echo "{ foobar; }" > %t1 +# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR4 %s +# ERR4: line 1: unexpected EOF + +## Missing \" in foobar definition +# RUN echo "{ \"foobar; };" > %t1 +# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR5 %s +# ERR5: line 1: unexpected EOF + +# RUN: echo "{ extern \"BOGUS\" { test }; };" > %t1 +# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR6 %s +# ERR6: line 1: ; expected, but got BOGUS