Skip to content

Commit 2f37a22

Browse files
authored
[llvm-objdump] -r: support CREL
Extract the llvm-readelf decoder to `decodeCrel` (#91280) and reuse it for llvm-objdump. Because the section representation of LLVMObject (`SectionRef`) is 64-bit, insufficient to hold all decoder states, `section_rel_begin` is modified to decode CREL eagerly and hold the decoded relocations inside ELFObjectFile<ELFT>. The test is adapted from llvm/test/tools/llvm-readobj/ELF/crel.test. Pull Request: #97382
1 parent 12e47aa commit 2f37a22

File tree

8 files changed

+372
-45
lines changed

8 files changed

+372
-45
lines changed

llvm/include/llvm/Object/ELF.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
#include "llvm/BinaryFormat/ELF.h"
2222
#include "llvm/Object/ELFTypes.h"
2323
#include "llvm/Object/Error.h"
24+
#include "llvm/Support/DataExtractor.h"
2425
#include "llvm/Support/Error.h"
2526
#include <cassert>
2627
#include <cstddef>
2728
#include <cstdint>
2829
#include <limits>
30+
#include <type_traits>
2931
#include <utility>
3032

3133
namespace llvm {
@@ -207,6 +209,47 @@ bool isSectionInSegment(const typename ELFT::Phdr &Phdr,
207209
checkSectionVMA<ELFT>(Phdr, Sec);
208210
}
209211

212+
// HdrHandler is called once with the number of relocations and whether the
213+
// relocations have addends. EntryHandler is called once per decoded relocation.
214+
template <bool Is64>
215+
Error decodeCrel(
216+
ArrayRef<uint8_t> Content,
217+
function_ref<void(uint64_t /*relocation count*/, bool /*explicit addends*/)>
218+
HdrHandler,
219+
function_ref<void(Elf_Crel_Impl<Is64>)> EntryHandler) {
220+
DataExtractor Data(Content, true, 8); // endian and address size are unused
221+
DataExtractor::Cursor Cur(0);
222+
const uint64_t Hdr = Data.getULEB128(Cur);
223+
size_t Count = Hdr / 8;
224+
const size_t FlagBits = Hdr & ELF::CREL_HDR_ADDEND ? 3 : 2;
225+
const size_t Shift = Hdr % ELF::CREL_HDR_ADDEND;
226+
using uint = typename Elf_Crel_Impl<Is64>::uint;
227+
uint Offset = 0, Addend = 0;
228+
HdrHandler(Count, Hdr & ELF::CREL_HDR_ADDEND);
229+
uint32_t SymIdx = 0, Type = 0;
230+
for (; Count; --Count) {
231+
// The delta offset and flags member may be larger than uint64_t. Special
232+
// case the first byte (2 or 3 flag bits; the rest are offset bits). Other
233+
// ULEB128 bytes encode the remaining delta offset bits.
234+
const uint8_t B = Data.getU8(Cur);
235+
Offset += B >> FlagBits;
236+
if (B >= 0x80)
237+
Offset += (Data.getULEB128(Cur) << (7 - FlagBits)) - (0x80 >> FlagBits);
238+
// Delta symidx/type/addend members (SLEB128).
239+
if (B & 1)
240+
SymIdx += Data.getSLEB128(Cur);
241+
if (B & 2)
242+
Type += Data.getSLEB128(Cur);
243+
if (B & 4 & Hdr)
244+
Addend += Data.getSLEB128(Cur);
245+
if (!Cur)
246+
break;
247+
EntryHandler(
248+
{Offset << Shift, SymIdx, Type, std::make_signed_t<uint>(Addend)});
249+
}
250+
return Cur.takeError();
251+
}
252+
210253
template <class ELFT>
211254
class ELFFile {
212255
public:

llvm/include/llvm/Object/ELFObjectFile.h

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "llvm/Support/ELFAttributes.h"
3030
#include "llvm/Support/Error.h"
3131
#include "llvm/Support/ErrorHandling.h"
32+
#include "llvm/Support/LEB128.h"
3233
#include "llvm/Support/MemoryBufferRef.h"
3334
#include "llvm/Support/ScopedPrinter.h"
3435
#include "llvm/TargetParser/SubtargetFeature.h"
@@ -122,6 +123,8 @@ class ELFObjectFileBase : public ObjectFile {
122123
Expected<std::vector<BBAddrMap>>
123124
readBBAddrMap(std::optional<unsigned> TextSectionIndex = std::nullopt,
124125
std::vector<PGOAnalysisMap> *PGOAnalyses = nullptr) const;
126+
127+
StringRef getCrelDecodeProblem(SectionRef Sec) const;
125128
};
126129

127130
class ELFSectionRef : public SectionRef {
@@ -292,6 +295,10 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
292295
const Elf_Shdr *DotSymtabSec = nullptr; // Symbol table section.
293296
const Elf_Shdr *DotSymtabShndxSec = nullptr; // SHT_SYMTAB_SHNDX section.
294297

298+
// Hold CREL relocations for SectionRef::relocations().
299+
mutable SmallVector<SmallVector<Elf_Crel, 0>, 0> Crels;
300+
mutable SmallVector<std::string, 0> CrelDecodeProblems;
301+
295302
Error initContent() override;
296303

297304
void moveSymbolNext(DataRefImpl &Symb) const override;
@@ -446,6 +453,7 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
446453

447454
const Elf_Rel *getRel(DataRefImpl Rel) const;
448455
const Elf_Rela *getRela(DataRefImpl Rela) const;
456+
Elf_Crel getCrel(DataRefImpl Crel) const;
449457

450458
Expected<const Elf_Sym *> getSymbol(DataRefImpl Sym) const {
451459
return EF.template getEntry<Elf_Sym>(Sym.d.a, Sym.d.b);
@@ -499,6 +507,8 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
499507
bool isRelocatableObject() const override;
500508

501509
void createFakeSections() { EF.createFakeSections(); }
510+
511+
StringRef getCrelDecodeProblem(DataRefImpl Sec) const;
502512
};
503513

504514
using ELF32LEObjectFile = ELFObjectFile<ELF32LE>;
@@ -1022,6 +1032,24 @@ ELFObjectFile<ELFT>::section_rel_begin(DataRefImpl Sec) const {
10221032
uintptr_t SHT = reinterpret_cast<uintptr_t>((*SectionsOrErr).begin());
10231033
RelData.d.a = (Sec.p - SHT) / EF.getHeader().e_shentsize;
10241034
RelData.d.b = 0;
1035+
if (reinterpret_cast<const Elf_Shdr *>(Sec.p)->sh_type == ELF::SHT_CREL) {
1036+
if (RelData.d.a + 1 > Crels.size())
1037+
Crels.resize(RelData.d.a + 1);
1038+
auto &Crel = Crels[RelData.d.a];
1039+
if (Crel.empty()) {
1040+
ArrayRef<uint8_t> Content = cantFail(getSectionContents(Sec));
1041+
size_t I = 0;
1042+
Error Err = decodeCrel<ELFT::Is64Bits>(
1043+
Content, [&](uint64_t Count, bool) { Crel.resize(Count); },
1044+
[&](Elf_Crel Crel) { Crels[RelData.d.a][I++] = Crel; });
1045+
if (Err) {
1046+
Crel.assign(1, Elf_Crel{0, 0, 0, 0});
1047+
if (RelData.d.a + 1 > CrelDecodeProblems.size())
1048+
CrelDecodeProblems.resize(RelData.d.a + 1);
1049+
CrelDecodeProblems[RelData.d.a] = toString(std::move(Err));
1050+
}
1051+
}
1052+
}
10251053
return relocation_iterator(RelocationRef(RelData, this));
10261054
}
10271055

@@ -1030,9 +1058,13 @@ relocation_iterator
10301058
ELFObjectFile<ELFT>::section_rel_end(DataRefImpl Sec) const {
10311059
const Elf_Shdr *S = reinterpret_cast<const Elf_Shdr *>(Sec.p);
10321060
relocation_iterator Begin = section_rel_begin(Sec);
1061+
DataRefImpl RelData = Begin->getRawDataRefImpl();
1062+
if (S->sh_type == ELF::SHT_CREL) {
1063+
RelData.d.b = Crels[RelData.d.a].size();
1064+
return relocation_iterator(RelocationRef(RelData, this));
1065+
}
10331066
if (S->sh_type != ELF::SHT_RELA && S->sh_type != ELF::SHT_REL)
10341067
return Begin;
1035-
DataRefImpl RelData = Begin->getRawDataRefImpl();
10361068
const Elf_Shdr *RelSec = getRelSection(RelData);
10371069

10381070
// Error check sh_link here so that getRelocationSymbol can just use it.
@@ -1050,7 +1082,7 @@ Expected<section_iterator>
10501082
ELFObjectFile<ELFT>::getRelocatedSection(DataRefImpl Sec) const {
10511083
const Elf_Shdr *EShdr = getSection(Sec);
10521084
uintX_t Type = EShdr->sh_type;
1053-
if (Type != ELF::SHT_REL && Type != ELF::SHT_RELA)
1085+
if (Type != ELF::SHT_REL && Type != ELF::SHT_RELA && Type != ELF::SHT_CREL)
10541086
return section_end();
10551087

10561088
Expected<const Elf_Shdr *> SecOrErr = EF.getSection(EShdr->sh_info);
@@ -1070,7 +1102,9 @@ symbol_iterator
10701102
ELFObjectFile<ELFT>::getRelocationSymbol(DataRefImpl Rel) const {
10711103
uint32_t symbolIdx;
10721104
const Elf_Shdr *sec = getRelSection(Rel);
1073-
if (sec->sh_type == ELF::SHT_REL)
1105+
if (sec->sh_type == ELF::SHT_CREL)
1106+
symbolIdx = getCrel(Rel).r_symidx;
1107+
else if (sec->sh_type == ELF::SHT_REL)
10741108
symbolIdx = getRel(Rel)->getSymbol(EF.isMips64EL());
10751109
else
10761110
symbolIdx = getRela(Rel)->getSymbol(EF.isMips64EL());
@@ -1087,6 +1121,8 @@ ELFObjectFile<ELFT>::getRelocationSymbol(DataRefImpl Rel) const {
10871121
template <class ELFT>
10881122
uint64_t ELFObjectFile<ELFT>::getRelocationOffset(DataRefImpl Rel) const {
10891123
const Elf_Shdr *sec = getRelSection(Rel);
1124+
if (sec->sh_type == ELF::SHT_CREL)
1125+
return getCrel(Rel).r_offset;
10901126
if (sec->sh_type == ELF::SHT_REL)
10911127
return getRel(Rel)->r_offset;
10921128

@@ -1096,6 +1132,8 @@ uint64_t ELFObjectFile<ELFT>::getRelocationOffset(DataRefImpl Rel) const {
10961132
template <class ELFT>
10971133
uint64_t ELFObjectFile<ELFT>::getRelocationType(DataRefImpl Rel) const {
10981134
const Elf_Shdr *sec = getRelSection(Rel);
1135+
if (sec->sh_type == ELF::SHT_CREL)
1136+
return getCrel(Rel).r_type;
10991137
if (sec->sh_type == ELF::SHT_REL)
11001138
return getRel(Rel)->getType(EF.isMips64EL());
11011139
else
@@ -1117,9 +1155,11 @@ void ELFObjectFile<ELFT>::getRelocationTypeName(
11171155
template <class ELFT>
11181156
Expected<int64_t>
11191157
ELFObjectFile<ELFT>::getRelocationAddend(DataRefImpl Rel) const {
1120-
if (getRelSection(Rel)->sh_type != ELF::SHT_RELA)
1121-
return createError("Section is not SHT_RELA");
1122-
return (int64_t)getRela(Rel)->r_addend;
1158+
if (getRelSection(Rel)->sh_type == ELF::SHT_RELA)
1159+
return (int64_t)getRela(Rel)->r_addend;
1160+
if (getRelSection(Rel)->sh_type == ELF::SHT_CREL)
1161+
return (int64_t)getCrel(Rel).r_addend;
1162+
return createError("Relocation section does not have addends");
11231163
}
11241164

11251165
template <class ELFT>
@@ -1142,6 +1182,14 @@ ELFObjectFile<ELFT>::getRela(DataRefImpl Rela) const {
11421182
return *Ret;
11431183
}
11441184

1185+
template <class ELFT>
1186+
typename ELFObjectFile<ELFT>::Elf_Crel
1187+
ELFObjectFile<ELFT>::getCrel(DataRefImpl Crel) const {
1188+
assert(getRelSection(Crel)->sh_type == ELF::SHT_CREL);
1189+
assert(Crel.d.a < Crels.size());
1190+
return Crels[Crel.d.a][Crel.d.b];
1191+
}
1192+
11451193
template <class ELFT>
11461194
Expected<ELFObjectFile<ELFT>>
11471195
ELFObjectFile<ELFT>::create(MemoryBufferRef Object, bool InitContent) {
@@ -1453,6 +1501,15 @@ template <class ELFT> bool ELFObjectFile<ELFT>::isRelocatableObject() const {
14531501
return EF.getHeader().e_type == ELF::ET_REL;
14541502
}
14551503

1504+
template <class ELFT>
1505+
StringRef ELFObjectFile<ELFT>::getCrelDecodeProblem(DataRefImpl Sec) const {
1506+
uintptr_t SHT = reinterpret_cast<uintptr_t>(cantFail(EF.sections()).begin());
1507+
auto I = (Sec.p - SHT) / EF.getHeader().e_shentsize;
1508+
if (I < CrelDecodeProblems.size())
1509+
return CrelDecodeProblems[I];
1510+
return "";
1511+
}
1512+
14561513
} // end namespace object
14571514
} // end namespace llvm
14581515

llvm/lib/Object/ELF.cpp

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -408,46 +408,31 @@ ELFFile<ELFT>::getCrelHeader(ArrayRef<uint8_t> Content) const {
408408
template <class ELFT>
409409
Expected<typename ELFFile<ELFT>::RelsOrRelas>
410410
ELFFile<ELFT>::decodeCrel(ArrayRef<uint8_t> Content) const {
411-
DataExtractor Data(Content, isLE(), sizeof(typename ELFT::Addr));
412-
DataExtractor::Cursor Cur(0);
413-
const uint64_t Hdr = Data.getULEB128(Cur);
414-
const size_t Count = Hdr / 8;
415-
const size_t FlagBits = Hdr & ELF::CREL_HDR_ADDEND ? 3 : 2;
416-
const size_t Shift = Hdr % ELF::CREL_HDR_ADDEND;
417411
std::vector<Elf_Rel> Rels;
418412
std::vector<Elf_Rela> Relas;
419-
if (Hdr & ELF::CREL_HDR_ADDEND)
420-
Relas.resize(Count);
421-
else
422-
Rels.resize(Count);
423-
typename ELFT::uint Offset = 0, Addend = 0;
424-
uint32_t SymIdx = 0, Type = 0;
425-
for (size_t I = 0; I != Count; ++I) {
426-
// The delta offset and flags member may be larger than uint64_t. Special
427-
// case the first byte (2 or 3 flag bits; the rest are offset bits). Other
428-
// ULEB128 bytes encode the remaining delta offset bits.
429-
const uint8_t B = Data.getU8(Cur);
430-
Offset += B >> FlagBits;
431-
if (B >= 0x80)
432-
Offset += (Data.getULEB128(Cur) << (7 - FlagBits)) - (0x80 >> FlagBits);
433-
// Delta symidx/type/addend members (SLEB128).
434-
if (B & 1)
435-
SymIdx += Data.getSLEB128(Cur);
436-
if (B & 2)
437-
Type += Data.getSLEB128(Cur);
438-
if (B & 4 & Hdr)
439-
Addend += Data.getSLEB128(Cur);
440-
if (Hdr & ELF::CREL_HDR_ADDEND) {
441-
Relas[I].r_offset = Offset << Shift;
442-
Relas[I].setSymbolAndType(SymIdx, Type, false);
443-
Relas[I].r_addend = Addend;
444-
} else {
445-
Rels[I].r_offset = Offset << Shift;
446-
Rels[I].setSymbolAndType(SymIdx, Type, false);
447-
}
448-
}
449-
if (!Cur)
450-
return std::move(Cur.takeError());
413+
size_t I = 0;
414+
bool HasAddend;
415+
Error Err = object::decodeCrel<ELFT::Is64Bits>(
416+
Content,
417+
[&](uint64_t Count, bool HasA) {
418+
HasAddend = HasA;
419+
if (HasAddend)
420+
Relas.resize(Count);
421+
else
422+
Rels.resize(Count);
423+
},
424+
[&](Elf_Crel Crel) {
425+
if (HasAddend) {
426+
Relas[I].r_offset = Crel.r_offset;
427+
Relas[I].setSymbolAndType(Crel.r_symidx, Crel.r_type, false);
428+
Relas[I++].r_addend = Crel.r_addend;
429+
} else {
430+
Rels[I].r_offset = Crel.r_offset;
431+
Rels[I++].setSymbolAndType(Crel.r_symidx, Crel.r_type, false);
432+
}
433+
});
434+
if (Err)
435+
return std::move(Err);
451436
return std::make_pair(std::move(Rels), std::move(Relas));
452437
}
453438

llvm/lib/Object/ELFObjectFile.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,3 +1013,14 @@ Expected<std::vector<BBAddrMap>> ELFObjectFileBase::readBBAddrMap(
10131013
return readBBAddrMapImpl(cast<ELF64BEObjectFile>(this)->getELFFile(),
10141014
TextSectionIndex, PGOAnalyses);
10151015
}
1016+
1017+
StringRef ELFObjectFileBase::getCrelDecodeProblem(SectionRef Sec) const {
1018+
auto Data = Sec.getRawDataRefImpl();
1019+
if (const auto *Obj = dyn_cast<ELF32LEObjectFile>(this))
1020+
return Obj->getCrelDecodeProblem(Data);
1021+
if (const auto *Obj = dyn_cast<ELF32BEObjectFile>(this))
1022+
return Obj->getCrelDecodeProblem(Data);
1023+
if (const auto *Obj = dyn_cast<ELF64LEObjectFile>(this))
1024+
return Obj->getCrelDecodeProblem(Data);
1025+
return cast<ELF64BEObjectFile>(this)->getCrelDecodeProblem(Data);
1026+
}

0 commit comments

Comments
 (0)