diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2196,16 +2196,19 @@ real->scriptDefined = true; sym->scriptDefined = true; - // Tell LTO not to eliminate these symbols. - sym->isUsedInRegularObj = true; - // If sym is referenced in any object file, bitcode file or shared object, - // retain wrap which is the redirection target of sym. If the object file - // defining sym has sym references, we cannot easily distinguish the case - // from cases where sym is not referenced. Retain wrap because we choose to - // wrap sym references regardless of whether sym is defined + // If a symbol is referenced in any object file, bitcode file or shared + // object, mark its redirection target (foo for __real_foo and __wrap_foo + // for foo) as referenced after redirection, which will be used to tell LTO + // to not eliminate the redirection target. If the object file defining the + // symbol also references it, we cannot easily distinguish the case from + // cases where the symbol is not referenced. Retain the redirection target + // in this case because we choose to wrap symbol references regardless of + // whether the symbol is defined // (https://sourceware.org/bugzilla/show_bug.cgi?id=26358). + if (real->referenced || real->isDefined()) + sym->referencedAfterWrapping = true; if (sym->referenced || sym->isDefined()) - wrap->isUsedInRegularObj = true; + wrap->referencedAfterWrapping = true; } return v; } diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -248,7 +248,9 @@ // 2) Symbols that are used in regular objects. // 3) C named sections if we have corresponding __start_/__stop_ symbol. // 4) Symbols that are defined in bitcode files and used for dynamic linking. + // 5) Symbols that will be referenced after linker wrapping is performed. r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj || + sym->referencedAfterWrapping || (r.Prevailing && sym->includeInDynsym()) || usedStartStop.count(objSym.getSectionName()); // Identify symbols exported dynamically, and that therefore could be diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -39,7 +39,12 @@ idx2 = idx1; idx1 = idx3; - if (!real->isUsedInRegularObj && sym->isUndefined()) + // Propagate symbol usage information to the redirected symbols. + if (sym->isUsedInRegularObj) + wrap->isUsedInRegularObj = true; + if (real->isUsedInRegularObj) + sym->isUsedInRegularObj = true; + else if (sym->isUndefined()) sym->isUsedInRegularObj = false; // Now renaming is complete, and no one refers to real. We drop real from diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -131,6 +131,11 @@ // This is also used to retain __wrap_foo when foo is referenced. uint8_t referenced : 1; + // Used to track if this symbol will be referenced after wrapping is performed + // (i.e. this will be true for foo if __real_foo is referenced, and will be + // true for __wrap_foo if foo is referenced). + uint8_t referencedAfterWrapping : 1; + // True if this symbol is specified by --trace-symbol option. uint8_t traced : 1; @@ -240,12 +245,13 @@ binding(binding), type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3), isPreemptible(false), isUsedInRegularObj(false), used(false), exportDynamic(false), - inDynamicList(false), referenced(false), traced(false), - hasVersionSuffix(false), isInIplt(false), gotInIgot(false), - folded(false), needsTocRestore(false), scriptDefined(false), - needsCopy(false), needsGot(false), needsPlt(false), needsTlsDesc(false), - needsTlsGd(false), needsTlsGdToIe(false), needsGotDtprel(false), - needsTlsIe(false), hasDirectReloc(false) {} + inDynamicList(false), referenced(false), referencedAfterWrapping(false), + traced(false), hasVersionSuffix(false), isInIplt(false), + gotInIgot(false), folded(false), needsTocRestore(false), + scriptDefined(false), needsCopy(false), needsGot(false), + needsPlt(false), needsTlsDesc(false), needsTlsGd(false), + needsTlsGdToIe(false), needsGotDtprel(false), needsTlsIe(false), + hasDirectReloc(false) {} public: // True if this symbol is in the Iplt sub-section of the Plt and the Igot diff --git a/lld/test/ELF/invalid-plt-relocation.test b/lld/test/ELF/invalid-plt-relocation.test deleted file mode 100644 --- a/lld/test/ELF/invalid-plt-relocation.test +++ /dev/null @@ -1,32 +0,0 @@ -# REQUIRES: x86 - -# RUN: rm -rf %t && split-file %s %t -# RUN: llvm-mc -triple x86_64-elf --filetype=obj -o %t/unwind.o %t/unwind.s -# RUN: ld.lld -shared -o %t/libunwind.so -soname libunwind.so %t/unwind.o -# RUN: llvm-as -o %t/resume.bc %t/resume.ll -# RUN: not ld.lld -shared -o %t/libresume.so %t/resume.bc %t/libunwind.so \ -# RUN: --wrap _Unwind_Resume 2>&1 | FileCheck %s - -# CHECK: error: PLT relocation references symbol not present in dynamic symbol table: __wrap__Unwind_Resume - -#--- unwind.s -.globl _Unwind_Resume -.globl __wrap__Unwind_Resume -_Unwind_Resume: -__wrap__Unwind_Resume: - retq - -#--- resume.ll -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" -define dso_local void @_Z1fv() optnone noinline personality i8* bitcast (void ()* @throw to i8*) { - invoke void @throw() - to label %unreachable unwind label %lpad -lpad: - %1 = landingpad { i8*, i32 } - cleanup - resume { i8*, i32 } %1 -unreachable: - unreachable -} -declare void @throw() diff --git a/lld/test/ELF/lto/wrap-unreferenced-before-codegen.test b/lld/test/ELF/lto/wrap-unreferenced-before-codegen.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/wrap-unreferenced-before-codegen.test @@ -0,0 +1,92 @@ +# REQUIRES: x86 +## Verify that we can correctly wrap symbols produced only during LTO codegen +## and unreferenced before then. + +# RUN: rm -rf %t && split-file %s %t +# RUN: llvm-mc -triple x86_64-elf --filetype=obj -o %t/unwind.o %t/unwind.s +# RUN: ld.lld -shared -o %t/libunwind.so -soname libunwind.so %t/unwind.o +# RUN: llvm-as -o %t/resume.bc %t/resume.ll +# RUN: ld.lld -shared -o %t/libresume.so -soname libresume.so %t/resume.bc \ +# RUN: %t/libunwind.so --wrap _Unwind_Resume +# RUN: llvm-objdump --dynamic-reloc --disassemble %t/libresume.so | \ +# RUN: FileCheck --check-prefix=UNWIND-DISASM %s +# RUN: llvm-readelf --dyn-syms %t/libresume.so | \ +# RUN: FileCheck --check-prefix=UNWIND-DYNSYM %s + +# UNWIND-DISASM: [[#%x,RELOC:]] R_X86_64_JUMP_SLOT __wrap__Unwind_Resume +# UNWIND-DISASM-LABEL: <_Z1fv>: +# UNWIND-DISASM: callq{{.*}}<__wrap__Unwind_Resume@plt> +# UNWIND-DISASM-LABEL: <__wrap__Unwind_Resume@plt>: +# UNWIND-DISASM-NEXT: jmpq *[[#]](%rip) # [[#%#x,RELOC]] + +# UNWIND-DYNSYM: Symbol table '.dynsym' contains 5 entries: +# UNWIND-DYNSYM: NOTYPE LOCAL DEFAULT UND +# UNWIND-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND throw +# UNWIND-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND _Unwind_Resume +# UNWIND-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND __wrap__Unwind_Resume +# UNWIND-DYNSYM-NEXT: FUNC GLOBAL DEFAULT 9 _Z1fv + +# RUN: llvm-mc -triple x86_64-elf -filetype=obj -o %t/malloc.o %t/malloc.s +# RUN: ld.lld -shared -o %t/libmalloc.so -soname libmalloc.so %t/malloc.o +# RUN: llvm-mc -triple x86_64-elf -filetype=obj -o %t/emutls.o %t/emutls.s +# RUN: llvm-as -o %t/usetls.bc %t/usetls.ll +# RUN: ld.lld -shared -o %t/libusetls.so %t/usetls.bc %t/libmalloc.so \ +# RUN: --start-lib %t/emutls.o -mllvm -emulated-tls --wrap malloc +# RUN: llvm-objdump --dynamic-reloc --disassemble %t/libusetls.so | \ +# RUN: FileCheck --check-prefix=USETLS-DISASM %s +# RUN: llvm-readelf --dyn-syms %t/libusetls.so | \ +# RUN: FileCheck --check-prefix=USETLS-DYNSYM %s + +# USETLS-DISASM: [[#%x,RELOC:]] R_X86_64_JUMP_SLOT __wrap_malloc +# USETLS-DISASM-LABEL: <__emutls_get_address>: +# USETLS-DISASM-NEXT: jmp{{.*}}<__wrap_malloc@plt> +# USETLS-DISASM-LABEL: <__wrap_malloc@plt>: +# USETLS-DISASM-NEXT: jmpq *[[#]](%rip) # [[#%#x,RELOC]] + +# USETLS-DYNSYM: Symbol table '.dynsym' contains 6 entries: +# USETLS-DYNSYM: NOTYPE LOCAL DEFAULT UND +# USETLS-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND malloc +# USETLS-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND __wrap_malloc +# USETLS-DYNSYM-NEXT: FUNC GLOBAL DEFAULT 6 f +# USETLS-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT 6 __emutls_get_address + +#--- unwind.s +.globl _Unwind_Resume +.globl __wrap__Unwind_Resume +_Unwind_Resume: +__wrap__Unwind_Resume: + retq + +#--- resume.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" +define dso_local void @_Z1fv() optnone noinline personality i8* bitcast (void ()* @throw to i8*) { + invoke void @throw() + to label %unreachable unwind label %lpad +lpad: + %1 = landingpad { i8*, i32 } + cleanup + resume { i8*, i32 } %1 +unreachable: + unreachable +} +declare void @throw() + +#--- malloc.s +.globl malloc +malloc: + retq + +#--- emutls.s +.globl __emutls_get_address +__emutls_get_address: + jmp malloc@plt + +#--- usetls.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" +@x = dso_local thread_local global i32 0, align 4 +define dso_local i32 @f() { + %loaded = load i32, ptr @x, align 4 + ret i32 %loaded +}