[lld] r364913 - [ELF] Only allow the binding of SharedSymbol to change for the first undef ref

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 2 04:37:21 PDT 2019


Author: maskray
Date: Tue Jul  2 04:37:21 2019
New Revision: 364913

URL: http://llvm.org/viewvc/llvm-project?rev=364913&view=rev
Log:
[ELF] Only allow the binding of SharedSymbol to change for the first undef ref

Fixes PR42442

t.o has a STB_GLOBAL undef ref to f
t2.so has a STB_WEAK undef ref to f
t1.so defines f

ld.lld t.o t1.so t2.so currently sets the binding of `f` to STB_WEAK.
This is not correct because there exists a STB_GLOBAL undef ref from a
regular object. The problem is that resolveUndefined() doesn't check
if the undef ref is seen for the first time:

    if (isShared() || isLazy() || (isUndefined() && Other.Binding != STB_WEAK))
      Binding = Other.Binding;

The isShared() condition should be `isShared() && !Referenced`
where Referenced is set to true after an undef ref is seen.

In practice, when linking a pthread program with glibc:

    // a.o
    #include <pthread.h>
    pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
    int main() { pthread_mutex_unlock(&mu); }

{clang,gcc} -fuse-ld=lld a.o -lpthread # libpthread.so is linked before libgcc_s.so.1

The weak undef pthread_mutex_unlock in libgcc_s.so.1 makes the result
weak, which diverges from GNU linkers where STB_DEFAULT is used:

    23: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND pthread_mutex_lock

(Note, if -pthread is used instead, libpthread.so will be linked **after**
libgcc_s.so.1 . lld sets the binding to the expected STB_GLOBAL)

Similar linking sequences (ld.lld t.o t1.so t2.so) appear to be used by
Go, which cause a build error https://github.com/golang/go/issues/31912.

Reviewed By: grimar, ruiu

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

Added:
    lld/trunk/test/ELF/weak-undef-shared2.s
Modified:
    lld/trunk/ELF/Symbols.cpp
    lld/trunk/ELF/Symbols.h
    lld/trunk/test/ELF/weak-undef-shared.s

