llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-llvm-binary-utilities Author: Fangrui Song (MaskRay) <details> <summary>Changes</summary> The decoder code is similar to that for llvm-readelf -r (#<!-- -->91280). 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. --- Full diff: https://github.com/llvm/llvm-project/pull/97382.diff 6 Files Affected: - (modified) llvm/include/llvm/Object/ELFObjectFile.h (+87-6) - (modified) llvm/lib/Object/ELFObjectFile.cpp (+11) - (added) llvm/test/tools/llvm-objdump/ELF/crel.test (+213) - (modified) llvm/test/tools/llvm-objdump/X86/elf-disassemble-relocs.test (+4) - (modified) llvm/tools/llvm-objdump/ELFDump.cpp (+5-1) - (modified) llvm/tools/llvm-objdump/llvm-objdump.cpp (+9) ``````````diff diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index 8cc09e7fd7d55..665d848d5b607 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -29,6 +29,7 @@ #include "llvm/Support/ELFAttributes.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/TargetParser/SubtargetFeature.h" @@ -122,6 +123,8 @@ class ELFObjectFileBase : public ObjectFile { Expected<std::vector<BBAddrMap>> readBBAddrMap(std::optional<unsigned> TextSectionIndex = std::nullopt, std::vector<PGOAnalysisMap> *PGOAnalyses = nullptr) const; + + StringRef getCrelError(SectionRef Sec) const; }; class ELFSectionRef : public SectionRef { @@ -292,6 +295,11 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase { const Elf_Shdr *DotSymtabSec = nullptr; // Symbol table section. const Elf_Shdr *DotSymtabShndxSec = nullptr; // SHT_SYMTAB_SHNDX section. + // Hold CREL relocations for SectionRef::relocations(). + mutable SmallVector<SmallVector<Elf_Crel, 0>, 0> Crels; + // Hold CREL decoding errors. + mutable SmallVector<std::string, 0> CrelErrs; + Error initContent() override; void moveSymbolNext(DataRefImpl &Symb) const override; @@ -446,6 +454,7 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase { const Elf_Rel *getRel(DataRefImpl Rel) const; const Elf_Rela *getRela(DataRefImpl Rela) const; + Elf_Crel getCrel(DataRefImpl Crel) const; Expected<const Elf_Sym *> getSymbol(DataRefImpl Sym) const { return EF.template getEntry<Elf_Sym>(Sym.d.a, Sym.d.b); @@ -499,6 +508,8 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase { bool isRelocatableObject() const override; void createFakeSections() { EF.createFakeSections(); } + + StringRef getCrelError(DataRefImpl Sec) const; }; using ELF32LEObjectFile = ELFObjectFile<ELF32LE>; @@ -1022,6 +1033,47 @@ ELFObjectFile<ELFT>::section_rel_begin(DataRefImpl Sec) const { uintptr_t SHT = reinterpret_cast<uintptr_t>((*SectionsOrErr).begin()); RelData.d.a = (Sec.p - SHT) / EF.getHeader().e_shentsize; RelData.d.b = 0; + if (reinterpret_cast<const Elf_Shdr *>(Sec.p)->sh_type == ELF::SHT_CREL) { + if (RelData.d.a + 1 > Crels.size()) { + Crels.resize(RelData.d.a + 1); + CrelErrs.resize(RelData.d.a + 1); + } + if (Crels[RelData.d.a].empty()) { + // Decode SHT_CREL. See ELFFile<ELFT>::decodeCrel. + ArrayRef<uint8_t> Content = cantFail(getSectionContents(Sec)); + DataExtractor Data(Content, ELFT::Endianness == endianness::little, + sizeof(typename ELFT::Addr)); + DataExtractor::Cursor Cur(0); + const uint64_t Hdr = Data.getULEB128(Cur); + const size_t Count = Hdr / 8; + const size_t FlagBits = Hdr & ELF::CREL_HDR_ADDEND ? 3 : 2; + const size_t Shift = Hdr % ELF::CREL_HDR_ADDEND; + uintX_t Offset = 0, Addend = 0; + uint32_t Symidx = 0, Type = 0; + for (size_t i = 0; i != Count; ++i) { + const uint8_t B = Data.getU8(Cur); + Offset += B >> FlagBits; + if (B >= 0x80) + Offset += + (Data.getULEB128(Cur) << (7 - FlagBits)) - (0x80 >> FlagBits); + if (B & 1) + Symidx += Data.getSLEB128(Cur); + if (B & 2) + Type += Data.getSLEB128(Cur); + if (B & 4 && FlagBits == 3) + Addend += Data.getSLEB128(Cur); + if (!Cur) + break; + Crels[RelData.d.a].push_back( + Elf_Crel{Offset << Shift, uint32_t(Symidx), Type, + std::make_signed_t<typename ELFT::uint>(Addend)}); + } + if (!Cur) { + Crels[RelData.d.a].assign(1, Elf_Crel{0, 0, 0, 0}); + CrelErrs[RelData.d.a] = toString(Cur.takeError()); + } + } + } return relocation_iterator(RelocationRef(RelData, this)); } @@ -1030,9 +1082,13 @@ relocation_iterator ELFObjectFile<ELFT>::section_rel_end(DataRefImpl Sec) const { const Elf_Shdr *S = reinterpret_cast<const Elf_Shdr *>(Sec.p); relocation_iterator Begin = section_rel_begin(Sec); + DataRefImpl RelData = Begin->getRawDataRefImpl(); + if (S->sh_type == ELF::SHT_CREL) { + RelData.d.b = Crels[RelData.d.a].size(); + return relocation_iterator(RelocationRef(RelData, this)); + } if (S->sh_type != ELF::SHT_RELA && S->sh_type != ELF::SHT_REL) return Begin; - DataRefImpl RelData = Begin->getRawDataRefImpl(); const Elf_Shdr *RelSec = getRelSection(RelData); // Error check sh_link here so that getRelocationSymbol can just use it. @@ -1050,7 +1106,7 @@ Expected<section_iterator> ELFObjectFile<ELFT>::getRelocatedSection(DataRefImpl Sec) const { const Elf_Shdr *EShdr = getSection(Sec); uintX_t Type = EShdr->sh_type; - if (Type != ELF::SHT_REL && Type != ELF::SHT_RELA) + if (Type != ELF::SHT_REL && Type != ELF::SHT_RELA && Type != ELF::SHT_CREL) return section_end(); Expected<const Elf_Shdr *> SecOrErr = EF.getSection(EShdr->sh_info); @@ -1070,7 +1126,9 @@ symbol_iterator ELFObjectFile<ELFT>::getRelocationSymbol(DataRefImpl Rel) const { uint32_t symbolIdx; const Elf_Shdr *sec = getRelSection(Rel); - if (sec->sh_type == ELF::SHT_REL) + if (sec->sh_type == ELF::SHT_CREL) + symbolIdx = getCrel(Rel).r_symidx; + else if (sec->sh_type == ELF::SHT_REL) symbolIdx = getRel(Rel)->getSymbol(EF.isMips64EL()); else symbolIdx = getRela(Rel)->getSymbol(EF.isMips64EL()); @@ -1087,6 +1145,8 @@ ELFObjectFile<ELFT>::getRelocationSymbol(DataRefImpl Rel) const { template <class ELFT> uint64_t ELFObjectFile<ELFT>::getRelocationOffset(DataRefImpl Rel) const { const Elf_Shdr *sec = getRelSection(Rel); + if (sec->sh_type == ELF::SHT_CREL) + return getCrel(Rel).r_offset; if (sec->sh_type == ELF::SHT_REL) return getRel(Rel)->r_offset; @@ -1096,6 +1156,8 @@ uint64_t ELFObjectFile<ELFT>::getRelocationOffset(DataRefImpl Rel) const { template <class ELFT> uint64_t ELFObjectFile<ELFT>::getRelocationType(DataRefImpl Rel) const { const Elf_Shdr *sec = getRelSection(Rel); + if (sec->sh_type == ELF::SHT_CREL) + return getCrel(Rel).r_type; if (sec->sh_type == ELF::SHT_REL) return getRel(Rel)->getType(EF.isMips64EL()); else @@ -1117,9 +1179,11 @@ void ELFObjectFile<ELFT>::getRelocationTypeName( template <class ELFT> Expected<int64_t> ELFObjectFile<ELFT>::getRelocationAddend(DataRefImpl Rel) const { - if (getRelSection(Rel)->sh_type != ELF::SHT_RELA) - return createError("Section is not SHT_RELA"); - return (int64_t)getRela(Rel)->r_addend; + if (getRelSection(Rel)->sh_type == ELF::SHT_RELA) + return (int64_t)getRela(Rel)->r_addend; + if (getRelSection(Rel)->sh_type == ELF::SHT_CREL) + return (int64_t)getCrel(Rel).r_addend; + return createError("Section is not SHT_RELA"); } template <class ELFT> @@ -1142,6 +1206,14 @@ ELFObjectFile<ELFT>::getRela(DataRefImpl Rela) const { return *Ret; } +template <class ELFT> +typename ELFObjectFile<ELFT>::Elf_Crel +ELFObjectFile<ELFT>::getCrel(DataRefImpl Crel) const { + assert(getRelSection(Crel)->sh_type == ELF::SHT_CREL); + assert(Crel.d.a < Crels.size()); + return Crels[Crel.d.a][Crel.d.b]; +} + template <class ELFT> Expected<ELFObjectFile<ELFT>> ELFObjectFile<ELFT>::create(MemoryBufferRef Object, bool InitContent) { @@ -1453,6 +1525,15 @@ template <class ELFT> bool ELFObjectFile<ELFT>::isRelocatableObject() const { return EF.getHeader().e_type == ELF::ET_REL; } +template <class ELFT> +StringRef ELFObjectFile<ELFT>::getCrelError(DataRefImpl Sec) const { + uintptr_t SHT = reinterpret_cast<uintptr_t>(cantFail(EF.sections()).begin()); + auto I = (Sec.p - SHT) / EF.getHeader().e_shentsize; + if (I < CrelErrs.size()) + return CrelErrs[I]; + return ""; +} + } // end namespace object } // end namespace llvm diff --git a/llvm/lib/Object/ELFObjectFile.cpp b/llvm/lib/Object/ELFObjectFile.cpp index cbc55a145e0e6..3da7da759a145 100644 --- a/llvm/lib/Object/ELFObjectFile.cpp +++ b/llvm/lib/Object/ELFObjectFile.cpp @@ -1013,3 +1013,14 @@ Expected<std::vector<BBAddrMap>> ELFObjectFileBase::readBBAddrMap( return readBBAddrMapImpl(cast<ELF64BEObjectFile>(this)->getELFFile(), TextSectionIndex, PGOAnalyses); } + +StringRef ELFObjectFileBase::getCrelError(SectionRef Sec) const { + auto Data = Sec.getRawDataRefImpl(); + if (const auto *Obj = dyn_cast<ELF32LEObjectFile>(this)) + return Obj->getCrelError(Data); + if (const auto *Obj = dyn_cast<ELF32BEObjectFile>(this)) + return Obj->getCrelError(Data); + if (const auto *Obj = dyn_cast<ELF64LEObjectFile>(this)) + return Obj->getCrelError(Data); + return cast<ELF64BEObjectFile>(this)->getCrelError(Data); +} diff --git a/llvm/test/tools/llvm-objdump/ELF/crel.test b/llvm/test/tools/llvm-objdump/ELF/crel.test new file mode 100644 index 0000000000000..7eb435e232ae4 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/crel.test @@ -0,0 +1,213 @@ +# RUN: yaml2obj --docnum=1 %s -o %t +# RUN: llvm-objdump -r %t | FileCheck %s --strict-whitespace --match-full-lines + +# CHECK:RELOCATION RECORDS FOR [.text]: +# CHECK-NEXT:OFFSET TYPE VALUE +# CHECK-NEXT:0000000000000001 R_X86_64_32 g1+0x1 +# CHECK-NEXT:0000000000000002 R_X86_64_64 l1+0x2 +# CHECK-NEXT:0000000000000000 R_X86_64_32S g1-0x1 +# CHECK-NEXT:0000000000000004 R_X86_64_32S .text-0x8000000000000000 +#CHECK-EMPTY: +# CHECK-NEXT:RELOCATION RECORDS FOR [nonalloc]: +# CHECK-NEXT:OFFSET TYPE VALUE +# CHECK-NEXT:0000000000000010 R_X86_64_64 g1+0x1 +# CHECK-NEXT:0000000000000020 R_X86_64_64 g2+0x2 +# CHECK-NEXT:0000000000000030 R_X86_64_64 *ABS* +# CHECK-NOT:{{.}} + +--- !ELF +FileHeader: !FileHeader + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000000000000" + Flags: [SHF_ALLOC] +- Name: .crel.text + Type: SHT_CREL + Info: .text + Link: .symtab + Relocations: + - Offset: 0x1 + Symbol: g1 + Type: R_X86_64_32 + Addend: 1 + - Offset: 0x2 + Symbol: l1 + Type: R_X86_64_64 + Addend: 2 + - Offset: 0x0 + Symbol: g1 + Type: R_X86_64_32S + Addend: 0xffffffffffffffff + - Offset: 0x4 + Symbol: .text + Type: R_X86_64_32S + Addend: 0x8000000000000000 +- Name: nonalloc + Type: SHT_PROGBITS + Size: 0x30 +- Name: .crelnonalloc + Type: SHT_CREL + Info: nonalloc + Link: .symtab + Relocations: + - Offset: 0x10 + Symbol: g1 + Type: R_X86_64_64 + Addend: 1 + - Offset: 0x20 + Symbol: g2 + Type: R_X86_64_64 + Addend: 2 + - Offset: 0x30 + Symbol: 0 + Type: R_X86_64_64 + +Symbols: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: l1 + - Name: g1 + Section: .text + Value: 0x0 + Size: 4 + Binding: STB_GLOBAL + - Name: g2 + Binding: STB_GLOBAL + +## Check relocation formatting on ELFCLASS32 as well. +# RUN: yaml2obj --docnum=2 %s > %t2 +# RUN: llvm-objdump -r %t2 | FileCheck %s --check-prefix=ELF32 --strict-whitespace --match-full-lines + +# ELF32:RELOCATION RECORDS FOR [.text]: +# ELF32-NEXT:OFFSET TYPE VALUE +# ELF32-NEXT:00000008 R_ARM_REL32 l1+0x1 +# ELF32-NEXT:00000004 R_ARM_ABS32 g1 + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2MSB + Type: ET_REL + Machine: EM_ARM +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x10 +- Name: .crel.text + Type: SHT_CREL + Info: .text + Link: .symtab + Relocations: + - Offset: 0x8 + Symbol: l1 + Type: R_ARM_REL32 + Addend: 1 + - Offset: 0x4 + Symbol: g1 + Type: R_ARM_ABS32 +Symbols: + - Name: l1 + - Name: g1 + Binding: STB_GLOBAL + +## Check CREL with implicit addends. +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: llvm-objdump -r %t3 | FileCheck %s --check-prefix=IMPLICIT --strict-whitespace --match-full-lines +# IMPLICIT:RELOCATION RECORDS FOR [.data]: +# IMPLICIT-NEXT:OFFSET TYPE VALUE +# IMPLICIT-NEXT:000000000000001f R_X86_64_32 g1 +# IMPLICIT-NEXT:000000000000003f R_X86_64_64 g1 +# IMPLICIT-NEXT:0000000000000000 R_X86_64_32S l1 +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .data + Type: SHT_PROGBITS + - Name: .crel.data + Type: SHT_CREL + Flags: [ SHF_INFO_LINK ] + Link: .symtab + Info: .data + Content: 187f030a82017787feffffffffffffff077f0a +Symbols: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: l1 + Section: .text + - Name: g1 + Section: .text + Binding: STB_GLOBAL + +## Test errors. +# RUN: yaml2obj --docnum=4 %s -o %t.err +# RUN: llvm-objdump -r %t.err 2>&1 | FileCheck %s --check-prefix=ERR -DFILE=%t.err + +# ERR:RELOCATION RECORDS FOR [.data]: +# ERR-NEXT:OFFSET TYPE VALUE +# ERR-NEXT:warning: '[[FILE]]': unable to decode LEB128 at offset 0x00000000: malformed uleb128, extends past end +# ERR-NOT:{{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .data + Type: SHT_PROGBITS + - Name: .crel.data + Type: SHT_CREL + Flags: [] + Link: .symtab + Info: .data +Symbols: + - Name: .text + Type: STT_SECTION + Section: .text + +# RUN: yaml2obj --docnum=5 %s -o %t.err2 +# RUN: llvm-objdump -r %t.err2 2>&1 | FileCheck %s --check-prefix=ERR2 -DFILE=%t.err2 + +# ERR2:RELOCATION RECORDS FOR [.data]: +# ERR2-NEXT:OFFSET TYPE VALUE +# ERR2-NEXT:warning: '[[FILE]]': unexpected end of data at offset 0x1 while reading [0x1, 0x2) +# ERR-NOT:{{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2MSB + Type: ET_REL + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .data + Type: SHT_PROGBITS + - Name: .crel.data + Type: SHT_CREL + Flags: [] + Link: .symtab + Info: .data + Content: 08 +Symbols: + - Name: .text + Type: STT_SECTION + Section: .text diff --git a/llvm/test/tools/llvm-objdump/X86/elf-disassemble-relocs.test b/llvm/test/tools/llvm-objdump/X86/elf-disassemble-relocs.test index cce0712e8fa0d..a6bd09ce3fa24 100644 --- a/llvm/test/tools/llvm-objdump/X86/elf-disassemble-relocs.test +++ b/llvm/test/tools/llvm-objdump/X86/elf-disassemble-relocs.test @@ -6,6 +6,10 @@ # RUN: llvm-objdump 1.o -d -r | FileCheck %s --implicit-check-not="RELOCATION RECORDS" # RUN: llvm-objdump 1.o -r --disassemble-symbols=x2,x4 | FileCheck %s --check-prefix=CHECK2 +# RUN: llvm-mc -filetype=obj -triple=x86_64 -crel 1.s -o 1leb.o +# RUN: llvm-objdump 1leb.o -d -r | FileCheck %s --implicit-check-not="RELOCATION RECORDS" +# RUN: llvm-objdump 1leb.o -r --disassemble-symbols=x2,x4 | FileCheck %s --check-prefix=CHECK2 + #--- 1.s # CHECK: 0000000000000000 <x1>: # CHECK-NEXT: 0: e8 00 00 00 00 callq 0x5 <x1+0x5> diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp index 8c184fc1fbb66..5ac13495662fa 100644 --- a/llvm/tools/llvm-objdump/ELFDump.cpp +++ b/llvm/tools/llvm-objdump/ELFDump.cpp @@ -104,7 +104,11 @@ static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj, // In SHT_REL case we would need to read the addend from section data. // GNU objdump does not do that and we just follow for simplicity atm. bool Undef = false; - if ((*SecOrErr)->sh_type == ELF::SHT_RELA) { + if ((*SecOrErr)->sh_type == ELF::SHT_CREL) { + auto ERela = Obj->getCrel(Rel); + Addend = ERela.r_addend; + Undef = ERela.getSymbol(false) == 0; + } else if ((*SecOrErr)->sh_type == ELF::SHT_RELA) { const typename ELFT::Rela *ERela = Obj->getRela(Rel); Addend = ERela->r_addend; Undef = ERela->getSymbol(false) == 0; diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 6249be4f332b7..792a27f8d3d3d 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -2687,6 +2687,15 @@ void Dumper::printRelocations() { << "VALUE\n"; for (SectionRef Section : P.second) { + // CREL requires decoding and has its specific errors. + if (O.isELF() && ELFSectionRef(Section).getType() == ELF::SHT_CREL) { + const ELFObjectFileBase *ELF = cast<const ELFObjectFileBase>(&O); + StringRef Err = ELF->getCrelError(Section); + if (!Err.empty()) { + reportUniqueWarning(Err); + continue; + } + } for (const RelocationRef &Reloc : Section.relocations()) { uint64_t Address = Reloc.getOffset(); SmallString<32> RelocName; `````````` </details> https://github.com/llvm/llvm-project/pull/97382 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits