Skip to content

Commit cd976fe

Browse files
committed
[ELF] Only allow the binding of SharedSymbol to change for the first 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, peter.smith, ruiu, espindola Subscribers: jfb, emaste, arichardson, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D63974
1 parent 114ca00 commit cd976fe

File tree

4 files changed

+69
-22
lines changed

4 files changed

+69
-22
lines changed

ELF/Symbols.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,13 +410,22 @@ void Symbol::resolveUndefined(const Undefined &Other) {
410410
if (Traced)
411411
printTraceSymbol(&Other);
412412

413-
if (isShared() || isLazy() || (isUndefined() && Other.Binding != STB_WEAK))
414-
Binding = Other.Binding;
415-
416-
if (isLazy()) {
413+
if (isUndefined()) {
414+
// The binding may "upgrade" when a non-weak reference is seen later.
415+
if (Other.Binding != STB_WEAK)
416+
Binding = Other.Binding;
417+
} else if (auto *S = dyn_cast<SharedSymbol>(this)) {
418+
// The binding of a SharedSymbol will be weak if there is at least one
419+
// reference and all are weak. The binding has one opportunity to change to
420+
// weak: if the first reference is weak.
421+
if (Other.Binding != STB_WEAK || !S->BindingFinalized)
422+
Binding = Other.Binding;
423+
S->BindingFinalized = true;
424+
} else if (isLazy()) {
417425
// An undefined weak will not fetch archive members. See comment on Lazy in
418426
// Symbols.h for the details.
419-
if (Other.Binding == STB_WEAK) {
427+
Binding = Other.Binding;
428+
if (Binding == STB_WEAK) {
420429
Type = Other.Type;
421430
return;
422431
}
@@ -635,5 +644,6 @@ void Symbol::resolveShared(const SharedSymbol &Other) {
635644
uint8_t Bind = Binding;
636645
replace(Other);
637646
Binding = Bind;
647+
cast<SharedSymbol>(this)->BindingFinalized = true;
638648
}
639649
}

ELF/Symbols.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@ class SharedSymbol : public Symbol {
364364

365365
uint64_t Value; // st_value
366366
uint64_t Size; // st_size
367+
368+
// This is true if no undefined reference has been seen yet. The binding may
369+
// change to STB_WEAK if the first undefined reference is weak.
370+
bool BindingFinalized = false;
367371
};
368372

369373
// LazyArchive and LazyObject represent a symbols that is not yet in the link,

test/ELF/weak-undef-shared.s

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
1-
// REQUIRES: x86
2-
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
3-
// RUN: llvm-mc %p/Inputs/shared.s -o %t2.o -filetype=obj -triple=x86_64-pc-linux
4-
// RUN: ld.lld %t2.o -o %t2.so -shared
5-
// RUN: ld.lld %t.o %t2.so -o %t.exe
6-
// RUN: llvm-readobj --symbols %t.exe | FileCheck %s
1+
# REQUIRES: x86
2+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
3+
# RUN: ld.lld -shared %t.o -o %t.so
74

8-
// CHECK: Name: bar
9-
// CHECK-NEXT: Value: 0x201020
10-
// CHECK-NEXT: Size: 0
11-
// CHECK-NEXT: Binding: Weak
12-
// CHECK-NEXT: Type: Function
13-
// CHECK-NEXT: Other: 0
14-
// CHECK-NEXT: Section: Undefined
5+
# RUN: echo '.data; .weak foo; .quad foo' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
6+
# RUN: echo '.data; .quad foo' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
157

16-
.global _start
17-
_start:
18-
.weak bar
19-
.quad bar
8+
## If the first undefined reference is weak, the binding changes to
9+
## STB_WEAK.
10+
# RUN: ld.lld %t1.o %t.so -o %t
11+
# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
12+
# RUN: ld.lld %t.so %t1.o -o %t
13+
# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
14+
15+
## The binding remains STB_WEAK if there is no STB_GLOBAL undefined reference.
16+
# RUN: ld.lld %t1.o %t.so %t1.o -o %t
17+
# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
18+
# RUN: ld.lld %t.so %t1.o %t1.o -o %t
19+
# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=WEAK %s
20+
21+
## The binding changes back to STB_GLOBAL if there is a STB_GLOBAL undefined reference.
22+
# RUN: ld.lld %t1.o %t.so %t2.o -o %t
23+
# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=GLOBAL %s
24+
# RUN: ld.lld %t2.o %t.so %t1.o -o %t
25+
# RUN: llvm-readelf --dyn-syms %t | FileCheck --check-prefix=GLOBAL %s
26+
27+
# WEAK: NOTYPE WEAK DEFAULT UND foo
28+
# GLOBAL: NOTYPE GLOBAL DEFAULT UND foo
29+
30+
.globl foo
31+
foo:

test/ELF/weak-undef-shared2.s

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# REQUIRES: x86
2+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
3+
# RUN: echo '.globl f; f:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
4+
# RUN: echo '.weak f; .data; .quad f' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
5+
# RUN: ld.lld -shared %t1.o -o %t1.so
6+
# RUN: ld.lld -shared %t2.o -o %t2.so
7+
8+
## The undefined reference is STB_GLOBAL in %t.o while STB_WEAK in %t2.so.
9+
## Check the binding of the result is STB_GLOBAL.
10+
11+
# RUN: ld.lld %t.o %t1.so %t2.so -o %t
12+
# RUN: llvm-readelf --dyn-syms %t | FileCheck %s
13+
# RUN: ld.lld %t1.so %t.o %t2.so -o %t
14+
# RUN: llvm-readelf --dyn-syms %t | FileCheck %s
15+
# RUN: ld.lld %t1.so %t2.so %t.o -o %t
16+
# RUN: llvm-readelf --dyn-syms %t | FileCheck %s
17+
18+
# CHECK: NOTYPE GLOBAL DEFAULT UND f
19+
20+
.data
21+
.quad f

0 commit comments

Comments
 (0)