forked from OSchip/llvm-project
Symbols re-defined with -wrap and -defsym need to be excluded from inter-
procedural optimizations to prevent dropping symbols and allow the linker to process re-directs. PR33145: --wrap doesn't work with lto. Differential Revision: https://reviews.llvm.org/D33621 llvm-svn: 304719
This commit is contained in:
parent
b2ef948628
commit
db3b87b2c0
|
@ -67,6 +67,12 @@ struct VersionDefinition {
|
|||
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 @@ struct Configuration {
|
|||
std::vector<SymbolVersion> VersionScriptGlobals;
|
||||
std::vector<SymbolVersion> VersionScriptLocals;
|
||||
std::vector<uint8_t> BuildIdVector;
|
||||
llvm::MapVector<Symbol *, RenamedSymbol> RenamedSymbols;
|
||||
bool AllowMultipleDefinition;
|
||||
bool AsNeeded = false;
|
||||
bool Bsymbolic;
|
||||
|
|
|
@ -970,6 +970,14 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
Symtab.scanShlibUndefined();
|
||||
Symtab.scanVersionScript();
|
||||
|
||||
// Create wrapped symbols for -wrap option.
|
||||
for (auto *Arg : Args.filtered(OPT_wrap))
|
||||
Symtab.addSymbolWrap(Arg->getValue());
|
||||
|
||||
// Create alias symbols for -defsym option.
|
||||
for (std::pair<StringRef, StringRef> &Def : getDefsym(Args))
|
||||
Symtab.addSymbolAlias(Def.first, Def.second);
|
||||
|
||||
Symtab.addCombinedLTOObject();
|
||||
if (ErrorCount)
|
||||
return;
|
||||
|
@ -979,12 +987,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
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<StringRef, StringRef> &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.
|
||||
|
|
|
@ -136,6 +136,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
|
|||
|
||||
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
|
||||
// Used to implement --wrap.
|
||||
template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
|
||||
template <class ELFT> void SymbolTable<ELFT>::addSymbolWrap(StringRef Name) {
|
||||
SymbolBody *B = find(Name);
|
||||
if (!B)
|
||||
return;
|
||||
|
@ -164,16 +164,16 @@ template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
|
|||
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
|
||||
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
|
||||
|
||||
// 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));
|
||||
// Tell LTO not to eliminate this symbol
|
||||
Wrap->IsUsedInRegularObj = true;
|
||||
|
||||
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 <class ELFT>
|
||||
void SymbolTable<ELFT>::alias(StringRef Alias, StringRef Name) {
|
||||
template <class ELFT> void SymbolTable<ELFT>::addSymbolAlias(StringRef Alias,
|
||||
StringRef Name) {
|
||||
SymbolBody *B = find(Name);
|
||||
if (!B) {
|
||||
error("-defsym: undefined symbol: " + Name);
|
||||
|
@ -181,7 +181,27 @@ void SymbolTable<ELFT>::alias(StringRef Alias, StringRef Name) {
|
|||
}
|
||||
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. The renames are created
|
||||
// before LTO in addSymbolWrap() and addSymbolAlias() to have a chance to inform
|
||||
// LTO (if LTO is running) not to include these symbols in IPO. Now that the
|
||||
// symbols are finalized, we can perform the replacement.
|
||||
template <class ELFT> void SymbolTable<ELFT>::applySymbolRenames() {
|
||||
for (auto &KV : Config->RenamedSymbols) {
|
||||
Symbol *Sym = KV.first;
|
||||
Symbol *Rename = KV.second.Target;
|
||||
Sym->Binding = KV.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) {
|
||||
|
|
|
@ -39,6 +39,9 @@ template <class ELFT> class SymbolTable {
|
|||
public:
|
||||
void addFile(InputFile *File);
|
||||
void addCombinedLTOObject();
|
||||
void addSymbolAlias(StringRef Alias, StringRef Name);
|
||||
void addSymbolWrap(StringRef Name);
|
||||
void applySymbolRenames();
|
||||
|
||||
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
|
||||
ArrayRef<ObjectFile<ELFT> *> getObjectFiles() const { return ObjectFiles; }
|
||||
|
@ -85,8 +88,6 @@ public:
|
|||
SymbolBody *findInCurrentDSO(StringRef Name);
|
||||
|
||||
void trace(StringRef Name);
|
||||
void wrap(StringRef Name);
|
||||
void alias(StringRef Alias, StringRef Name);
|
||||
|
||||
private:
|
||||
std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);
|
||||
|
|
|
@ -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_bar2()
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @bar3() {
|
||||
call void @this_is_bar3()
|
||||
ret void
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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{{.*}}<bar3>
|
||||
; 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
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
; 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
|
||||
; RESOLS: ,__real_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
|
||||
}
|
||||
|
||||
define void @__real_bar() {
|
||||
ret void
|
||||
}
|
|
@ -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{{.*}}<bar>
|
||||
|
||||
; 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
|
||||
}
|
|
@ -366,8 +366,9 @@ private:
|
|||
/// 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 @@ struct SymbolResolution {
|
|||
|
||||
/// 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
|
||||
|
|
|
@ -405,10 +405,11 @@ void LTO::addSymbolToGlobalRes(const InputFile::Symbol &Sym,
|
|||
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 @@ static void writeToResolutionFile(raw_ostream &OS, InputFile *Input,
|
|||
OS << 'l';
|
||||
if (Res.VisibleToRegularObj)
|
||||
OS << 'x';
|
||||
if (Res.LinkerRedefined)
|
||||
OS << 'r';
|
||||
OS << '\n';
|
||||
}
|
||||
OS.flush();
|
||||
|
@ -543,6 +546,12 @@ Error LTO::addRegularLTO(BitcodeModule BM,
|
|||
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(
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -162,6 +162,8 @@ static int run(int argc, char **argv) {
|
|||
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';
|
||||
|
|
Loading…
Reference in New Issue