Skip to content

Commit 1af25a9

Browse files
committed
[ELF] Fix wrapping symbols produced during LTO codegen
We were previously not correctly wrapping symbols that were only produced during LTO codegen and unreferenced before then, or symbols only referenced from such symbols. The root cause was that we weren't marking the wrapped symbol as used if we only saw the use after LTO codegen, leading to the failed wrapping. Fix this by explicitly tracking whether a symbol will become referenced after wrapping is done. We can use this property to tell LTO to preserve such symbols, instead of overload isUsedInRegularObj for this purpose. Since we're no longer setting isUsedInRegularObj for all symbols which will be wrapped, its value at the time of performing the wrapping in the symbol table will accurately reflect whether the symbol was actually used in an object (including in an LTO-generated object), and we can propagate that value to the wrapped symbol and thereby ensure we wrap correctly. This incorrect wrapping was the only scenario I was aware of where we produced an invalid PLT relocation, which D123985 started diagnosing, and with it fixed, we lose the test for that diagnosis. I think it's worth keeping the diagnosis though, in case we run into other issues in the future which would be caught by it. Fixes PR50675. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D124056
1 parent b862bcb commit 1af25a9

File tree

5 files changed

+125
-18
lines changed

5 files changed

+125
-18
lines changed

lld/ELF/Driver.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,16 +2196,19 @@ static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &args) {
21962196
real->scriptDefined = true;
21972197
sym->scriptDefined = true;
21982198

2199-
// Tell LTO not to eliminate these symbols.
2200-
sym->isUsedInRegularObj = true;
2201-
// If sym is referenced in any object file, bitcode file or shared object,
2202-
// retain wrap which is the redirection target of sym. If the object file
2203-
// defining sym has sym references, we cannot easily distinguish the case
2204-
// from cases where sym is not referenced. Retain wrap because we choose to
2205-
// wrap sym references regardless of whether sym is defined
2199+
// If a symbol is referenced in any object file, bitcode file or shared
2200+
// object, mark its redirection target (foo for __real_foo and __wrap_foo
2201+
// for foo) as referenced after redirection, which will be used to tell LTO
2202+
// to not eliminate the redirection target. If the object file defining the
2203+
// symbol also references it, we cannot easily distinguish the case from
2204+
// cases where the symbol is not referenced. Retain the redirection target
2205+
// in this case because we choose to wrap symbol references regardless of
2206+
// whether the symbol is defined
22062207
// (https://sourceware.org/bugzilla/show_bug.cgi?id=26358).
2208+
if (real->referenced || real->isDefined())
2209+
sym->referencedAfterWrap = true;
22072210
if (sym->referenced || sym->isDefined())
2208-
wrap->isUsedInRegularObj = true;
2211+
wrap->referencedAfterWrap = true;
22092212
}
22102213
return v;
22112214
}

