summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/CMakeLists.txt1
-rw-r--r--lld/ELF/Config.h1
-rw-r--r--lld/ELF/Driver.cpp32
-rw-r--r--lld/ELF/Driver.h3
-rw-r--r--lld/ELF/DynamicList.cpp64
-rw-r--r--lld/ELF/DynamicList.h24
-rw-r--r--lld/ELF/Options.td4
-rw-r--r--lld/ELF/SymbolTable.cpp8
-rw-r--r--lld/ELF/SymbolTable.h1
-rw-r--r--lld/test/ELF/dynamic-list.s104
-rw-r--r--lld/test/ELF/invalid-dynamic-list.test37
11 files changed, 272 insertions, 7 deletions
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index 86633e0d44d..acf50f5133d 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -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
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 8295e7379be..656ae33d259 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -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;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 95b1c93aca8..b42c884548a 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/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 @@ 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)
diff --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h
index f562b0fa496..cd31f1c914b 100644
--- a/lld/ELF/Driver.h
+++ b/lld/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 @@ 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);
diff --git a/lld/ELF/DynamicList.cpp b/lld/ELF/DynamicList.cpp
new file mode 100644
index 00000000000..0bfdd0f45d1
--- /dev/null
+++ b/lld/ELF/DynamicList.cpp
@@ -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();
+}
diff --git a/lld/ELF/DynamicList.h b/lld/ELF/DynamicList.h
new file mode 100644
index 00000000000..35449f60f6d
--- /dev/null
+++ b/lld/ELF/DynamicList.h
@@ -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
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 4339d77f4cc..aa3a9828f8a 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -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>;
diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp
index 951a29453d4..f7fd8deab2d 100644
--- a/lld/ELF/SymbolTable.cpp
+++ b/lld/ELF/SymbolTable.cpp
@@ -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>;
diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h
index 2cbad46eb93..9309b20fdfc 100644
--- a/lld/ELF/SymbolTable.h
+++ b/lld/ELF/SymbolTable.h
@@ -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);
diff --git a/lld/test/ELF/dynamic-list.s b/lld/test/ELF/dynamic-list.s
new file mode 100644
index 00000000000..87339f9b767
--- /dev/null
+++ b/lld/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
diff --git a/lld/test/ELF/invalid-dynamic-list.test b/lld/test/ELF/invalid-dynamic-list.test
new file mode 100644
index 00000000000..0e7c820f563
--- /dev/null
+++ b/lld/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
OpenPOWER on IntegriCloud