Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -364,6 +364,10 @@ uint64_t Value; // st_value uint64_t Size; // st_size + + // This is false if no undefined reference has been seen yet. 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, Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -410,13 +410,22 @@ 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(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 @@ uint8_t Bind = Binding; replace(Other); Binding = Bind; + cast(this)->Referenced = true; } } Index: test/ELF/weak-undef-shared.s =================================================================== --- test/ELF/weak-undef-shared.s +++ test/ELF/weak-undef-shared.s @@ -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: Index: test/ELF/weak-undef-shared2.s =================================================================== --- /dev/null +++ test/ELF/weak-undef-shared2.s @@ -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