ELF: Implement --dynamic-list

This patch implements the --dynamic-list option, which adds a list of
global symbol that either should not be bounded by default definition
when creating shared libraries, or add in dynamic symbol table in the
case of creating executables.

The patch modifies the ScriptParserBase class to use a list of Token
instead of StringRef, which contains information if the token is a
quoted or unquoted strings. It is used to use a faster search for
exact match symbol name.

The input file follow a similar format of linker script with some
simplifications (it does not have scope or node names). It leads
to a simplified parser define in DynamicList.{cpp,h}.

Different from ld/gold neither glob pattern nor mangled names
(extern 'C++') are currently supported.

llvm-svn: 266227
This commit is contained in:
Adhemerval Zanella 2016-04-13 18:51:11 +00:00
parent 97b0663492
commit 9df0720766
11 changed files with 272 additions and 7 deletions

View File

@ -5,6 +5,7 @@ add_public_tablegen_target(ELFOptionsTableGen)
add_lld_library(lldELF
Driver.cpp
DriverUtils.cpp
DynamicList.cpp
Error.cpp
ICF.cpp
InputFiles.cpp

View File

@ -48,6 +48,7 @@ struct Configuration {
llvm::StringRef SoName;
llvm::StringRef Sysroot;
std::string RPath;
std::vector<llvm::StringRef> DynamicList;
std::vector<llvm::StringRef> SearchPaths;
std::vector<llvm::StringRef> Undefined;
bool AllowMultipleDefinition;

View File

@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "Driver.h"
#include "DynamicList.h"
#include "Config.h"
#include "Error.h"
#include "ICF.h"
@ -100,14 +101,10 @@ void LinkerDriver::addFile(StringRef Path) {
using namespace llvm::sys::fs;
if (Config->Verbose)
llvm::outs() << Path << "\n";
auto MBOrErr = MemoryBuffer::getFile(Path);
if (!MBOrErr) {
error(MBOrErr, "cannot open " + Path);
Optional<MemoryBufferRef> Buffer = readFile(Path);
if (!Buffer.hasValue())
return;
}
std::unique_ptr<MemoryBuffer> &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 @@ void LinkerDriver::addFile(StringRef Path) {
}
}
Optional<MemoryBufferRef> 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<MemoryBuffer> &MB = *MBOrErr;
MemoryBufferRef MBRef = MB->getMemBufferRef();
OwningMBs.push_back(std::move(MB)); // take MB ownership
return MBRef;
}
void LinkerDriver::readDynamicList(StringRef Path) {
if (Optional<MemoryBufferRef> 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 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
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 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Write the result to the file.
Symtab.scanShlibUndefined();
Symtab.scanDynamicList();
if (Config->GcSections)
markLive<ELFT>(&Symtab);
if (Config->ICF)

View File

@ -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 @@ public:
private:
std::vector<MemoryBufferRef> getArchiveMembers(MemoryBufferRef MB);
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
void readConfigs(llvm::opt::InputArgList &Args);
void readDynamicList(StringRef Path);
void createFiles(llvm::opt::InputArgList &Args);
template <class ELFT> void link(llvm::opt::InputArgList &Args);

64
lld/ELF/DynamicList.cpp Normal file
View File

@ -0,0 +1,64 @@
//===- 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 "ScriptParser.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
using namespace llvm;
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();
}

24
lld/ELF/DynamicList.h Normal file
View File

@ -0,0 +1,24 @@
//===- 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/Support/MemoryBuffer.h"
namespace lld {
namespace elf {
void parseDynamicList(MemoryBufferRef MB);
} // namespace elf
} // namespace lld
#endif

View File

@ -44,6 +44,9 @@ def discard_none : Flag<["-"], "discard-none">,
def dynamic_linker : Separate<["--", "-"], "dynamic-linker">,
HelpText<"Which dynamic linker to use">;
def dynamic_list : Separate<["--", "-"], "dynamic-list">,
HelpText<"Read a list of dynamic symbols">;
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_Bstatic_static: Flag<["-"], "static">, Alias<Bstatic>;
def alias_L__library_path : Joined<["--"], "library-path=">, Alias<L>;
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
def alias_dynamic_list: Joined<["--", "-"], "dynamic-list=">, Alias<dynamic_list>;
def alias_entry_e : JoinedOrSeparate<["-"], "e">, Alias<entry>;
def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
def alias_fini_fini : Joined<["-"], "fini=">, Alias<fini>;

View File

@ -339,6 +339,14 @@ template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
Sym->MustBeInDynSym = true;
}
// This function process the dynamic list option by marking all the symbols
// to be exported in the dynamic table.
template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() {
for (StringRef S : Config->DynamicList)
if (SymbolBody *B = find(S))
B->MustBeInDynSym = true;
}
template class elf::SymbolTable<ELF32LE>;
template class elf::SymbolTable<ELF32BE>;
template class elf::SymbolTable<ELF64LE>;

View File

@ -60,6 +60,7 @@ public:
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
void scanShlibUndefined();
void scanDynamicList();
SymbolBody *find(StringRef Name);
void wrap(StringRef Name);
InputFile *findFile(SymbolBody *B);

104
lld/test/ELF/dynamic-list.s Normal file
View File

@ -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

View File

@ -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