Index: lld/trunk/ELF/Symbols.h =================================================================== --- lld/trunk/ELF/Symbols.h +++ lld/trunk/ELF/Symbols.h @@ -335,8 +335,8 @@ 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 @@ SharedFile &getFile() const { return *cast(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, Index: lld/trunk/ELF/Symbols.cpp =================================================================== --- lld/trunk/ELF/Symbols.cpp +++ lld/trunk/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: lld/trunk/test/ELF/weak-undef-shared.s =================================================================== --- lld/trunk/test/ELF/weak-undef-shared.s +++ lld/trunk/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 - -// 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: Index: lld/trunk/test/ELF/weak-undef-shared2.s =================================================================== --- lld/trunk/test/ELF/weak-undef-shared2.s +++ lld/trunk/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