[llvm-branch-commits] [lld] ad5a6b1 - [ELF] Add -Bsymbolic-non-weak-functions

Tom Stellard via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jul 29 19:30:48 PDT 2021


Author: Fangrui Song
Date: 2021-07-29T19:29:45-07:00
New Revision: ad5a6b15ff04920a17524dd11adb828240c7103e

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

LOG: [ELF] Add -Bsymbolic-non-weak-functions

This option is a subset of -Bsymbolic-functions. It applies to STB_GLOBAL
STT_FUNC definitions.

The address of a vague linkage function (STB_WEAK STT_FUNC, e.g. an inline
function, a template instantiation) seen by a -Bsymbolic-functions linked
shared object may be different from the address seen from outside the shared
object. Such cases are uncommon. (ELF/Mach-O programs may use
`-fvisibility-inlines-hidden` to break such pointer equality.  On Windows,
correct dllexport and dllimport are needed to make pointer equality work.
Windows link.exe enables /OPT:ICF by default so different inline functions may
have the same address.)

```
// a.cc -> a.o -> a.so (-Bsymbolic-functions)
inline void f() {}
void *g() { return (void *)&f; }

// b.cc -> b.o -> exe
// The address is different!
inline void f() {}
```

-Bsymbolic-non-weak-functions is a safer (C++ conforming) subset of
-Bsymbolic-functions, which can make such programs work.

Implementations usually emit a vague linkage definition in a COMDAT group.  We
could detect the group (with more code) but I feel that we should just check
STB_WEAK for simplicity. A weak definition will thus serve as an escape hatch
for rare cases when users want interposition on definitions.

GNU ld feature request: https://sourceware.org/bugzilla/show_bug.cgi?id=27871

Longer write-up: https://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic

If Linux distributions migrate to protected non-vague-linkage external linkage
functions by default, the linker option can still be handy because it allows
rapid experiment without recompilation. Protected function addresses currently
have deep issues in GNU ld.

Reviewed By: peter.smith

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

(cherry picked from commit b06426da764a8d0254521b33d667db8f26ae5e2f)

Added: 
    

Modified: 
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/Options.td
    lld/ELF/Symbols.cpp
    lld/ELF/SyntheticSections.cpp
    lld/docs/ReleaseNotes.rst
    lld/docs/ld.lld.1
    lld/test/ELF/bsymbolic.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 9144347045b9..a996a815599a 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -38,6 +38,10 @@ enum ELFKind {
   ELF64BEKind
 };
 
+// For -Bno-symbolic, -Bsymbolic-non-weak-functions, -Bsymbolic-functions,
+// -Bsymbolic.
+enum class BsymbolicKind { None, NonWeakFunctions, Functions, All };
+
 // For --build-id.
 enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid };
 
