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::StringMap 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(B->getName()) != + 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,15 @@ Obj->parse(DummyGroups); ObjectFiles.push_back(Obj); } + + // Restore bindings of renamed symbols. + for (auto &i : Config->RenamedSymbols) { + SymbolBody *Body = find(i.first()); + if (Body) { + Symbol *Sym = Body->symbol(); + Sym->Binding = i.second; + } + } } template @@ -154,6 +163,32 @@ 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) { + std::string WrappedName = "__wrap_" + Name.str(); + std::string RealName = "__real_" + Name.str(); + Config->RenamedSymbols[WrappedName] = getSymbolBinding(find(WrappedName)); + Config->RenamedSymbols[RealName] = getSymbolBinding(find(RealName)); + Config->RenamedSymbols[Name] = getSymbolBinding(find(Name)); +} + +template void SymbolTable::addLTOSymbolAlias(StringRef Alias, + StringRef Name) { + Config->RenamedSymbols[Alias] = getSymbolBinding(find(Alias)); + Config->RenamedSymbols[Name] = getSymbolBinding(find(Name)); +} + // Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM. // Used to implement --wrap. template void SymbolTable::wrap(StringRef Name) { 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 void @bar1() { + ret void +} + +define void @bar2() { + ret void +} + +define 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,13 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @this_is_bar() +declare void @this_is_real_bar() + +define void @bar() { + ret void +} + +define void @__real_bar() { + ret void +} Index: lld/test/ELF/lto/defsym.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/defsym.ll @@ -0,0 +1,31 @@ +; 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 +; RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=RELS %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 74 +; CHECK-NEXT: callq 85 + +; RELS: JUMP_SLOT bar3 +; RELS-NEXT: JUMP_SLOT bar3 + +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,24 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: ld.lld %t.o -o %t.out -wrap=bar +; RUN: llvm-readobj -t %t.out | FileCheck %s + +; CHECK: Name: __wrap_bar +; CHECK-NEXT: Value: +; CHECK-NEXT: Size: +; CHECK-NEXT: Binding: Global +; 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" + +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,29 @@ +; 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 -r %t.so | FileCheck -check-prefix=RELS %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 58 +; CHECK-NEXT: callq 69 + +; RELS: JUMP_SLOT __wrap_bar +; RELS-NEXT: JUMP_SLOT bar + +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,7 +366,8 @@ /// 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; @@ -418,7 +418,7 @@ // Flag as visible outside of ThinLTO if visible from a regular object or // if this is a reference in the regular LTO partition. GlobalRes.VisibleOutsideThinLTO |= - (Res.VisibleToRegularObj || Sym.isUsed() || + (Res.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() || Partition == GlobalResolution::RegularLTO); } @@ -438,6 +438,8 @@ OS << 'l'; if (Res.VisibleToRegularObj) OS << 'x'; + if (Res.LinkerRedefined) + OS << 'r'; OS << '\n'; } OS.flush(); @@ -542,6 +544,8 @@ if (Sym.isUndefined()) continue; Keep.push_back(GV); + if (Res.LinkerRedefined) + GV->setLinkage(GlobalValue::WeakAnyLinkage); switch (GV->getLinkage()) { default: break;