Skip to content

Commit db3b87b

Browse files
committed
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
1 parent b2ef948 commit db3b87b

File tree

14 files changed

+222
-23
lines changed

14 files changed

+222
-23
lines changed

lld/ELF/Config.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ struct VersionDefinition {
6767
size_t NameOff = 0; // Offset in the string table
6868
};
6969

70+
// Structure for mapping renamed symbols
71+
struct RenamedSymbol {
72+
Symbol *Target;
73+
uint8_t OrigBinding;
74+
};
75+
7076
// This struct contains the global configuration for the linker.
7177
// Most fields are direct mapping from the command line options
7278
// and such fields have the same name as the corresponding options.
@@ -98,6 +104,7 @@ struct Configuration {
98104
std::vector<SymbolVersion> VersionScriptGlobals;
99105
std::vector<SymbolVersion> VersionScriptLocals;
100106
std::vector<uint8_t> BuildIdVector;
107+
llvm::MapVector<Symbol *, RenamedSymbol> RenamedSymbols;
101108
bool AllowMultipleDefinition;
102109
bool AsNeeded = false;
103110
bool Bsymbolic;

lld/ELF/Driver.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,14 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
970970
Symtab.scanShlibUndefined();
971971
Symtab.scanVersionScript();
972972

973+
// Create wrapped symbols for -wrap option.
974+
for (auto *Arg : Args.filtered(OPT_wrap))
975+
Symtab.addSymbolWrap(Arg->getValue());
976+
977+
// Create alias symbols for -defsym option.
978+
for (std::pair<StringRef, StringRef> &Def : getDefsym(Args))
979+
Symtab.addSymbolAlias(Def.first, Def.second);
980+
973981
Symtab.addCombinedLTOObject();
974982
if (ErrorCount)
975983
return;
@@ -979,12 +987,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
979987
for (StringRef Sym : Script->Opt.ReferencedSymbols)
980988
Symtab.addUndefined(Sym);
981989

982-
for (auto *Arg : Args.filtered(OPT_wrap))
983-
Symtab.wrap(Arg->getValue());
984-
985-
// Handle --defsym=sym=alias option.
986-
for (std::pair<StringRef, StringRef> &Def : getDefsym(Args))
987-
Symtab.alias(Def.first, Def.second);
990+
// Apply symbol renames for -wrap and -defsym
991+
Symtab.applySymbolRenames();
988992

989993
// Now that we have a complete list of input files.
990994
// Beyond this point, no new files are added.

lld/ELF/LTO.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
136136
Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
137137
if (R.Prevailing)
138138
undefine(Sym);
139+
R.LinkerRedefined = Config->RenamedSymbols.count(Sym);
139140
}
140141
checkError(LTOObj->add(std::move(F.Obj), Resols));
141142
}

lld/ELF/SymbolTable.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -156,32 +156,52 @@ template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
156156

157157
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
158158
// Used to implement --wrap.
159-
template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
159+
template <class ELFT> void SymbolTable<ELFT>::addSymbolWrap(StringRef Name) {
160160
SymbolBody *B = find(Name);
161161
if (!B)
162162
return;
163163
Symbol *Sym = B->symbol();
164164
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
165165
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
166166

167-
// We rename symbols by replacing the old symbol's SymbolBody with the new
168-
// symbol's SymbolBody. This causes all SymbolBody pointers referring to the
169-
// old symbol to instead refer to the new symbol.
170-
memcpy(Real->Body.buffer, Sym->Body.buffer, sizeof(Sym->Body));
171-
memcpy(Sym->Body.buffer, Wrap->Body.buffer, sizeof(Wrap->Body));
167+
// Tell LTO not to eliminate this symbol
168+
Wrap->IsUsedInRegularObj = true;
169+
170+
Config->RenamedSymbols[Real] = RenamedSymbol{Sym, Real->Binding};
171+
Config->RenamedSymbols[Sym] = RenamedSymbol{Wrap, Sym->Binding};
172172
}
173173

