[lld] b06426d - [ELF] Add -Bsymbolic-non-weak-functions

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 29 14:46:59 PDT 2021


Author: Fangrui Song
Date: 2021-07-29T14:46:53-07:00
New Revision: b06426da764a8d0254521b33d667db8f26ae5e2f

URL: https://github.com/llvm/llvm-project/commit/b06426da764a8d0254521b33d667db8f26ae5e2f
DIFF: https://github.com/llvm/llvm-project/commit/b06426da764a8d0254521b33d667db8f26ae5e2f.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

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 9144347045b93..a996a815599a4 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 a15959158653b..91e7df21a60ad 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 bedcf43bbe856..f0e4c11b79ebd 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 1039be369d9e4..496be33dd182c 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 3496df1d2814c..187b2ac90c21a 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 442681c0e700f..d21299f7e5dab 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -24,7 +24,8 @@ Non-comprehensive list of changes in this release
 ELF Improvements
 ----------------
 
-* ...
+* ``-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 ba3b0779e6991..bd67e58daa4dd 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 a083d2cebe887..ff182b110d066 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-commits mailing list