[lld] 751f18e - [ELF] Refine --export-dynamic-symbol semantics to be compatible GNU ld 2.35

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 11:30:13 PDT 2020


Author: Fangrui Song
Date: 2020-06-01T11:30:03-07:00
New Revision: 751f18e7d46dbb53f62c4c567e331b9bc87febf6

URL: https://github.com/llvm/llvm-project/commit/751f18e7d46dbb53f62c4c567e331b9bc87febf6
DIFF: https://github.com/llvm/llvm-project/commit/751f18e7d46dbb53f62c4c567e331b9bc87febf6.diff

LOG: [ELF] Refine --export-dynamic-symbol semantics to be compatible GNU ld 2.35

GNU ld from binutils 2.35 onwards will likely support
--export-dynamic-symbol but with different semantics.
https://sourceware.org/pipermail/binutils/2020-May/111302.html

Differences:

1. -export-dynamic-symbol is not supported
2. --export-dynamic-symbol takes a glob argument
3. --export-dynamic-symbol can suppress binding the references to the definition within the shared object if (-Bsymbolic or -Bsymbolic-functions)
4. --export-dynamic-symbol does not imply -u

I don't think the first three points can affect any user.
For the fourth point, Not implying -u can lead to some archive members unfetched.
Add -u foo to restore the previous behavior.

Exact semantics:

* -no-pie or -pie: matched non-local defined symbols will be added to the dynamic symbol table.
* -shared: matched non-local STV_DEFAULT symbols will not be bound to definitions within the shared object
  even if they would otherwise be due to -Bsymbolic, -Bsymbolic-functions, or --dynamic-list.

Reviewed By: psmith

Differential Revision: https://reviews.llvm.org/D80487

Added: 
    