174174
// Creates alias for symbol. Used to implement --defsym=ALIAS=SYM.
175-
template <class ELFT>
176-
void SymbolTable<ELFT>::alias(StringRef Alias, StringRef Name) {
175+
template <class ELFT> void SymbolTable<ELFT>::addSymbolAlias(StringRef Alias,
176+
StringRef Name) {
177177
SymbolBody *B = find(Name);
178178
if (!B) {
179179
error("-defsym: undefined symbol: " + Name);
180180
return;
181181
}
182182
Symbol *Sym = B->symbol();
183183
Symbol *AliasSym = addUndefined(Alias);
184-
memcpy(AliasSym->Body.buffer, Sym->Body.buffer, sizeof(AliasSym->Body));
184+
185+
// Tell LTO not to eliminate this symbol
186+
Sym->IsUsedInRegularObj = true;
187+
Config->RenamedSymbols[AliasSym] = RenamedSymbol{Sym, AliasSym->Binding};
188+
}
189+
190+
// Apply symbol renames created by -wrap and -defsym. The renames are created
191+
// before LTO in addSymbolWrap() and addSymbolAlias() to have a chance to inform
192+
// LTO (if LTO is running) not to include these symbols in IPO. Now that the
193+
// symbols are finalized, we can perform the replacement.
194+
template <class ELFT> void SymbolTable<ELFT>::applySymbolRenames() {
195+
for (auto &KV : Config->RenamedSymbols) {
196+
Symbol *Sym = KV.first;
197+
Symbol *Rename = KV.second.Target;
198+
Sym->Binding = KV.second.OrigBinding;
199+
200+
// We rename symbols by replacing the old symbol's SymbolBody with the new
201+
// symbol's SymbolBody. This causes all SymbolBody pointers referring to the
202+
// old symbol to instead refer to the new symbol.
203+
memcpy(Sym->Body.buffer, Rename->Body.buffer, sizeof(Sym->Body));
204+
}
185205
}
186206

187207
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {

lld/ELF/SymbolTable.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ template <class ELFT> class SymbolTable {
3939
public:
4040
void addFile(InputFile *File);
4141
void addCombinedLTOObject();
42+
void addSymbolAlias(StringRef Alias, StringRef Name);
43+
void addSymbolWrap(StringRef Name);
44+
void applySymbolRenames();
4245

4346
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
4447
ArrayRef<ObjectFile<ELFT> *> getObjectFiles() const { return ObjectFiles; }
@@ -85,8 +88,6 @@ template <class ELFT> class SymbolTable {
8588
SymbolBody *findInCurrentDSO(StringRef Name);
8689

8790
void trace(StringRef Name);
88-
void wrap(StringRef Name);
89-
void alias(StringRef Alias, StringRef Name);
9091

9192
private:
9293
std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);

lld/test/ELF/lto/Inputs/defsym-bar.ll

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
2+
target triple = "x86_64-unknown-linux-gnu"
3+
4+
declare void @this_is_bar1()
5+
declare void @this_is_bar2()
6+
declare void @this_is_bar3()
7+
8+
define hidden void @bar1() {
9+
call void @this_is_bar1()
10+
ret void
11+
}
12+
13+
define hidden void @bar2() {
14+
call void @this_is_bar2()
15+
ret void
16+
}
17+
18+
define hidden void @bar3() {
19+
call void @this_is_bar3()
20+
ret void
21+
}

lld/test/ELF/lto/Inputs/wrap-bar.ll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
2+
target triple = "x86_64-unknown-linux-gnu"
3+
4+
define hidden void @bar() {
5+
ret void
6+
}
7+
8+
define hidden void @__real_bar() {
9+
ret void
10+
}
11+
12+
define hidden void @__wrap_bar() {
13+
ret void
14+
}

lld/test/ELF/lto/defsym.ll

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
; REQUIRES: x86
2+
; RUN: llvm-as %s -o %t.o
3+
; RUN: llvm-as %S/Inputs/defsym-bar.ll -o %t1.o
4+
; RUN: ld.lld %t.o %t1.o -shared -o %t.so -defsym=bar2=bar3
5+
; RUN: llvm-objdump -d %t.so | FileCheck %s
6+
7+
; Call to bar2() should not be inlined and should be routed to bar3()
8+
; Symbol bar3 should not be eliminated
9+
10+
; CHECK: foo:
11+
; CHECK-NEXT: pushq %rax
12+
; CHECK-NEXT: callq
13+
; CHECK-NEXT: callq{{.*}}<bar3>
14+
; CHECK-NEXT: callq
15+
16+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
17+
target triple = "x86_64-unknown-linux-gnu"
18+
19+
declare void @bar1()
20+
declare void @bar2()
21+
declare void @bar3()
22+
23+
define void @foo() {
24+
call void @bar1()
25+
call void @bar2()
26+
call void @bar3()
27+
ret void
28+
}

lld/test/ELF/lto/wrap-1.ll

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; REQUIRES: x86
2+
; RUN: llvm-as %s -o %t.o
3+
; RUN: ld.lld %t.o -o %t.out -wrap=bar -save-temps
4+
; RUN: llvm-readobj -t %t.out | FileCheck %s
5+
; RUN: cat %t.out.resolution.txt | FileCheck -check-prefix=RESOLS %s
6+
7+
; CHECK: Name: __wrap_bar
8+
; CHECK-NEXT: Value:
9+
; CHECK-NEXT: Size:
10+
; CHECK-NEXT: Binding: Global
11+
; CHECK-NEXT: Type: Function
12+
13+
; Make sure that the 'r' (linker redefined) bit is set for bar and __wrap_bar
14+
; in the resolutions file.
15+
; RESOLS: ,bar,r
16+
; RESOLS: ,__wrap_bar,px
17+
; RESOLS: ,__real_bar,pxr
18+
19+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
20+
target triple = "x86_64-unknown-linux-gnu"
21+
22+
declare void @bar()
23+
24+
define void @_start() {
25+
call void @bar()
26+
ret void
27+
}
28+
29+
define void @__wrap_bar() {
30+
ret void
31+
}
32+
33+
define void @__real_bar() {
34+
ret void
35+
}

lld/test/ELF/lto/wrap-2.ll

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; REQUIRES: x86
2+
; RUN: llvm-as %s -o %t.o
3+
; RUN: llvm-as %S/Inputs/wrap-bar.ll -o %t1.o
4+
; RUN: ld.lld %t.o %t1.o -shared -o %t.so -wrap=bar
5+
; RUN: llvm-objdump -d %t.so | FileCheck %s
6+
; RUN: llvm-readobj -t %t.so | FileCheck -check-prefix=BIND %s
7+
8+
; Make sure that calls in foo() are not eliminated and that bar is
9+
; routed to __wrap_bar and __real_bar is routed to bar.
10+
11+
; CHECK: foo:
12+
; CHECK-NEXT: pushq %rax
13+
; CHECK-NEXT: callq{{.*}}<__wrap_bar>
14+
; CHECK-NEXT: callq{{.*}}<bar>
15+
16+
; Check that bar and __wrap_bar retain their original binding.
17+
; BIND: Name: bar
18+
; BIND-NEXT: Value:
19+
; BIND-NEXT: Size:
20+
; BIND-NEXT: Binding: Local
21+
; BIND: Name: __wrap_bar
22+
; BIND-NEXT: Value:
23+
; BIND-NEXT: Size:
24+
; BIND-NEXT: Binding: Local
25+
26+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
27+
target triple = "x86_64-unknown-linux-gnu"
28+
29+
declare void @bar()
30+
declare void @__real_bar()
31+
32+
define void @foo() {
33+
call void @bar()
34+
call void @__real_bar()
35+
ret void
36+
}

llvm/include/llvm/LTO/LTO.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,9 @@ class LTO {
366366
/// each global symbol based on its internal resolution of that symbol.
367367
struct SymbolResolution {
368368
SymbolResolution()
369-
: Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0) {
370-
}
369+
: Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0),
370+
LinkerRedefined(0) {}
371+
371372
/// The linker has chosen this definition of the symbol.
372373
unsigned Prevailing : 1;
373374

@@ -377,6 +378,10 @@ struct SymbolResolution {
377378

378379
/// The definition of this symbol is visible outside of the LTO unit.
379380
unsigned VisibleToRegularObj : 1;
381+
382+
/// Linker redefined version of the symbol which appeared in -wrap or -defsym
383+
/// linker option.
384+
unsigned LinkerRedefined : 1;
380385
};
381386

382387
} // namespace lto

llvm/lib/LTO/LTO.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,10 +405,11 @@ void LTO::addSymbolToGlobalRes(const InputFile::Symbol &Sym,
405405
if (Res.Prevailing)
406406
GlobalRes.IRName = Sym.getIRName();
407407

408-
// Set the partition to external if we know it is used elsewhere, e.g.
409-
// it is visible to a regular object, is referenced from llvm.compiler_used,
410-
// or was already recorded as being referenced from a different partition.
411-
if (Res.VisibleToRegularObj || Sym.isUsed() ||
408+
// Set the partition to external if we know it is re-defined by the linker
409+
// with -defsym or -wrap options, used elsewhere, e.g. it is visible to a
410+
// regular object, is referenced from llvm.compiler_used, or was already
411+
// recorded as being referenced from a different partition.
412+
if (Res.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() ||
412413
(GlobalRes.Partition != GlobalResolution::Unknown &&
413414
GlobalRes.Partition != Partition)) {
414415
GlobalRes.Partition = GlobalResolution::External;
@@ -439,6 +440,8 @@ static void writeToResolutionFile(raw_ostream &OS, InputFile *Input,
439440
OS << 'l';
440441
if (Res.VisibleToRegularObj)
441442
OS << 'x';
443+
if (Res.LinkerRedefined)
444+
OS << 'r';
442445
OS << '\n';
443446
}
444447
OS.flush();
@@ -543,6 +546,12 @@ Error LTO::addRegularLTO(BitcodeModule BM,
543546
if (Sym.isUndefined())
544547
continue;
545548
Keep.push_back(GV);
549+
// For symbols re-defined with linker -wrap and -defsym options,
550+
// set the linkage to weak to inhibit IPO. The linkage will be
551+
// restored by the linker.
552+
if (Res.LinkerRedefined)
553+
GV->setLinkage(GlobalValue::WeakAnyLinkage);
554+
546555
GlobalValue::LinkageTypes OriginalLinkage = GV->getLinkage();
547556
if (GlobalValue::isLinkOnceLinkage(OriginalLinkage))
548557
GV->setLinkage(GlobalValue::getWeakLinkage(
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
; RUN: llvm-as %s -o %t.o
2+
; RUN: llvm-lto2 run -o %t1.o %t.o -r %t.o,bar,pr
3+
; RUN: llvm-readobj -t %t1.o.0 | FileCheck %s
4+
5+
; CHECK: Name: bar
6+
; CHECK-NEXT: Value:
7+
; CHECK-NEXT: Size:
8+
; CHECK-NEXT: Binding: Weak
9+
; CHECK-NEXT: Type: Function
10+
11+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
12+
target triple = "x86_64-unknown-linux-gnu"
13+
14+
define void @bar() {
15+
ret void
16+
}

llvm/tools/llvm-lto2/llvm-lto2.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ static int run(int argc, char **argv) {
162162
Res.FinalDefinitionInLinkageUnit = true;
163163
else if (C == 'x')
164164
Res.VisibleToRegularObj = true;
165+
else if (C == 'r')
166+
Res.LinkerRedefined = true;
165167
else {
166168
llvm::errs() << "invalid character " << C << " in resolution: " << R
167169
<< '\n';

0 commit comments

Comments
 (0)