aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Object/ELFObjectFile.cpp
diff options
context:
space:
mode:
authorFangrui Song <i@maskray.me>2023-05-16 09:22:21 -0700
committerFangrui Song <i@maskray.me>2023-05-16 09:22:21 -0700
commit9e37a7bd1f38fed4e00704d561b3897fe8915c4c (patch)
tree45cc0eb991a6d301bb4954b16cd38e16f918a6bc /llvm/lib/Object/ELFObjectFile.cpp
parent724f4a5bac25e2f8ec327559621062cf9ec205fb (diff)
downloadllvm-9e37a7bd1f38fed4e00704d561b3897fe8915c4c.zip
llvm-9e37a7bd1f38fed4e00704d561b3897fe8915c4c.tar.gz
llvm-9e37a7bd1f38fed4e00704d561b3897fe8915c4c.tar.bz2
[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 https://github.com/llvm/llvm-project/issues/62537 Reviewed By: bd1976llvm Differential Revision: https://reviews.llvm.org/D149817
Diffstat (limited to 'llvm/lib/Object/ELFObjectFile.cpp')
-rw-r--r--llvm/lib/Object/ELFObjectFile.cpp85
1 files changed, 53 insertions, 32 deletions
diff --git a/llvm/lib/Object/ELFObjectFile.cpp b/llvm/lib/Object/ELFObjectFile.cpp
index 204ba6b..da36da3 100644
--- a/llvm/lib/Object/ELFObjectFile.cpp
+++ b/llvm/lib/Object/ELFObjectFile.cpp
@@ -601,20 +601,21 @@ void ELFObjectFileBase::setARMSubArch(Triple &TheTriple) const {
TheTriple.setArchName(Triple);
}
-std::vector<std::pair<std::optional<DataRefImpl>, uint64_t>>
-ELFObjectFileBase::getPltAddresses() const {
+std::vector<ELFPltEntry> ELFObjectFileBase::getPltEntries() const {
std::string Err;
const auto Triple = makeTriple();
const auto *T = TargetRegistry::lookupTarget(Triple.str(), Err);
if (!T)
return {};
- uint64_t JumpSlotReloc = 0;
+ uint32_t JumpSlotReloc = 0, GlobDatReloc = 0;
switch (Triple.getArch()) {
case Triple::x86:
JumpSlotReloc = ELF::R_386_JUMP_SLOT;
+ GlobDatReloc = ELF::R_386_GLOB_DAT;
break;
case Triple::x86_64:
JumpSlotReloc = ELF::R_X86_64_JUMP_SLOT;
+ GlobDatReloc = ELF::R_X86_64_GLOB_DAT;
break;
case Triple::aarch64:
case Triple::aarch64_be:
@@ -628,7 +629,8 @@ ELFObjectFileBase::getPltAddresses() const {
T->createMCInstrAnalysis(MII.get()));
if (!MIA)
return {};
- std::optional<SectionRef> Plt, RelaPlt;
+ std::vector<std::pair<uint64_t, uint64_t>> PltEntries;
+ std::optional<SectionRef> RelaPlt, RelaDyn;
uint64_t GotBaseVA = 0;
for (const SectionRef &Section : sections()) {
Expected<StringRef> NameOrErr = Section.getName();
@@ -638,47 +640,66 @@ ELFObjectFileBase::getPltAddresses() const {
}
StringRef Name = *NameOrErr;
- if (Name == ".plt")
- Plt = Section;
- else if (Name == ".rela.plt" || Name == ".rel.plt")
+ if (Name == ".rela.plt" || Name == ".rel.plt") {
RelaPlt = Section;
- else if (Name == ".got.plt")
+ } else if (Name == ".rela.dyn" || Name == ".rel.dyn") {
+ RelaDyn = Section;
+ } else if (Name == ".got.plt") {
GotBaseVA = Section.getAddress();
+ } else if (Name == ".plt" || Name == ".plt.got") {
+ Expected<StringRef> PltContents = Section.getContents();
+ if (!PltContents) {
+ consumeError(PltContents.takeError());
+ return {};
+ }
+ llvm::append_range(
+ PltEntries,
+ MIA->findPltEntries(Section.getAddress(),
+ arrayRefFromStringRef(*PltContents), Triple));
+ }
}
- if (!Plt || !RelaPlt)
- return {};
- Expected<StringRef> PltContents = Plt->getContents();
- if (!PltContents) {
- consumeError(PltContents.takeError());
- return {};
- }
- auto PltEntries = MIA->findPltEntries(
- Plt->getAddress(), arrayRefFromStringRef(*PltContents), Triple);
// Build a map from GOT entry virtual address to PLT entry virtual address.
DenseMap<uint64_t, uint64_t> GotToPlt;
- for (auto [Plt, GotPltEntry] : PltEntries) {
+ for (auto [Plt, GotPlt] : PltEntries) {
+ uint64_t GotPltEntry = GotPlt;
// An x86-32 PIC PLT uses jmp DWORD PTR [ebx-offset]. Add
// _GLOBAL_OFFSET_TABLE_ (EBX) to get the .got.plt (or .got) entry address.
- if (static_cast<int64_t>(GotPltEntry) < 0 && getEMachine() == ELF::EM_386)
- GotPltEntry = ~GotPltEntry + GotBaseVA;
+ // See X86MCTargetDesc.cpp:findPltEntries for the 1 << 32 bit.
+ if (GotPltEntry & (uint64_t(1) << 32) && getEMachine() == ELF::EM_386)
+ GotPltEntry = static_cast<int32_t>(GotPltEntry) + GotBaseVA;
GotToPlt.insert(std::make_pair(GotPltEntry, Plt));
}
+
// Find the relocations in the dynamic relocation table that point to
// locations in the GOT for which we know the corresponding PLT entry.
- std::vector<std::pair<std::optional<DataRefImpl>, uint64_t>> Result;
- for (const auto &Relocation : RelaPlt->relocations()) {
- if (Relocation.getType() != JumpSlotReloc)
- continue;
- auto PltEntryIter = GotToPlt.find(Relocation.getOffset());
- if (PltEntryIter != GotToPlt.end()) {
- symbol_iterator Sym = Relocation.getSymbol();
- if (Sym == symbol_end())
- Result.emplace_back(std::nullopt, PltEntryIter->second);
- else
- Result.emplace_back(Sym->getRawDataRefImpl(), PltEntryIter->second);
+ std::vector<ELFPltEntry> Result;
+ auto handleRels = [&](iterator_range<relocation_iterator> Rels,
+ uint32_t RelType, StringRef PltSec) {
+ for (const auto &R : Rels) {
+ if (R.getType() != RelType)
+ continue;
+ auto PltEntryIter = GotToPlt.find(R.getOffset());
+ if (PltEntryIter != GotToPlt.end()) {
+ symbol_iterator Sym = R.getSymbol();
+ if (Sym == symbol_end())
+ Result.push_back(
+ ELFPltEntry{PltSec, std::nullopt, PltEntryIter->second});
+ else
+ Result.push_back(ELFPltEntry{PltSec, Sym->getRawDataRefImpl(),
+ PltEntryIter->second});
+ }
}
- }
+ };
+
+ if (RelaPlt)
+ handleRels(RelaPlt->relocations(), JumpSlotReloc, ".plt");
+
+ // If a symbol needing a PLT entry also needs a GLOB_DAT relocation, GNU ld's
+ // x86 port places the PLT entry in the .plt.got section.
+ if (RelaDyn)
+ handleRels(RelaDyn->relocations(), GlobDatReloc, ".plt.got");
+
return Result;
}