Modified: lld/trunk/ELF/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.cpp?rev=364913&r1=364912&r2=364913&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.cpp (original)
+++ lld/trunk/ELF/Symbols.cpp Tue Jul  2 04:37:21 2019
@@ -410,13 +410,22 @@ void Symbol::resolveUndefined(const Unde
   if (Traced)
     printTraceSymbol(&Other);
 
-  if (isShared() || isLazy() || (isUndefined() && Other.Binding != STB_WEAK))
-    Binding = Other.Binding;
-
-  if (isLazy()) {
+  if (isUndefined()) {
+    // The binding may "upgrade" from weak to non-weak.
+    if (Other.Binding != STB_WEAK)
+      Binding = Other.Binding;
+  } else if (auto *S = dyn_cast<SharedSymbol>(this)) {
+    // The binding of a SharedSymbol will be weak if there is at least one
+    // reference and all are weak. The binding has one opportunity to change to
+    // weak: if the first reference is weak.
+    if (Other.Binding != STB_WEAK || !S->Referenced)
+      Binding = Other.Binding;
+    S->Referenced = true;
+  } else if (isLazy()) {
     // An undefined weak will not fetch archive members. See comment on Lazy in
     // Symbols.h for the details.
     if (Other.Binding == STB_WEAK) {
+      Binding = STB_WEAK;
       Type = Other.Type;
       return;
     }
@@ -635,5 +644,6 @@ void Symbol::resolveShared(const SharedS
     uint8_t Bind = Binding;
     replace(Other);
     Binding = Bind;
+    cast<SharedSymbol>(this)->Referenced = true;
   }
 }

Modified: lld/trunk/ELF/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.h?rev=364913&r1=364912&r2=364913&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.h (original)
+++ lld/trunk/ELF/Symbols.h Tue Jul  2 04:37:21 2019
@@ -335,8 +335,8 @@ public:
   SharedSymbol(InputFile &File, StringRef Name, uint8_t Binding,
                uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size,
                uint32_t Alignment, uint32_t VerdefIndex)
-      : Symbol(SharedKind, &File, Name, Binding, StOther, Type),
-        Alignment(Alignment), Value(Value), Size(Size) {
+      : Symbol(SharedKind, &File, Name, Binding, StOther, Type), Value(Value),
+        Size(Size), Alignment(Alignment) {
     this->VerdefIndex = VerdefIndex;
     // GNU ifunc is a mechanism to allow user-supplied functions to
     // resolve PLT slot values at load-time. This is contrary to the
@@ -360,10 +360,14 @@ public:
 
   SharedFile &getFile() const { return *cast<SharedFile>(File); }
 
-  uint32_t Alignment;
-
   uint64_t Value; // st_value
   uint64_t Size;  // st_size
+  uint32_t Alignment;
+
+  // This is true if there has been at least one undefined reference to the
+  // symbol. The binding may change to STB_WEAK if the first undefined reference
+  // is weak.
+  bool Referenced = false;
 };
 
 // LazyArchive and LazyObject represent a symbols that is not yet in the link,

Modified: lld/trunk/test/ELF/weak-undef-shared.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/weak-undef-shared.s?rev=364913&r1=364912&r2=364913&view=diff
==============================================================================
--- lld/trunk/test/ELF/weak-undef-shared.s (original)
+++ lld/trunk/test/ELF/weak-undef-shared.s Tue Jul  2 04:37:21 2019
@@ -1,19 +1,31 @@
-// REQUIRES: x86
-// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
-// RUN: llvm-mc %p/Inputs/shared.s -o %t2.o -filetype=obj -triple=x86_64-pc-linux
-// RUN: ld.lld %t2.o -o %t2.so -shared
-// RUN: ld.lld %t.o %t2.so -o %t.exe
-// RUN: llvm-readobj --symbols %t.exe | FileCheck %s
-
-// CHECK:      Name: bar
-// CHECK-NEXT: Value: 0x201020
-// CHECK-NEXT: Size: 0
-// CHECK-NEXT: Binding: Weak
-// CHECK-NEXT: Type: Function
-// CHECK-NEXT: Other: 0
-// CHECK-NEXT: Section: Undefined
-
-.global _start
-_start:
-        .weak bar
-        .quad bar
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+
+# RUN: echo '.data; .weak foo; .quad foo' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
+# RUN: echo '.data; .quad foo' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
+
+## If the first undefined reference is weak, the binding changes to
+## STB_WEAK.
+# RUN: ld.lld %t1.o %t.so -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
+# RUN: ld.lld %t.so %t1.o -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
+
+## The binding remains STB_WEAK if there is no STB_GLOBAL undefined reference.
+# RUN: ld.lld %t1.o %t.so %t1.o -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
+# RUN: ld.lld %t.so %t1.o %t1.o -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
+
+## The binding changes back to STB_GLOBAL if there is a STB_GLOBAL undefined reference.
+# RUN: ld.lld %t1.o %t.so %t2.o -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=GLOBAL %s
+# RUN: ld.lld %t2.o %t.so %t1.o -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=GLOBAL %s
+
+# WEAK:   NOTYPE WEAK   DEFAULT UND foo
+# GLOBAL: NOTYPE GLOBAL DEFAULT UND foo
+
+.globl foo
+foo:

Added: lld/trunk/test/ELF/weak-undef-shared2.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/weak-undef-shared2.s?rev=364913&view=auto
==============================================================================
--- lld/trunk/test/ELF/weak-undef-shared2.s (added)
+++ lld/trunk/test/ELF/weak-undef-shared2.s Tue Jul  2 04:37:21 2019
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: echo '.globl f; f:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
+# RUN: echo '.weak f; .data; .quad f' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
+# RUN: ld.lld -shared %t1.o -o %t1.so
+# RUN: ld.lld -shared %t2.o -o %t2.so
+
+## The undefined reference is STB_GLOBAL in %t.o while STB_WEAK in %t2.so.
+## Check the binding of the result is STB_GLOBAL.
+
+# RUN: ld.lld %t.o %t1.so %t2.so -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck %s
+# RUN: ld.lld %t1.so %t.o %t2.so -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck %s
+# RUN: ld.lld %t1.so %t2.so %t.o -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck %s
+
+# CHECK: NOTYPE GLOBAL DEFAULT UND f
+
+.data
+.quad f




More information about the llvm-commits mailing list