Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -98,6 +98,10 @@ std::vector VersionScriptGlobals; std::vector VersionScriptLocals; std::vector BuildIdVector; + + // Mapping of renamed symbols to their original pre-LTO bindings. + llvm::DenseMap RenamedSymbols; + bool AllowMultipleDefinition; bool AsNeeded = false; bool Bsymbolic; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -969,6 +969,14 @@ Symtab.scanShlibUndefined(); Symtab.scanVersionScript(); + // Pass wrapped symbols to LTO + for (auto *Arg : Args.filtered(OPT_wrap)) + Symtab.addLTOSymbolWrap(Arg->getValue()); + + // Pass alias symbols to LTO + for (std::pair &Def : getDefsym(Args)) + Symtab.addLTOSymbolAlias(Def.first, Def.second); + Symtab.addCombinedLTOObject(); if (ErrorCount) return; Index: lld/ELF/LTO.cpp =================================================================== --- lld/ELF/LTO.cpp +++ lld/ELF/LTO.cpp @@ -136,6 +136,8 @@ Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym()); if (R.Prevailing) undefine(Sym); + R.LinkerRedefined = Config->RenamedSymbols.find(Sym) != + Config->RenamedSymbols.end(); } checkError(LTOObj->add(std::move(F.Obj), Resols)); } Index: lld/ELF/SymbolTable.h =================================================================== --- lld/ELF/SymbolTable.h +++ lld/ELF/SymbolTable.h @@ -39,6 +39,8 @@ public: void addFile(InputFile *File); void addCombinedLTOObject(); + void addLTOSymbolWrap(StringRef Name); + void addLTOSymbolAlias(StringRef Alias, StringRef Name); ArrayRef getSymbols() const { return SymVector; } ArrayRef *> getObjectFiles() const { return ObjectFiles; } Index: lld/ELF/SymbolTable.cpp =================================================================== --- lld/ELF/SymbolTable.cpp +++ lld/ELF/SymbolTable.cpp @@ -126,6 +126,12 @@ Obj->parse(DummyGroups); ObjectFiles.push_back(Obj); } + + // Restore bindings of renamed symbols. + for (auto &RSI : Config->RenamedSymbols) { + Symbol *Sym = RSI.first; + Sym->Binding = RSI.second; + } } template @@ -154,15 +160,55 @@ Symtab.insert({CachedHashStringRef(Name), {-1, true}}); } +// Get pre-LTO symbol binding for -wrap and -defsym symbols. +// If it's in the symbol table, read it from the symbol. +// Otherwise an undef for it will be created with STB_GLOBAL. +static uint8_t getSymbolBinding(SymbolBody *SB) { + uint8_t Binding = STB_GLOBAL; + if (SB) { + Symbol *S = SB->symbol(); + Binding = S->Binding; + } + return Binding; +} + +template void SymbolTable::addLTOSymbolWrap(StringRef Name) { + SymbolBody *SB = find(Name); + if (!SB) + return; + Symbol *Sym = SB->symbol(); + Symbol *Real = addUndefined(Saver.save("__real_" + Name)); + Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name)); + + Config->RenamedSymbols[Sym] = getSymbolBinding(SB); + Config->RenamedSymbols[Real] = getSymbolBinding(Real->body()); + Config->RenamedSymbols[Wrap] = getSymbolBinding(Wrap->body()); +} + +template void SymbolTable::addLTOSymbolAlias(StringRef Alias, + StringRef Name) { + SymbolBody *SB = find(Name); + if (!SB) { + error("-defsym: undefined symbol: " + Name); + return; + } + Symbol *Sym = SB->symbol(); + Symbol *AliasSym = addUndefined(Alias); + Config->RenamedSymbols[Sym] = getSymbolBinding(SB); + Config->RenamedSymbols[AliasSym] = getSymbolBinding(AliasSym->body()); +} + // Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM. // Used to implement --wrap. template void SymbolTable::wrap(StringRef Name) { - SymbolBody *B = find(Name); - if (!B) + SymbolBody *SB = find(Name); + if (!SB) return; - Symbol *Sym = B->symbol(); - Symbol *Real = addUndefined(Saver.save("__real_" + Name)); - Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name)); + Symbol *Sym = SB->symbol(); + SymbolBody *RB = find("__real_" + Name.str()); + SymbolBody *WB = find("__wrap_" + Name.str()); + Symbol *Real = RB->symbol(); + Symbol *Wrap = WB->symbol(); // We rename symbols by replacing the old symbol's SymbolBody with the new // symbol's SymbolBody. This causes all SymbolBody pointers referring to the @@ -175,12 +221,12 @@ template void SymbolTable::alias(StringRef Alias, StringRef Name) { SymbolBody *B = find(Name); - if (!B) { - error("-defsym: undefined symbol: " + Name); + if (!B) return; - } Symbol *Sym = B->symbol(); - Symbol *AliasSym = addUndefined(Alias); + + SymbolBody *AB = find(Alias); + Symbol *AliasSym = AB->symbol(); memcpy(AliasSym->Body.buffer, Sym->Body.buffer, sizeof(AliasSym->Body)); } Index: lld/test/ELF/lto/Inputs/defsym-bar.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/Inputs/defsym-bar.ll @@ -0,0 +1,14 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define hidden void @bar1() { + ret void +} + +define hidden void @bar2() { + ret void +} + +define hidden void @bar3() { + ret void +} Index: lld/test/ELF/lto/Inputs/wrap-bar.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/Inputs/wrap-bar.ll @@ -0,0 +1,14 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define hidden void @bar() { + ret void +} + +define hidden void @__real_bar() { + ret void +} + +define hidden void @__wrap_bar() { + ret void +} Index: lld/test/ELF/lto/defsym.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/defsym.ll @@ -0,0 +1,27 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: llvm-as %S/Inputs/defsym-bar.ll -o %t1.o +; RUN: ld.lld %t.o %t1.o -shared -o %t.so -defsym=bar2=bar3 +; RUN: llvm-objdump -d %t.so | FileCheck %s + +; Call to bar2() should not be inlined and should be routed to bar3() +; Symbol bar3 should not be eliminated + +; CHECK: foo: +; CHECK-NEXT: pushq %rax +; CHECK-NEXT: callq{{.*}} +; CHECK-NEXT: callq{{.*}} + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @bar1() +declare void @bar2() +declare void @bar3() + +define void @foo() { + call void @bar1() + call void @bar2() + call void @bar3() + ret void +} Index: lld/test/ELF/lto/wrap-1.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/wrap-1.ll @@ -0,0 +1,30 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: ld.lld %t.o -o %t.out -wrap=bar -save-temps +; RUN: llvm-readobj -t %t.out | FileCheck %s +; RUN: cat %t.out.resolution.txt | FileCheck -check-prefix=RESOLS %s + +; CHECK: Name: __wrap_bar +; CHECK-NEXT: Value: +; CHECK-NEXT: Size: +; CHECK-NEXT: Binding: Global +; CHECK-NEXT: Type: Function + +; Make sure that the 'r' (linker redefined) bit is set for bar and __wrap_bar +; in the resolutions file. +; RESOLS: ,bar,r +; RESOLS: ,__wrap_bar,pxr + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @bar() + +define void @_start() { + call void @bar() + ret void +} + +define void @__wrap_bar() { + ret void +} Index: lld/test/ELF/lto/wrap-2.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/wrap-2.ll @@ -0,0 +1,36 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: llvm-as %S/Inputs/wrap-bar.ll -o %t1.o +; RUN: ld.lld %t.o %t1.o -shared -o %t.so -wrap=bar +; RUN: llvm-objdump -d %t.so | FileCheck %s +; RUN: llvm-readobj -t %t.so | FileCheck -check-prefix=BIND %s + +; Make sure that calls in foo() are not eliminated and that bar is +; routed to __wrap_bar and __real_bar is routed to bar. + +; CHECK: foo: +; CHECK-NEXT: pushq %rax +; CHECK-NEXT: callq{{.*}}<__wrap_bar> +; CHECK-NEXT: callq{{.*}} + +; Check that bar and __wrap_bar retain their original binding. +; BIND: Name: bar +; BIND-NEXT: Value: +; BIND-NEXT: Size: +; BIND-NEXT: Binding: Local +; BIND: Name: __wrap_bar +; BIND-NEXT: Value: +; BIND-NEXT: Size: +; BIND-NEXT: Binding: Local + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @bar() +declare void @__real_bar() + +define void @foo() { + call void @bar() + call void @__real_bar() + ret void +} Index: llvm/include/llvm/LTO/LTO.h =================================================================== --- llvm/include/llvm/LTO/LTO.h +++ llvm/include/llvm/LTO/LTO.h @@ -366,8 +366,9 @@ /// each global symbol based on its internal resolution of that symbol. struct SymbolResolution { SymbolResolution() - : Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0) { - } + : Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0), + LinkerRedefined(0) {} + /// The linker has chosen this definition of the symbol. unsigned Prevailing : 1; @@ -377,6 +378,10 @@ /// The definition of this symbol is visible outside of the LTO unit. unsigned VisibleToRegularObj : 1; + + /// Linker redefined version of the symbol which appeared in -wrap or -defsym + /// linker option. + unsigned LinkerRedefined : 1; }; } // namespace lto Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ llvm/lib/LTO/LTO.cpp @@ -407,7 +407,7 @@ // Set the partition to external if we know it is used elsewhere, e.g. // it is visible to a regular object, is referenced from llvm.compiler_used, // or was already recorded as being referenced from a different partition. - if (Res.VisibleToRegularObj || Sym.isUsed() || + if (Res.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() || (GlobalRes.Partition != GlobalResolution::Unknown && GlobalRes.Partition != Partition)) { GlobalRes.Partition = GlobalResolution::External; @@ -438,6 +438,8 @@ OS << 'l'; if (Res.VisibleToRegularObj) OS << 'x'; + if (Res.LinkerRedefined) + OS << 'r'; OS << '\n'; } OS.flush(); @@ -542,6 +544,11 @@ if (Sym.isUndefined()) continue; Keep.push_back(GV); + // For symbols re-defined with linker -wrap and -defsym options, + // set the linkage to weak to inhibit IPO. The linkage will be + // restored by the linker. + if (Res.LinkerRedefined) + GV->setLinkage(GlobalValue::WeakAnyLinkage); switch (GV->getLinkage()) { default: break; Index: llvm/test/LTO/Resolution/X86/linker-redef.ll =================================================================== --- /dev/null +++ llvm/test/LTO/Resolution/X86/linker-redef.ll @@ -0,0 +1,16 @@ +; RUN: llvm-as %s -o %t.o +; RUN: llvm-lto2 run -o %t1.o %t.o -r %t.o,bar,pr +; RUN: llvm-readobj -t %t1.o.0 | FileCheck %s + +; CHECK: Name: bar +; CHECK-NEXT: Value: +; CHECK-NEXT: Size: +; CHECK-NEXT: Binding: Weak +; CHECK-NEXT: Type: Function + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @bar() { + ret void +} Index: llvm/tools/llvm-lto2/llvm-lto2.cpp =================================================================== --- llvm/tools/llvm-lto2/llvm-lto2.cpp +++ llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -157,6 +157,8 @@ Res.FinalDefinitionInLinkageUnit = true; else if (C == 'x') Res.VisibleToRegularObj = true; + else if (C == 'r') + Res.LinkerRedefined = true; else { llvm::errs() << "invalid character " << C << " in resolution: " << R << '\n';