Modified: 
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/Options.td
    lld/ELF/ScriptParser.cpp
    lld/ELF/Symbols.cpp
    lld/docs/ReleaseNotes.rst
    lld/docs/ld.lld.1
    lld/test/ELF/export-dynamic-symbol.s
    lld/test/ELF/warn-backrefs.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 7e5ee7acef08..d494ec156823 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -161,7 +161,6 @@ struct Configuration {
   bool gdbIndex;
   bool gnuHash = false;
   bool gnuUnique;
-  bool hasDynamicList = false;
   bool hasDynSymTab;
   bool ignoreDataAddressEquality;
   bool ignoreFunctionAddressEquality;
@@ -192,6 +191,7 @@ struct Configuration {
   llvm::Optional<uint32_t> shuffleSectionSeed;
   bool singleRoRx;
   bool shared;
+  bool symbolic;
   bool isStatic = false;
   bool sysvHash = false;
   bool target1Rel;

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 0019bb8cfdb9..b375ab839c36 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1172,25 +1172,21 @@ static void readConfigs(opt::InputArgList &args) {
       error(arg->getSpelling() + ": " + toString(pat.takeError()));
   }
 
-  // Parses -dynamic-list and -export-dynamic-symbol. They make some
-  // symbols private. Note that -export-dynamic takes precedence over them
-  // as it says all symbols should be exported.
-  if (!config->exportDynamic) {
-    for (auto *arg : args.filtered(OPT_dynamic_list))
-      if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
-        readDynamicList(*buffer);
-
-    for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
-      config->dynamicList.push_back(
-          {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/false});
-  }
+  // When producing an executable, --dynamic-list specifies non-local defined
+  // symbols whith are required to be exported. When producing a shared object,
+  // symbols not specified by --dynamic-list are non-preemptible.
+  config->symbolic =
+      args.hasArg(OPT_Bsymbolic) || args.hasArg(OPT_dynamic_list);
+  for (auto *arg : args.filtered(OPT_dynamic_list))
+    if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+      readDynamicList(*buffer);
 
-  // If --export-dynamic-symbol=foo is given and symbol foo is defined in
-  // an object file in an archive file, that object file should be pulled
-  // out and linked. (It doesn't have to behave like that from technical
-  // point of view, but this is needed for compatibility with GNU.)
+  // --export-dynamic-symbol specifies additional --dynamic-list symbols if any
+  // other option expresses a symbolic intention: -no-pie, -pie, -Bsymbolic,
+  // -Bsymbolic-functions (if STT_FUNC), --dynamic-list.
   for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
-    config->undefined.push_back(arg->getValue());
+    config->dynamicList.push_back(
+        {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/true});
 
   for (auto *arg : args.filtered(OPT_version_script))
     if (Optional<std::string> path = searchScript(arg->getValue())) {

diff  --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index a6ca64242b4e..939af8f510a7 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -145,7 +145,12 @@ def discard_none: F<"discard-none">,
 
 defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">;
 
-defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">;
+defm dynamic_list : Eq<"dynamic-list",
+   "Read a list of dynamic symbols. (executable) Put matched non-local defined"
+   "symbols to the dynamic symbol table. (shared object) References to matched"
+   "non-local STV_DEFAULT symbols shouldn't be bound to definitions within the "
+   "shared object. Implies -Bsymbolic but does not set DF_SYMBOLIC">,
+   MetaVarName<"<file>">;
 
 defm eh_frame_hdr: B<"eh-frame-hdr",
     "Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header",
@@ -181,8 +186,12 @@ defm export_dynamic: B<"export-dynamic",
     "Put symbols in the dynamic symbol table",
     "Do not put symbols in the dynamic symbol table (default)">;
 
-defm export_dynamic_symbol:
-  Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">;
+defm export_dynamic_symbol : EEq<"export-dynamic-symbol",
+    "(executable) Put matched symbols in the dynamic symbol table. "
+    "(shared object) References to matched non-local STV_DEFAULT symbols "
+    "shouldn't be bound to definitions within the shared object. "
+    "Does not imply -Bsymbolic.">,
+    MetaVarName<"glob">;
 
 defm fatal_warnings: B<"fatal-warnings",
     "Treat warnings as errors",

diff  --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index d8375bc931d6..d23a46d250e4 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -175,7 +175,6 @@ static ExprValue bitOr(ExprValue a, ExprValue b) {
 }
 
 void ScriptParser::readDynamicList() {
-  config->hasDynamicList = true;
   expect("{");
   std::vector<SymbolVersion> locals;
   std::vector<SymbolVersion> globals;

diff  --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index ff1e600fa2d5..8f2f55418df5 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -365,13 +365,11 @@ bool elf::computeIsPreemptible(const Symbol &sym) {
   if (!config->shared)
     return false;
 
-  // If the dynamic list is present, it specifies preemptable symbols in a DSO.
-  if (config->hasDynamicList)
+  // If -Bsymbolic or --dynamic-list is specified, or -Bsymbolic-functions is
+  // specified and the symbol is STT_FUNC, the symbol is preemptible iff it is
+  // in the dynamic list.
+  if (config->symbolic || (config->bsymbolicFunctions && sym.isFunc()))
     return sym.inDynamicList;
-
-  // -Bsymbolic means that definitions are not preempted.
-  if (config->bsymbolic || (config->bsymbolicFunctions && sym.isFunc()))
-    return false;
   return true;
 }
 

diff  --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 99769560ae51..fe3de8306cd8 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -35,6 +35,7 @@ Breaking changes
 
 * One-dash form of some long option (``--thinlto-*``, ``--lto-*``, ``--shuffle-sections=``)
   are no longer supported.
+* ``--export-dynamic-symbol`` no longer implies ``-u``.
 
 COFF Improvements
 -----------------

diff  --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index 781bff1e970c..0522feb145f1 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -157,6 +157,10 @@ This is recorded in an ELF segment of type
 .It Fl -dynamic-list Ns = Ns Ar file
 Read a list of dynamic symbols from
 .Ar file .
+(executable) Put matched non-local defined symbols to the dynamic symbol table.
+(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object. Implies
+.Cm -Bsymbolic
+but does not set DF_SYMBOLIC
 .It Fl -eh-frame-hdr
 Request creation of
 .Li .eh_frame_hdr
@@ -184,10 +188,14 @@ This option is currently only supported on AArch64.
 Exclude static libraries from automatic export.
 .It Fl -export-dynamic , Fl E
 Put symbols in the dynamic symbol table.
-.It Fl -export-dynamic-symbol Ns = Ns Ar symbol
-Include
-.Ar symbol
-in the dynamic symbol table.
+.It Fl -export-dynamic-symbol Ns = Ns Ar glob
+(executable) Put matched non-local defined symbols to the dynamic symbol table.
+(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object even if they would otherwise be due to
+.Cm -Bsymbolic
+,
+.Cm -Bsymbolic-functions
+or
+.Cm --dynamic-list
 .It Fl -fatal-warnings
 Treat warnings as errors.
 .It Fl -filter Ns = Ns Ar value , Fl F Ar value

diff  --git a/lld/test/ELF/export-dynamic-symbol.s b/lld/test/ELF/export-dynamic-symbol.s
index 22536035424c..886a4cd59595 100644
--- a/lld/test/ELF/export-dynamic-symbol.s
+++ b/lld/test/ELF/export-dynamic-symbol.s
@@ -1,18 +1,54 @@
 # REQUIRES: x86
 
-# RUN: rm -f %t.a
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/archive2.s -o %t1.o
-# RUN: llvm-ar rcs %t.a %t1.o
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
 
-# RUN: ld.lld -shared -o %t.so --export-dynamic-symbol foo %t.a %t2.o
-# RUN: llvm-readelf -dyn-symbols %t.so | FileCheck %s
+## For an executable, --export-dynamic-symbol exports a symbol if it is non-local and defined.
+# RUN: ld.lld -pie --export-dynamic-symbol foo %t.o -o %t
+# RUN: llvm-nm -D -p %t | FileCheck %s
 
-# RUN: ld.lld -shared -o %t.so --export-dynamic --export-dynamic-symbol foo %t.a %t2.o
-# RUN: llvm-readelf -dyn-symbols %t.so | FileCheck %s
+## --export-dynamic exports all non-local defined symbols.
+## --export-dynamic-symbol is shadowed.
+# RUN: ld.lld -pie --export-dynamic --export-dynamic-symbol foo %t.o -o %t.start
+# RUN: llvm-nm -D -p %t.start | FileCheck --check-prefixes=CHECK,START %s
 
-# CHECK: foo
+# CHECK-NOT:  .
+# START:      T _start
+# CHECK:      T foo
+# CHECK-NOT:  .
 
-.global _start
+## --export-dynamic-symbol does not imply -u: %t1.a(%t1.o) is not fetched.
+## This is compatible with GNU ld since binutils 2.35 onwards.
+# RUN: echo '.globl foo, bar; foo: bar:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
+# RUN: rm -f %t1.a && llvm-ar rc %t1.a %t1.o
+# RUN: ld.lld --export-dynamic-symbol bar %t1.a %t.o -o %t.nofetch
+# RUN: llvm-nm %t.nofetch | FileCheck /dev/null --implicit-check-not=bar
+
+## --export-dynamic-symbol can make a symbol preemptible even if it would be otherwise
+## non-preemptible (due to -Bsymbolic, -Bsymbolic-functions or --dynamic-list).
+# RUN: ld.lld -shared -Bsymbolic --export-dynamic-symbol nomatch %t.o -o %t.nopreempt
+# RUN: llvm-objdump -d %t.nopreempt | FileCheck --check-prefix=NOPLT %s
+# RUN: ld.lld -shared -Bsymbolic --export-dynamic-symbol foo %t.o -o %t.preempt
+# RUN: llvm-objdump -d %t.preempt | FileCheck --check-prefix=PLT %s
+
+## 'nomatch' does not match any symbol. Don't warn.
+# RUN: ld.lld --fatal-warnings -shared -Bsymbolic-functions --export-dynamic-symbol nomatch %t.o -o %t.nopreempt2
+# RUN: llvm-objdump -d %t.nopreempt2 | FileCheck --check-prefix=NOPLT %s
+# RUN: ld.lld -shared -Bsymbolic-functions --export-dynamic-symbol foo %t.o -o %t.preempt2
+# RUN: llvm-objdump -d %t.preempt2 | FileCheck --check-prefix=PLT %s
+
+# RUN: echo '{};' > %t.list
+# RUN: ld.lld -shared --dynamic-list %t.list --export-dynamic-symbol foo %t.o -o %t.preempt3
+# RUN: llvm-objdump -d %t.preempt3 | FileCheck --check-prefix=PLT %s
+
+## The option value is a glob.
+# RUN: ld.lld -shared -Bsymbolic --export-dynamic-symbol 'f*' %t.o -o %t.preempt4
+# RUN: llvm-objdump -d %t.preempt4 | FileCheck --check-prefix=PLT %s
+
+# PLT:       <foo at plt>
+# NOPLT-NOT: <foo at plt>
+
+.global _start, foo
+.type foo, @function
 _start:
-  nop
+  call foo
+foo:

diff  --git a/lld/test/ELF/warn-backrefs.s b/lld/test/ELF/warn-backrefs.s
index 6308c57e6dc7..34d105d3036f 100644
--- a/lld/test/ELF/warn-backrefs.s
+++ b/lld/test/ELF/warn-backrefs.s
@@ -75,8 +75,8 @@
 ## In GNU linkers, -u does not make a backward reference.
 # RUN: ld.lld --fatal-warnings --warn-backrefs -u foo %t2.a %t1.o -o /dev/null
 
-## In GNU gold, --export-dynamic-symbol does not make a backward reference.
-# RUN: ld.lld --fatal-warnings --warn-backrefs --export-dynamic-symbol foo %t2.a %t1.o -o /dev/null
+## -u does not make a backward reference.
+# RUN: ld.lld --fatal-warnings --warn-backrefs -u foo %t2.a %t1.o -o /dev/null
 
 # RUN: not ld.lld --warn-backrefs-exclude='[' 2>&1 | FileCheck --check-prefix=INVALID %s
 # INVALID: error: --warn-backrefs-exclude: invalid glob pattern: [


        


More information about the llvm-commits mailing list