forked from OSchip/llvm-project
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:
parent
97b0663492
commit
9df0720766
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue