Skip to content

Commit 9e37a7b

Browse files
committed
[llvm-objdump][X86] Add @plt symbols for .plt.got
If a symbol needs both JUMP_SLOT and GLOB_DAT relocations, there is a minor linker optimization to keep just GLOB_DAT. This optimization is only implemented by GNU ld's x86 port and mold. https://maskray.me/blog/2021-08-29-all-about-global-offset-table#combining-.got-and-.got.plt With the optimizing, the PLT entry is placed in .plt.got and the associated GOTPLT entry is placed in .got (ld.bfd -z now) or .got.plt (ld.bfd -z lazy). The relocation is in .rel[a].dyn. This patch synthesizes `symbol@plt` labels for these .plt.got entries. Example: ``` cat > a.s <<e .globl _start; _start: mov combined0@gotpcrel(%rip), %rax; mov combined1@gotpcrel(%rip), %rax call combined0@plt; call combined1@plt call foo0@plt; call foo1@plt e cat > b.s <<e .globl foo0, foo1, combined0, combined1 foo0: foo1: combined0: combined1: e gcc -fuse-ld=bfd -shared b.s -o b.so gcc -fuse-ld=bfd -pie -nostdlib a.s b.so -o a ``` ``` Disassembly of section .plt: 0000000000001000 <.plt>: 1000: ff 35 ea 1f 00 00 pushq 0x1fea(%rip) # 0x2ff0 <_GLOBAL_OFFSET_TABLE_+0x8> 1006: ff 25 ec 1f 00 00 jmpq *0x1fec(%rip) # 0x2ff8 <_GLOBAL_OFFSET_TABLE_+0x10> 100c: 0f 1f 40 00 nopl (%rax) 0000000000001010 <foo1@plt>: 1010: ff 25 ea 1f 00 00 jmpq *0x1fea(%rip) # 0x3000 <_GLOBAL_OFFSET_TABLE_+0x18> 1016: 68 00 00 00 00 pushq $0x0 101b: e9 e0 ff ff ff jmp 0x1000 <.plt> 0000000000001020 <foo0@plt>: 1020: ff 25 e2 1f 00 00 jmpq *0x1fe2(%rip) # 0x3008 <_GLOBAL_OFFSET_TABLE_+0x20> 1026: 68 01 00 00 00 pushq $0x1 102b: e9 d0 ff ff ff jmp 0x1000 <.plt> Disassembly of section .plt.got: 0000000000001030 <combined0@plt>: 1030: ff 25 a2 1f 00 00 jmpq *0x1fa2(%rip) # 0x2fd8 <foo1+0x2fd8> 1036: 66 90 nop 0000000000001038 <combined1@plt>: 1038: ff 25 a2 1f 00 00 jmpq *0x1fa2(%rip) # 0x2fe0 <foo1+0x2fe0> 103e: 66 90 nop ``` For x86-32, with -z now, if we remove `foo0` and `foo1`, the absence of regular PLT will cause GNU ld to omit .got.plt, and our code cannot synthesize @plt labels. This is an extreme corner case that almost never happens in practice (to trigger the case, ensure every PLT symbol has been taken address). To fix it, we can get the `_GLOBAL_OFFSET_TABLE_` symbol value, but the complexity is not worth it. Close llvm#62537 Reviewed By: bd1976llvm Differential Revision: https://reviews.llvm.org/D149817
1 parent 724f4a5 commit 9e37a7b

File tree

6 files changed

+310
-52
lines changed

6 files changed

+310
-52
lines changed

llvm/include/llvm/Object/ELFObjectFile.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ extern const llvm::EnumEntry<unsigned> ElfSymbolTypes[NumElfSymbolTypes];
4848

4949
class elf_symbol_iterator;
5050

51+
struct ELFPltEntry {
52+
StringRef Section;
53+
std::optional<DataRefImpl> Symbol;
54+
uint64_t Address;
55+
};
56+
5157
class ELFObjectFileBase : public ObjectFile {
5258
friend class ELFRelocationRef;
5359
friend class ELFSectionRef;
@@ -97,8 +103,7 @@ class ELFObjectFileBase : public ObjectFile {
97103

98104
virtual uint16_t getEMachine() const = 0;
99105

100-
std::vector<std::pair<std::optional<DataRefImpl>, uint64_t>>
101-
getPltAddresses() const;
106+
std::vector<ELFPltEntry> getPltEntries() const;
102107

103108
/// Returns a vector containing a symbol version for each dynamic symbol.
104109
/// Returns an empty vector if version sections do not exist.

llvm/lib/Object/ELFObjectFile.cpp

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -601,20 +601,21 @@ void ELFObjectFileBase::setARMSubArch(Triple &TheTriple) const {
601601
TheTriple.setArchName(Triple);
602602
}
603603

604-
std::vector<std::pair<std::optional<DataRefImpl>, uint64_t>>
605-
ELFObjectFileBase::getPltAddresses() const {
604+
std::vector<ELFPltEntry> ELFObjectFileBase::getPltEntries() const {
606605
std::string Err;
607606
const auto Triple = makeTriple();
608607
const auto *T = TargetRegistry::lookupTarget(Triple.str(), Err);
609608
if (!T)
610609
return {};
611-
uint64_t JumpSlotReloc = 0;
610+
uint32_t JumpSlotReloc = 0, GlobDatReloc = 0;
612611
switch (Triple.getArch()) {
613612
case Triple::x86:
614613
JumpSlotReloc = ELF::R_386_JUMP_SLOT;
614+
GlobDatReloc = ELF::R_386_GLOB_DAT;
615615
break;
616616
case Triple::x86_64:
617617
JumpSlotReloc = ELF::R_X86_64_JUMP_SLOT;
618+
GlobDatReloc = ELF::R_X86_64_GLOB_DAT;
618619
break;
619620
case Triple::aarch64:
620621
case Triple::aarch64_be:
@@ -628,7 +629,8 @@ ELFObjectFileBase::getPltAddresses() const {
628629
T->createMCInstrAnalysis(MII.get()));
629630
if (!MIA)
630631
return {};
631-
std::optional<SectionRef> Plt, RelaPlt;
632+
std::vector<std::pair<uint64_t, uint64_t>> PltEntries;
633+
std::optional<SectionRef> RelaPlt, RelaDyn;
632634
uint64_t GotBaseVA = 0;
633635
for (const SectionRef &Section : sections()) {
634636
Expected<StringRef> NameOrErr = Section.getName();
@@ -638,47 +640,66 @@ ELFObjectFileBase::getPltAddresses() const {
638640
}
639641
StringRef Name = *NameOrErr;
640642

641-
if (Name == ".plt")
642-
Plt = Section;
643-
else if (Name == ".rela.plt" || Name == ".rel.plt")
643+
if (Name == ".rela.plt" || Name == ".rel.plt") {
644644
RelaPlt = Section;
645-
else if (Name == ".got.plt")
645+
} else if (Name == ".rela.dyn" || Name == ".rel.dyn") {
646+
RelaDyn = Section;
647+
} else if (Name == ".got.plt") {
646648
GotBaseVA = Section.getAddress();
649+
} else if (Name == ".plt" || Name == ".plt.got") {
650+
Expected<StringRef> PltContents = Section.getContents();
651+
if (!PltContents) {
652+
consumeError(PltContents.takeError());
653+
return {};
654+
}
655+
llvm::append_range(
656+
PltEntries,
657+
MIA->findPltEntries(Section.getAddress(),
658+
arrayRefFromStringRef(*PltContents), Triple));
659+
}
647660
}
648-
if (!Plt || !RelaPlt)
649-
return {};
650-
Expected<StringRef> PltContents = Plt->getContents();
651-
if (!PltContents) {
652-
consumeError(PltContents.takeError());
653-
return {};
654-
}
655-
auto PltEntries = MIA->findPltEntries(
656-
Plt->getAddress(), arrayRefFromStringRef(*PltContents), Triple);
657661

658662
// Build a map from GOT entry virtual address to PLT entry virtual address.
659663
DenseMap<uint64_t, uint64_t> GotToPlt;
660-
for (auto [Plt, GotPltEntry] : PltEntries) {
664+
for (auto [Plt, GotPlt] : PltEntries) {
665+
uint64_t GotPltEntry = GotPlt;
661666
// An x86-32 PIC PLT uses jmp DWORD PTR [ebx-offset]. Add
662667
// _GLOBAL_OFFSET_TABLE_ (EBX) to get the .got.plt (or .got) entry address.
663-
if (static_cast<int64_t>(GotPltEntry) < 0 && getEMachine() == ELF::EM_386)
664-
GotPltEntry = ~GotPltEntry + GotBaseVA;
668+
// See X86MCTargetDesc.cpp:findPltEntries for the 1 << 32 bit.
669+
if (GotPltEntry & (uint64_t(1) << 32) && getEMachine() == ELF::EM_386)
670+
GotPltEntry = static_cast<int32_t>(GotPltEntry) + GotBaseVA;
665671
GotToPlt.insert(std::make_pair(GotPltEntry, Plt));
666672
}
673+
667674
// Find the relocations in the dynamic relocation table that point to
668675
// locations in the GOT for which we know the corresponding PLT entry.
669-
std::vector<std::pair<std::optional<DataRefImpl>, uint64_t>> Result;
670-
for (const auto &Relocation : RelaPlt->relocations()) {
671-
if (Relocation.getType() != JumpSlotReloc)
672-
continue;
673-
auto PltEntryIter = GotToPlt.find(Relocation.getOffset());
674-
if (PltEntryIter != GotToPlt.end()) {
675-
symbol_iterator Sym = Relocation.getSymbol();
676-
if (Sym == symbol_end())
677-
Result.emplace_back(std::nullopt, PltEntryIter->second);
678-
else
679-
Result.emplace_back(Sym->getRawDataRefImpl(), PltEntryIter->second);
676+
std::vector<ELFPltEntry> Result;
677+
auto handleRels = [&](iterator_range<relocation_iterator> Rels,
678+
uint32_t RelType, StringRef PltSec) {
679+
for (const auto &R : Rels) {
680+
if (R.getType() != RelType)
681+
continue;
682+
auto PltEntryIter = GotToPlt.find(R.getOffset());
683+
if (PltEntryIter != GotToPlt.end()) {
684+
symbol_iterator Sym = R.getSymbol();
685+
if (Sym == symbol_end())
686+
Result.push_back(
687+
ELFPltEntry{PltSec, std::nullopt, PltEntryIter->second});
688+
else
689+
Result.push_back(ELFPltEntry{PltSec, Sym->getRawDataRefImpl(),
690+
PltEntryIter->second});
691+
}
680692
}
681-
}
693+
};
694+
695+
if (RelaPlt)
696+
handleRels(RelaPlt->relocations(), JumpSlotReloc, ".plt");
697+
698+
// If a symbol needing a PLT entry also needs a GLOB_DAT relocation, GNU ld's
699+
// x86 port places the PLT entry in the .plt.got section.
700+
if (RelaDyn)
701+
handleRels(RelaDyn->relocations(), GlobDatReloc, ".plt.got");
702+
682703
return Result;
683704
}
684705

llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,11 @@ findX86PltEntries(uint64_t PltSectionVA, ArrayRef<uint8_t> PltContents) {
577577
if (PltContents[Byte] == 0xff && PltContents[Byte + 1] == 0xa3) {
578578
// The jmp instruction at the beginning of each PLT entry jumps to the
579579
// address of the base of the .got.plt section plus the immediate.
580+
// Set the 1 << 32 bit to let ELFObjectFileBase::getPltEntries convert the
581+
// offset to an address. Imm may be a negative int32_t if the GOT entry is
582+
// in .got.
580583
uint32_t Imm = support::endian::read32le(PltContents.data() + Byte + 2);
581-
Result.emplace_back(PltSectionVA + Byte, ~static_cast<uint64_t>(Imm));
584+
Result.emplace_back(PltSectionVA + Byte, Imm | (uint64_t(1) << 32));
582585
Byte += 6;
583586
} else if (PltContents[Byte] == 0xff && PltContents[Byte + 1] == 0x25) {
584587
// The jmp instruction at the beginning of each PLT entry jumps to the
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
## If a symbol needing a PLT entry also needs a GLOB_DAT relocation, GNU ld's
2+
## x86 port places the PLT entry in .plt.got, relocated by a GLOB_DAT. The
3+
## JUMP_SLOT relocation is unused in this case.
4+
## Test that we synthesize @plt symbols for such PLT entries.
5+
# RUN: yaml2obj --docnum=1 %s -o %t.x86-64
6+
# RUN: llvm-objdump -d %t.x86-64 | FileCheck %s --check-prefix=64
7+
# RUN: yaml2obj --docnum=2 %s -o %t.x86-32
8+
# RUN: llvm-objdump -d %t.x86-32 | FileCheck %s --check-prefix=32
9+
10+
# 64: Disassembly of section .plt:
11+
# 64-EMPTY:
12+
# 64-NEXT: <.plt>:
13+
# 64: <foo1@plt>:
14+
# 64: <foo0@plt>:
15+
16+
# 64: Disassembly of section .plt.got:
17+
# 64-EMPTY:
18+
# 64-NEXT: <combined0@plt>:
19+
# 64: <combined1@plt>:
20+
21+
# 64: <_start>:
22+
# 64-NEXT: movq {{.*}}(%rip), %rax
23+
# 64-NEXT: movq {{.*}}(%rip), %rax
24+
# 64-NEXT: callq {{.*}} <combined0@plt>
25+
# 64-NEXT: callq {{.*}} <combined1@plt>
26+
# 64-NEXT: callq {{.*}} <foo0@plt>
27+
# 64-NEXT: callq {{.*}} <foo1@plt>
28+
29+
# 32: Disassembly of section .plt:
30+
# 32-EMPTY:
31+
# 32-NEXT: <.plt>:
32+
# 32: <foo1@plt>:
33+
# 32: <foo0@plt>:
34+
35+
# 32: Disassembly of section .plt.got:
36+
# 32-EMPTY:
37+
# 32-NEXT: <combined0@plt>:
38+
# 32: <combined1@plt>:
39+
40+
# 32: <_start>:
41+
# 32-NEXT: movl -0x8(%eax), %eax
42+
# 32-NEXT: movl -0x4(%eax), %eax
43+
# 32-NEXT: calll {{.*}} <combined0@plt>
44+
# 32-NEXT: calll {{.*}} <combined1@plt>
45+
# 32-NEXT: calll {{.*}} <foo0@plt>
46+
# 32-NEXT: calll {{.*}} <foo1@plt>
47+
48+
## %t.x86-64 is linked with ld.bfd -pie -z now a.o b.so and
49+
## doesn't have .got.plt.
50+
## %t.x86-32 is linked with ld.bfd -pie -z lazy a.o b.so and
51+
## has .got.plt.
52+
53+
## a.s
54+
## .globl _start; _start:
55+
## mov combined0@gotpcrel(%rip), %rax # movl combined0@GOT(%eax), %eax for x86-32
56+
## mov combined1@gotpcrel(%rip), %rax # movl combined1@GOT(%eax), %eax for x86-32
57+
## call combined0@plt
58+
## call combined1@plt
59+
## call foo0@plt
60+
## call foo1@plt
61+
62+
## b.s
63+
## .globl foo0, foo1, combined0, combined1
64+
## foo0: foo1: combined0: combined1:
65+
66+
--- !ELF
67+
FileHeader:
68+
Class: ELFCLASS64
69+
Data: ELFDATA2LSB
70+
Type: ET_DYN
71+
Machine: EM_X86_64
72+
Entry: 0x1040
73+
Sections:
74+
- Name: .rela.dyn
75+
Type: SHT_RELA
76+
Flags: [ SHF_ALLOC ]
77+
Address: 0x340
78+
Link: .dynsym
79+
AddressAlign: 0x8
80+
Relocations:
81+
- Offset: 0x2FF0
82+
Symbol: combined0
83+
Type: R_X86_64_GLOB_DAT
84+
- Offset: 0x2FF8
85+
Symbol: combined1
86+
Type: R_X86_64_GLOB_DAT
87+
- Name: .rela.plt
88+
Type: SHT_RELA
89+
Flags: [ SHF_ALLOC, SHF_INFO_LINK ]
90+
Address: 0x370
91+
Link: .dynsym
92+
AddressAlign: 0x8
93+
Info: .got
94+
Relocations:
95+
- Offset: 0x2FE0
96+
Symbol: foo1
97+
Type: R_X86_64_JUMP_SLOT
98+
- Offset: 0x2FE8
99+
Symbol: foo0
100+
Type: R_X86_64_JUMP_SLOT
101+
- Name: .plt
102+
Type: SHT_PROGBITS
103+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
104+
Address: 0x1000
105+
AddressAlign: 0x10
106+
EntSize: 0x10
107+
Offset: 0x1000
108+
Content: FF35CA1F0000FF25CC1F00000F1F4000FF25CA1F00006800000000E9E0FFFFFFFF25C21F00006801000000E9D0FFFFFF
109+
- Name: .plt.got
110+
Type: SHT_PROGBITS
111+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
112+
Address: 0x1030
113+
AddressAlign: 0x8
114+
EntSize: 0x8
115+
Content: FF25BA1F00006690FF25BA1F00006690
116+
- Name: .text
117+
Type: SHT_PROGBITS
118+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
119+
Address: 0x1040
120+
AddressAlign: 0x1
121+
Content: 488B05A91F0000488B05AA1F0000E8DDFFFFFFE8E0FFFFFFE8C3FFFFFFE8AEFFFFFF
122+
- Name: .got
123+
Type: SHT_PROGBITS
124+
Flags: [ SHF_WRITE, SHF_ALLOC ]
125+
Address: 0x2FC8
126+
AddressAlign: 0x8
127+
EntSize: 0x8
128+
Content: '682E000000000000000000000000000000000000000000001610000000000000261000000000000000000000000000000000000000000000'
129+
Symbols:
130+
- Name: _start
131+
Section: .text
132+
Binding: STB_GLOBAL
133+
Value: 0x1040
134+
DynamicSymbols:
135+
- Name: foo1
136+
Binding: STB_GLOBAL
137+
- Name: foo0
138+
Binding: STB_GLOBAL
139+
- Name: combined0
140+
Binding: STB_GLOBAL
141+
- Name: combined1
142+
Binding: STB_GLOBAL
143+
...
144+
145+
--- !ELF
146+
FileHeader:
147+
Class: ELFCLASS32
148+
Data: ELFDATA2LSB
149+
Type: ET_DYN
150+
Machine: EM_386
151+
Entry: 0x1040
152+
Sections:
153+
- Name: .rel.dyn
154+
Type: SHT_REL
155+
Flags: [ SHF_ALLOC ]
156+
Address: 0x218
157+
Link: .dynsym
158+
AddressAlign: 0x4
159+
Relocations:
160+
- Offset: 0x2FEC
161+
Symbol: combined0
162+
Type: R_386_GLOB_DAT
163+
- Offset: 0x2FF0
164+
Symbol: combined1
165+
Type: R_386_GLOB_DAT
166+
- Name: .rel.plt
167+
Type: SHT_REL
168+
Flags: [ SHF_ALLOC, SHF_INFO_LINK ]
169+
Address: 0x228
170+
Link: .dynsym
171+
AddressAlign: 0x4
172+
Info: .got.plt
173+
Relocations:
174+
- Offset: 0x3000
175+
Symbol: foo1
176+
Type: R_386_JUMP_SLOT
177+
- Offset: 0x3004
178+
Symbol: foo0
179+
Type: R_386_JUMP_SLOT
180+
- Name: .plt
181+
Type: SHT_PROGBITS
182+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
183+
Address: 0x1000
184+
AddressAlign: 0x10
185+
EntSize: 0x4
186+
Offset: 0x1000
187+
Content: FFB304000000FFA30800000000000000FFA30C0000006800000000E9E0FFFFFFFFA3100000006808000000E9D0FFFFFF
188+
- Name: .plt.got
189+
Type: SHT_PROGBITS
190+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
191+
Address: 0x1030
192+
AddressAlign: 0x8
193+
EntSize: 0x8
194+
Content: FFA3F8FFFFFF6690FFA3FCFFFFFF6690
195+
- Name: .text
196+
Type: SHT_PROGBITS
197+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
198+
Address: 0x1040
199+
AddressAlign: 0x1
200+
Content: 8B80F8FFFFFF8B80FCFFFFFFE8DFFFFFFFE8E2FFFFFFE8C5FFFFFFE8B0FFFFFF
201+
- Name: .got
202+
Type: SHT_PROGBITS
203+
Flags: [ SHF_WRITE, SHF_ALLOC ]
204+
Address: 0x2FEC
205+
AddressAlign: 0x4
206+
EntSize: 0x4
207+
Content: '0000000000000000'
208+
- Name: .got.plt
209+
Type: SHT_PROGBITS
210+
Flags: [ SHF_WRITE, SHF_ALLOC ]
211+
Address: 0x2FF4
212+
AddressAlign: 0x4
213+
EntSize: 0x4
214+
Content: 442F000000000000000000001610000026100000
215+
Symbols:
216+
- Name: _GLOBAL_OFFSET_TABLE_
217+
Type: STT_OBJECT
218+
Section: .got.plt
219+
Value: 0x2FF4
220+
- Name: _start
221+
Section: .text
222+
Binding: STB_GLOBAL
223+
Value: 0x1040
224+
DynamicSymbols:
225+
- Name: combined0
226+
Binding: STB_GLOBAL
227+
- Name: foo1
228+
Binding: STB_GLOBAL
229+
- Name: foo0
230+
Binding: STB_GLOBAL
231+
- Name: combined1
232+
Binding: STB_GLOBAL
233+
...

0 commit comments

Comments
 (0)