lld/ELF/LTO.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,11 @@ void BitcodeCompiler::add(BitcodeFile &f) {
247247
// for doing final link.
248248
// 2) Symbols that are used in regular objects.
249249
// 3) C named sections if we have corresponding __start_/__stop_ symbol.
250-
// 4) Symbols that are defined in bitcode files and used for dynamic linking.
250+
// 4) Symbols that are defined in bitcode files and used for dynamic
251+
// linking.
252+
// 5) Symbols that will be referenced after linker wrapping is performed.
251253
r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj ||
254+
sym->referencedAfterWrap ||
252255
(r.Prevailing && sym->includeInDynsym()) ||
253256
usedStartStop.count(objSym.getSectionName());
254257
// Identify symbols exported dynamically, and that therefore could be

lld/ELF/SymbolTable.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
3939
idx2 = idx1;
4040
idx1 = idx3;
4141

42-
if (!real->isUsedInRegularObj && sym->isUndefined())
42+
// Propagate symbol usage information to the redirected symbols.
43+
if (sym->isUsedInRegularObj)
44+
wrap->isUsedInRegularObj = true;
45+
if (real->isUsedInRegularObj)
46+
sym->isUsedInRegularObj = true;
47+
else if (sym->isUndefined())
4348
sym->isUsedInRegularObj = false;
4449

4550
// Now renaming is complete, and no one refers to real. We drop real from

lld/ELF/Symbols.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,13 @@ class Symbol {
131131
// Used to track if there has been at least one undefined reference to the
132132
// symbol. For Undefined and SharedSymbol, the binding may change to STB_WEAK
133133
// if the first undefined reference from a non-shared object is weak.
134-
//
135-
// This is also used to retain __wrap_foo when foo is referenced.
136134
uint8_t referenced : 1;
137135

136+
// Used to track if this symbol will be referenced after wrapping is performed
137+
// (i.e. this will be true for foo if __real_foo is referenced, and will be
138+
// true for __wrap_foo if foo is referenced).
139+
uint8_t referencedAfterWrap : 1;
140+
138141
// True if this symbol is specified by --trace-symbol option.
139142
uint8_t traced : 1;
140143

@@ -244,12 +247,13 @@ class Symbol {
244247
binding(binding), stOther(stOther), symbolKind(k),
245248
visibility(stOther & 3), isPreemptible(false),
246249
isUsedInRegularObj(false), used(false), exportDynamic(false),
247-
inDynamicList(false), referenced(false), traced(false),
248-
hasVersionSuffix(false), isInIplt(false), gotInIgot(false),
249-
folded(false), needsTocRestore(false), scriptDefined(false),
250-
needsCopy(false), needsGot(false), needsPlt(false), needsTlsDesc(false),
251-
needsTlsGd(false), needsTlsGdToIe(false), needsGotDtprel(false),
252-
needsTlsIe(false), hasDirectReloc(false) {}
250+
inDynamicList(false), referenced(false), referencedAfterWrap(false),
251+
traced(false), hasVersionSuffix(false), isInIplt(false),
252+
gotInIgot(false), folded(false), needsTocRestore(false),
253+
scriptDefined(false), needsCopy(false), needsGot(false),
254+
needsPlt(false), needsTlsDesc(false), needsTlsGd(false),
255+
needsTlsGdToIe(false), needsGotDtprel(false), needsTlsIe(false),
256+
hasDirectReloc(false) {}
253257

254258
public:
255259
// True if this symbol is in the Iplt sub-section of the Plt and the Igot
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# REQUIRES: x86
2+
## Verify that we can correctly wrap symbols produced only during LTO codegen
3+
## and unreferenced before then.
4+
5+
# RUN: rm -rf %t && split-file %s %t
6+
# RUN: llvm-mc -triple x86_64-elf --filetype=obj -o %t/unwind.o %t/unwind.s
7+
# RUN: ld.lld -shared -o %t/libunwind.so -soname libunwind.so %t/unwind.o
8+
# RUN: llvm-as -o %t/resume.bc %t/resume.ll
9+
# RUN: ld.lld -shared -o %t/libresume.so -soname libresume.so %t/resume.bc \
10+
# RUN: %t/libunwind.so --wrap _Unwind_Resume
11+
# RUN: llvm-objdump --dynamic-reloc --disassemble %t/libresume.so | \
12+
# RUN: FileCheck --check-prefix=UNWIND-DISASM %s
13+
# RUN: llvm-readelf --dyn-syms %t/libresume.so | \
14+
# RUN: FileCheck --check-prefix=UNWIND-DYNSYM %s
15+
16+
# UNWIND-DISASM: [[#%x,RELOC:]] R_X86_64_JUMP_SLOT __wrap__Unwind_Resume
17+
# UNWIND-DISASM-LABEL: <_Z1fv>:
18+
# UNWIND-DISASM: callq {{.*}}<__wrap__Unwind_Resume@plt>
19+
# UNWIND-DISASM-LABEL: <__wrap__Unwind_Resume@plt>:
20+
# UNWIND-DISASM-NEXT: jmpq *[[#]](%rip) # [[#%#x,RELOC]]
21+
22+
# UNWIND-DYNSYM: Symbol table '.dynsym' contains 5 entries:
23+
# UNWIND-DYNSYM: NOTYPE LOCAL DEFAULT UND
24+
# UNWIND-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND throw
25+
# UNWIND-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND _Unwind_Resume
26+
# UNWIND-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND __wrap__Unwind_Resume
27+
# UNWIND-DYNSYM-NEXT: FUNC GLOBAL DEFAULT 9 _Z1fv
28+
29+
# RUN: llvm-mc -triple x86_64-elf -filetype=obj -o %t/malloc.o %t/malloc.s
30+
# RUN: ld.lld -shared -o %t/libmalloc.so -soname libmalloc.so %t/malloc.o
31+
# RUN: llvm-mc -triple x86_64-elf -filetype=obj -o %t/emutls.o %t/emutls.s
32+
# RUN: llvm-as -o %t/usetls.bc %t/usetls.ll
33+
# RUN: ld.lld -shared -o %t/libusetls.so %t/usetls.bc %t/libmalloc.so \
34+
# RUN: --start-lib %t/emutls.o -mllvm -emulated-tls --wrap malloc
35+
# RUN: llvm-objdump --dynamic-reloc --disassemble %t/libusetls.so | \
36+
# RUN: FileCheck --check-prefix=USETLS-DISASM %s
37+
# RUN: llvm-readelf --dyn-syms %t/libusetls.so | \
38+
# RUN: FileCheck --check-prefix=USETLS-DYNSYM %s
39+
40+
# USETLS-DISASM: [[#%x,RELOC:]] R_X86_64_JUMP_SLOT __wrap_malloc
41+
# USETLS-DISASM-LABEL: <__emutls_get_address>:
42+
# USETLS-DISASM-NEXT: jmp{{.*}}<__wrap_malloc@plt>
43+
# USETLS-DISASM-LABEL: <__wrap_malloc@plt>:
44+
# USETLS-DISASM-NEXT: jmpq *[[#]](%rip) # [[#%#x,RELOC]]
45+
46+
# USETLS-DYNSYM: Symbol table '.dynsym' contains 6 entries:
47+
# USETLS-DYNSYM: NOTYPE LOCAL DEFAULT UND
48+
# USETLS-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND malloc
49+
# USETLS-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT UND __wrap_malloc
50+
# USETLS-DYNSYM-NEXT: FUNC GLOBAL DEFAULT 6 f
51+
# USETLS-DYNSYM-NEXT: NOTYPE GLOBAL DEFAULT 6 __emutls_get_address
52+
53+
#--- unwind.s
54+
.globl _Unwind_Resume
55+
.globl __wrap__Unwind_Resume
56+
_Unwind_Resume:
57+
__wrap__Unwind_Resume:
58+
retq
59+
60+
#--- resume.ll
61+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
62+
target triple = "x86_64-unknown-linux-gnu"
63+
define dso_local void @_Z1fv() optnone noinline personality i8* bitcast (void ()* @throw to i8*) {
64+
invoke void @throw()
65+
to label %unreachable unwind label %lpad
66+
lpad:
67+
%1 = landingpad { i8*, i32 }
68+
cleanup
69+
resume { i8*, i32 } %1
70+
unreachable:
71+
unreachable
72+
}
73+
declare void @throw()
74+
75+
#--- malloc.s
76+
.globl malloc
77+
malloc:
78+
retq
79+
80+
#--- emutls.s
81+
.globl __emutls_get_address
82+
__emutls_get_address:
83+
jmp malloc@plt
84+
85+
#--- usetls.ll
86+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
87+
target triple = "x86_64-unknown-linux-gnu"
88+
@x = dso_local thread_local global i32 0, align 4
89+
define dso_local i32 @f() {
90+
%loaded = load i32, ptr @x, align 4
91+
ret i32 %loaded
92+
}

0 commit comments

Comments
 (0)