Skip to content

Commit

Permalink
[ELF] Only allow the binding of SharedSymbol to change for the first …
Browse files Browse the repository at this point in the history
…undef ref

Summary:
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() && !BindingFinalized`
where BindingFinalized 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 golang/go#31912.

Reviewers: brooksmoses, grimar, ruiu, espindola

Subscribers: jfb, emaste, arichardson, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D63974
  • Loading branch information
MaskRay committed Jun 29, 2019
1 parent d9c1b45 commit e3bbe06
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 18 deletions.
8 changes: 7 additions & 1 deletion ELF/Symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,13 @@ void Symbol::resolveUndefined(const Undefined &Other) {
if (Traced)
printTraceSymbol(&Other);

if (isShared() || isLazy() || (isUndefined() && Other.Binding != STB_WEAK))
if (auto *S = dyn_cast<SharedSymbol>(this)) {
if (Other.Binding != STB_WEAK || !S->BindingFinalized)
Binding = Other.Binding;
S->BindingFinalized = true;
} else if (isLazy() || (isUndefined() && Other.Binding != STB_WEAK)) {
Binding = Other.Binding;
}

if (isLazy()) {
// An undefined weak will not fetch archive members. See comment on Lazy in
Expand Down Expand Up @@ -635,5 +640,6 @@ void Symbol::resolveShared(const SharedSymbol &Other) {
uint8_t Bind = Binding;
replace(Other);
Binding = Bind;
cast<SharedSymbol>(this)->BindingFinalized = true;
}
}
4 changes: 4 additions & 0 deletions ELF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ class SharedSymbol : public Symbol {

uint64_t Value; // st_value
uint64_t Size; // st_size

// This is true if no undefined reference has been seen yet. The binding may
// change to STB_WEAK if the first undefined reference is weak.
bool BindingFinalized = false;
};

// LazyArchive and LazyObject represent a symbols that is not yet in the link,
Expand Down
46 changes: 29 additions & 17 deletions test/ELF/weak-undef-shared.s
Original file line number Diff line number Diff line change
@@ -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
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
# RUN: ld.lld -shared %t.o -o %t.so

// 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
# 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

.global _start
_start:
.weak bar
.quad bar
## 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:
21 changes: 21 additions & 0 deletions test/ELF/weak-undef-shared2.s
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit e3bbe06

Please sign in to comment.