Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -67,6 +67,12 @@ size_t NameOff = 0; // Offset in the string table }; +// Structure for mapping renamed symbols +struct RenamedSymbol { + Symbol *Target; + uint8_t OrigBinding; +}; + // This struct contains the global configuration for the linker. // Most fields are direct mapping from the command line options // and such fields have the same name as the corresponding options. @@ -98,6 +104,7 @@ std::vector VersionScriptGlobals; std::vector VersionScriptLocals; std::vector BuildIdVector; + llvm::MapVector RenamedSymbols; bool AllowMultipleDefinition; bool AsNeeded = false; bool Bsymbolic; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -970,6 +970,14 @@ Symtab.scanShlibUndefined(); Symtab.scanVersionScript(); + // Create wrapped symbols + for (auto *Arg : Args.filtered(OPT_wrap)) + Symtab.addSymbolWrap(Arg->getValue()); + + // Create alias symbols + for (std::pair &Def : getDefsym(Args)) + Symtab.addSymbolAlias(Def.first, Def.second); + Symtab.addCombinedLTOObject(); if (ErrorCount) return; @@ -979,12 +987,8 @@ for (StringRef Sym : Script->Opt.ReferencedSymbols) Symtab.addUndefined(Sym); - for (auto *Arg : Args.filtered(OPT_wrap)) - Symtab.wrap(Arg->getValue()); - - // Handle --defsym=sym=alias option. - for (std::pair &Def : getDefsym(Args)) - Symtab.alias(Def.first, Def.second); + // Apply symbol renames for -wrap and -defsym + Symtab.applySymbolRenames(); // Now that we have a complete list of input files. // Beyond this point, no new files are added. Index: lld/ELF/LTO.cpp =================================================================== --- lld/ELF/LTO.cpp +++ lld/ELF/LTO.cpp @@ -136,6 +136,7 @@ Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym()); if (R.Prevailing) undefine(Sym); + R.LinkerRedefined = Config->RenamedSymbols.count(Sym); } checkError(LTOObj->add(std::move(F.Obj), Resols)); } Index: lld/ELF/SymbolTable.h =================================================================== --- lld/ELF/SymbolTable.h +++ lld/ELF/SymbolTable.h @@ -39,6 +39,9 @@ public: void addFile(InputFile *File); void addCombinedLTOObject(); + void addSymbolAlias(StringRef Alias, StringRef Name); + void addSymbolWrap(StringRef Name); + void applySymbolRenames(); ArrayRef getSymbols() const { return SymVector; } ArrayRef *> getObjectFiles() const { return ObjectFiles; } @@ -85,8 +88,6 @@ SymbolBody *findInCurrentDSO(StringRef Name); void trace(StringRef Name); - void wrap(StringRef Name); - void alias(StringRef Alias, StringRef Name); private: std::vector findByVersion(SymbolVersion Ver); Index: lld/ELF/SymbolTable.cpp =================================================================== --- lld/ELF/SymbolTable.cpp +++ lld/ELF/SymbolTable.cpp @@ -156,24 +156,23 @@ // Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM. // Used to implement --wrap. -template void SymbolTable::wrap(StringRef Name) { +template void SymbolTable::addSymbolWrap(StringRef Name) { SymbolBody *B = find(Name); if (!B) return; Symbol *Sym = B->symbol(); Symbol *Real = addUndefined(Saver.save("__real_" + Name)); Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name)); + // Tell LTO not to eliminate this symbol + Wrap->IsUsedInRegularObj = true; - // We rename symbols by replacing the old symbol's SymbolBody with the new - // symbol's SymbolBody. This causes all SymbolBody pointers referring to the - // old symbol to instead refer to the new symbol. - memcpy(Real->Body.buffer, Sym->Body.buffer, sizeof(Sym->Body)); - memcpy(Sym->Body.buffer, Wrap->Body.buffer, sizeof(Wrap->Body)); + Config->RenamedSymbols[Real] = RenamedSymbol{Sym, Real->Binding}; + Config->RenamedSymbols[Sym] = RenamedSymbol{Wrap, Sym->Binding}; } // Creates alias for symbol. Used to implement --defsym=ALIAS=SYM. -template -void SymbolTable::alias(StringRef Alias, StringRef Name) { +template void SymbolTable::addSymbolAlias(StringRef Alias, + StringRef Name) { SymbolBody *B = find(Name); if (!B) { error("-defsym: undefined symbol: " + Name); @@ -181,7 +180,22 @@ } Symbol *Sym = B->symbol(); Symbol *AliasSym = addUndefined(Alias); - memcpy(AliasSym->Body.buffer, Sym->Body.buffer, sizeof(AliasSym->Body)); + // Tell LTO not to eliminate this symbol + Sym->IsUsedInRegularObj = true; + Config->RenamedSymbols[AliasSym] = RenamedSymbol{Sym, AliasSym->Binding}; +} + +// Apply symbol renames created by -wrap and -defsym +template void SymbolTable::applySymbolRenames() { + for (auto &RSI : Config->RenamedSymbols) { + Symbol *Sym = RSI.first; + Symbol *Rename = RSI.second.Target; + Sym->Binding = RSI.second.OrigBinding; + // We rename symbols by replacing the old symbol's SymbolBody with the new + // symbol's SymbolBody. This causes all SymbolBody pointers referring to the + // old symbol to instead refer to the new symbol. + memcpy(Sym->Body.buffer, Rename->Body.buffer, sizeof(Sym->Body)); + } } static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) { Index: lld/test/ELF/lto/Inputs/defsym-bar.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/Inputs/defsym-bar.ll @@ -0,0 +1,21 @@ +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_bar1() +declare void @this_is_bar2() +declare void @this_is_bar3() + +define hidden void @bar1() { + call void @this_is_bar1() + ret void +} + +define hidden void @bar2() { + call void @this_is_bar1() + ret void +} + +define hidden void @bar3() { + call void @this_is_bar1() + 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,28 @@ +; 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{{.*}} +; 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,px + +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 @@ -405,10 +405,11 @@ if (Res.Prevailing) GlobalRes.IRName = Sym.getIRName(); - // 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() || + // Set the partition to external if we know it is re-defined by the linker + // with -defsym or -wrap options, 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.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() || (GlobalRes.Partition != GlobalResolution::Unknown && GlobalRes.Partition != Partition)) { GlobalRes.Partition = GlobalResolution::External; @@ -439,6 +440,8 @@ OS << 'l'; if (Res.VisibleToRegularObj) OS << 'x'; + if (Res.LinkerRedefined) + OS << 'r'; OS << '\n'; } OS.flush(); @@ -543,6 +546,12 @@ 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); + GlobalValue::LinkageTypes OriginalLinkage = GV->getLinkage(); if (GlobalValue::isLinkOnceLinkage(OriginalLinkage)) GV->setLinkage(GlobalValue::getWeakLinkage( 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 @@ -162,6 +162,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';