@@ -144,8 +148,7 @@ struct Configuration {
   bool armHasMovtMovw = false;
   bool armJ1J2BranchEncoding = false;
   bool asNeeded = false;
-  bool bsymbolic = false;
-  bool bsymbolicFunctions = false;
+  BsymbolicKind bsymbolic = BsymbolicKind::None;
   bool callGraphProfileSort;
   bool checkSections;
   bool checkDynamicRelocs;

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index a15959158653..91e7df21a60a 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1006,12 +1006,15 @@ static void readConfigs(opt::InputArgList &args) {
                    OPT_no_allow_multiple_definition, false) ||
       hasZOption(args, "muldefs");
   config->auxiliaryList = args::getStrings(args, OPT_auxiliary);
-  if (opt::Arg *arg = args.getLastArg(OPT_Bno_symbolic, OPT_Bsymbolic_functions,
-                                      OPT_Bsymbolic)) {
-    if (arg->getOption().matches(OPT_Bsymbolic_functions))
-      config->bsymbolicFunctions = true;
+  if (opt::Arg *arg =
+          args.getLastArg(OPT_Bno_symbolic, OPT_Bsymbolic_non_weak_functions,
+                          OPT_Bsymbolic_functions, OPT_Bsymbolic)) {
+    if (arg->getOption().matches(OPT_Bsymbolic_non_weak_functions))
+      config->bsymbolic = BsymbolicKind::NonWeakFunctions;
+    else if (arg->getOption().matches(OPT_Bsymbolic_functions))
+      config->bsymbolic = BsymbolicKind::Functions;
     else if (arg->getOption().matches(OPT_Bsymbolic))
-      config->bsymbolic = true;
+      config->bsymbolic = BsymbolicKind::All;
   }
   config->checkSections =
       args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
@@ -1374,7 +1377,8 @@ static void readConfigs(opt::InputArgList &args) {
   // When producing an executable, --dynamic-list specifies non-local defined
   // symbols which are required to be exported. When producing a shared object,
   // symbols not specified by --dynamic-list are non-preemptible.
-  config->symbolic = config->bsymbolic || args.hasArg(OPT_dynamic_list);
+  config->symbolic =
+      config->bsymbolic == BsymbolicKind::All || args.hasArg(OPT_dynamic_list);
   for (auto *arg : args.filtered(OPT_dynamic_list))
     if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
       readDynamicList(*buffer);

diff  --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index bedcf43bbe85..f0e4c11b79eb 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -43,6 +43,9 @@ def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind default visibility defined symbols
 def Bsymbolic_functions: F<"Bsymbolic-functions">,
   HelpText<"Bind default visibility defined function symbols locally for -shared">;
 
+def Bsymbolic_non_weak_functions: F<"Bsymbolic-non-weak-functions">,
+  HelpText<"Bind default visibility defined STB_GLOBAL function symbols locally for -shared">;
+
 def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">;
 
 def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;

diff  --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 1039be369d9e..496be33dd182 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -368,8 +368,12 @@ bool elf::computeIsPreemptible(const Symbol &sym) {
 
   // 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()))
+  // in the dynamic list. -Bsymbolic-non-weak-functions is a non-weak subset of
+  // -Bsymbolic-functions.
+  if (config->symbolic ||
+      (config->bsymbolic == BsymbolicKind::Functions && sym.isFunc()) ||
+      (config->bsymbolic == BsymbolicKind::NonWeakFunctions && sym.isFunc() &&
+       sym.binding != STB_WEAK))
     return sym.inDynamicList;
   return true;
 }

diff  --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 3496df1d2814..187b2ac90c21 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1356,7 +1356,7 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
   // Set DT_FLAGS and DT_FLAGS_1.
   uint32_t dtFlags = 0;
   uint32_t dtFlags1 = 0;
-  if (config->bsymbolic)
+  if (config->bsymbolic == BsymbolicKind::All)
     dtFlags |= DF_SYMBOLIC;
   if (config->zGlobal)
     dtFlags1 |= DF_1_GLOBAL;

diff  --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 40439c995f17..a52ee4348f78 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -30,6 +30,8 @@ ELF Improvements
   (`D102461 <https://reviews.llvm.org/D102461>`_)
 * A new linker script command ``OVERWRITE_SECTIONS`` has been added.
   (`D103303 <https://reviews.llvm.org/D103303>`_)
+* ``-Bsymbolic-non-weak-functions`` has been added as a ``STB_GLOBAL`` subset of ``-Bsymbolic-functions``.
+  (`D102570 <https://reviews.llvm.org/D102570>`_)
 
 Breaking changes
 ----------------

diff  --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index ba3b0779e699..bd67e58daa4d 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -85,6 +85,9 @@ flag.
 .It Fl Bsymbolic-functions
 Bind default visibility defined function symbols locally for
 .Fl shared.
+.It Fl Bsymbolic-non-weak-functions
+Bind default visibility defined STB_GLOBAL function symbols locally for
+.Fl shared.
 .It Fl -build-id Ns = Ns Ar value
 Generate a build ID note.
 .Ar value

diff  --git a/lld/test/ELF/bsymbolic.s b/lld/test/ELF/bsymbolic.s
index a083d2cebe88..ff182b110d06 100644
--- a/lld/test/ELF/bsymbolic.s
+++ b/lld/test/ELF/bsymbolic.s
@@ -6,22 +6,27 @@
 # RUN: llvm-readobj -r %t0.so | FileCheck %s --check-prefix=REL_DEF
 # RUN: llvm-objdump -d %t0.so | FileCheck %s --check-prefix=ASM_DEF
 
+## -Bsymbolic-functions makes all STB_GLOBAL STT_FUNC definitions non-preemptible.
+# RUN: ld.lld -shared -Bsymbolic-non-weak-functions %t/a.o %t/b.o -o %t1.so
+# RUN: llvm-readobj -r %t1.so | FileCheck %s --check-prefix=REL_GFUN
+# RUN: llvm-objdump -d %t1.so | FileCheck %s --check-prefix=ASM_GFUN
+
 ## -Bsymbolic-functions makes all STT_FUNC definitions non-preemptible.
-# RUN: ld.lld -shared -Bsymbolic-functions %t/a.o %t/b.o -o %t1.so
-# RUN: llvm-readobj -r %t1.so | FileCheck %s --check-prefix=REL_FUN
-# RUN: llvm-objdump -d %t1.so | FileCheck %s --check-prefix=ASM_FUN
+# RUN: ld.lld -shared -Bsymbolic-functions %t/a.o %t/b.o -o %t2.so
+# RUN: llvm-readobj -r %t2.so | FileCheck %s --check-prefix=REL_FUN
+# RUN: llvm-objdump -d %t2.so | FileCheck %s --check-prefix=ASM_FUN
 
 ## -Bsymbolic makes all definitions non-preemptible.
-# RUN: ld.lld -shared -Bsymbolic %t/a.o %t/b.o -o %t2.so
-# RUN: llvm-readobj -r %t2.so | FileCheck %s --check-prefix=REL_ALL
-# RUN: llvm-objdump -d %t2.so | FileCheck %s --check-prefix=ASM_ALL
+# RUN: ld.lld -shared -Bsymbolic %t/a.o %t/b.o -o %t3.so
+# RUN: llvm-readobj -r %t3.so | FileCheck %s --check-prefix=REL_ALL
+# RUN: llvm-objdump -d %t3.so | FileCheck %s --check-prefix=ASM_ALL
 
 # RUN: ld.lld -shared -Bsymbolic-functions -Bsymbolic %t/a.o %t/b.o -o %t.so
-# RUN: cmp %t.so %t2.so
+# RUN: cmp %t.so %t3.so
 # RUN: ld.lld -shared -Bsymbolic -Bsymbolic-functions %t/a.o %t/b.o -o %t.so
-# RUN: cmp %t.so %t1.so
-# RUN: ld.lld -shared -Bno-symbolic -Bsymbolic %t/a.o %t/b.o -o %t.so
 # RUN: cmp %t.so %t2.so
+# RUN: ld.lld -shared -Bno-symbolic -Bsymbolic %t/a.o %t/b.o -o %t.so
+# RUN: cmp %t.so %t3.so
 
 ## -Bno-symbolic can cancel previously specified -Bsymbolic and -Bsymbolic-functions.
 # RUN: ld.lld -shared -Bsymbolic -Bno-symbolic %t/a.o %t/b.o -o %t.so
@@ -36,6 +41,7 @@
 # REL_DEF-NEXT: }
 # REL_DEF-NEXT: .rela.plt {
 # REL_DEF-NEXT:   R_X86_64_JUMP_SLOT default
+# REL_DEF-NEXT:   R_X86_64_JUMP_SLOT weak_default
 # REL_DEF-NEXT:   R_X86_64_JUMP_SLOT ext_default
 # REL_DEF-NEXT:   R_X86_64_JUMP_SLOT notype_default
 # REL_DEF-NEXT:   R_X86_64_JUMP_SLOT undef
@@ -45,10 +51,31 @@
 # ASM_DEF-NEXT:   callq {{.*}} <default at plt>
 # ASM_DEF-NEXT:   callq {{.*}} <protected>
 # ASM_DEF-NEXT:   callq {{.*}} <hidden>
+# ASM_DEF-NEXT:   callq {{.*}} <weak_default at plt>
 # ASM_DEF-NEXT:   callq {{.*}} <ext_default at plt>
 # ASM_DEF-NEXT:   callq {{.*}} <notype_default at plt>
 # ASM_DEF-NEXT:   callq {{.*}} <undef at plt>
 
+# REL_GFUN:      .rela.dyn {
+# REL_GFUN-NEXT:   R_X86_64_RELATIVE -
+# REL_GFUN-NEXT:   R_X86_64_RELATIVE -
+# REL_GFUN-NEXT:   R_X86_64_64 data_default
+# REL_GFUN-NEXT: }
+# REL_GFUN-NEXT: .rela.plt {
+# REL_GFUN-NEXT:   R_X86_64_JUMP_SLOT weak_default
+# REL_GFUN-NEXT:   R_X86_64_JUMP_SLOT notype_default
+# REL_GFUN-NEXT:   R_X86_64_JUMP_SLOT undef
+# REL_GFUN-NEXT: }
+
+# ASM_GFUN:      <_start>:
+# ASM_GFUN-NEXT:   callq {{.*}} <default>
+# ASM_GFUN-NEXT:   callq {{.*}} <protected>
+# ASM_GFUN-NEXT:   callq {{.*}} <hidden>
+# ASM_GFUN-NEXT:   callq {{.*}} <weak_default at plt>
+# ASM_GFUN-NEXT:   callq {{.*}} <ext_default>
+# ASM_GFUN-NEXT:   callq {{.*}} <notype_default at plt>
+# ASM_GFUN-NEXT:   callq {{.*}} <undef at plt>
+
 # REL_FUN:      .rela.dyn {
 # REL_FUN-NEXT:   R_X86_64_RELATIVE -
 # REL_FUN-NEXT:   R_X86_64_RELATIVE -
@@ -63,6 +90,7 @@
 # ASM_FUN-NEXT:   callq {{.*}} <default>
 # ASM_FUN-NEXT:   callq {{.*}} <protected>
 # ASM_FUN-NEXT:   callq {{.*}} <hidden>
+# ASM_FUN-NEXT:   callq {{.*}} <weak_default>
 # ASM_FUN-NEXT:   callq {{.*}} <ext_default>
 # ASM_FUN-NEXT:   callq {{.*}} <notype_default at plt>
 # ASM_FUN-NEXT:   callq {{.*}} <undef at plt>
@@ -80,20 +108,24 @@
 # ASM_ALL-NEXT:   callq {{.*}} <default>
 # ASM_ALL-NEXT:   callq {{.*}} <protected>
 # ASM_ALL-NEXT:   callq {{.*}} <hidden>
+# ASM_ALL-NEXT:   callq {{.*}} <weak_default>
 # ASM_ALL-NEXT:   callq {{.*}} <ext_default>
 # ASM_ALL-NEXT:   callq {{.*}} <notype_default>
 # ASM_ALL-NEXT:   callq {{.*}} <undef at plt>
 
 #--- a.s
 .globl default, protected, hidden, notype_default
+.weak weak_default
 .protected protected
 .hidden hidden
 .type default, @function
 .type protected, @function
 .type hidden, @function
+.type weak_default, @function
 default: nop
 protected: nop
 hidden: nop
+weak_default: nop
 notype_default: nop
 
 .globl _start
@@ -102,6 +134,8 @@ _start:
   callq protected at PLT
   callq hidden at PLT
 
+  callq weak_default at PLT
+
   callq ext_default at PLT
 
   callq notype_default at PLT


        


More information about the llvm-branch-commits mailing list