aboutsummaryrefslogtreecommitdiff
path: root/llvm
diff options
context:
space:
mode:
authorFangrui Song <i@maskray.me>2024-04-19 09:21:57 -0700
committerGitHub <noreply@github.com>2024-04-19 09:21:57 -0700
commitd86079f93c9f59d31f2cebb55dce24783070bd77 (patch)
treeb135f17837f54df3269787b9a746ae1bfffeb748 /llvm
parent854cc8986650267846c5eaf8ba164584a9ea7c01 (diff)
downloadllvm-d86079f93c9f59d31f2cebb55dce24783070bd77.zip
llvm-d86079f93c9f59d31f2cebb55dce24783070bd77.tar.gz
llvm-d86079f93c9f59d31f2cebb55dce24783070bd77.tar.bz2
[llvm-readelf] Print more information for RELR
llvm-readelf/llvm-readobj print RELR as REL relocations with a fixed type (e.g. `R_*_RELATIVE`). GNU readelf printed only addresses and have recently switched to a more descritive style that includes a symbolic address column (symbolized using .symtab/.strtab) (milestone: binutils 2.43). This patch implements the new GNU style, which seems superior to the current REL style and essentially obsoletes LLVM-specific --raw-relr (`printRelrReloc`). Pull Request: https://github.com/llvm/llvm-project/pull/89162
Diffstat (limited to 'llvm')
-rw-r--r--llvm/test/tools/llvm-readobj/ELF/relr-relocs.test141
-rw-r--r--llvm/tools/llvm-readobj/ELFDumper.cpp130
2 files changed, 211 insertions, 60 deletions
diff --git a/llvm/test/tools/llvm-readobj/ELF/relr-relocs.test b/llvm/test/tools/llvm-readobj/ELF/relr-relocs.test
index 91b148e..9b59f99 100644
--- a/llvm/test/tools/llvm-readobj/ELF/relr-relocs.test
+++ b/llvm/test/tools/llvm-readobj/ELF/relr-relocs.test
@@ -47,29 +47,39 @@
# RAW-GNU1-NEXT: 00000000000f0501
# RAW-GNU1-NEXT: 000a700550400009
-# RUN: llvm-readelf --relocations %t1 | FileCheck --check-prefix=GNU1 %s
-# GNU1: Relocation section '.relr.dyn' at offset 0x40 contains 21 entries:
-# GNU1: 0000000000010d60 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000010d68 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000010da0 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020000 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020040 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020050 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020080 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020088 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020090 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020098 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020210 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 00000000000202a8 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 00000000000202d8 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 00000000000202e8 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 00000000000202f8 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020308 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020358 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020360 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020368 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020380 0000000000000008 R_X86_64_RELATIVE
-# GNU1-NEXT: 0000000000020390 0000000000000008 R_X86_64_RELATIVE
+# RUN: llvm-readelf --relocations %t1 | FileCheck --check-prefix=GNU1 --match-full-lines --strict-whitespace %s
+# GNU1:Relocation section '.relr.dyn' at offset 0x40 contains 21 entries:
+# GNU1-NEXT:Index: Entry Address Symbolic Address
+# GNU1-NEXT:0000: 0000000000010d60 0000000000010d60
+# GNU1-NEXT:0001: 0000000000000103 0000000000010d68
+# GNU1-NEXT: 0000000000010da0 base + 0x30
+# GNU1-NEXT:0002: 0000000000020000 0000000000020000 foo
+# GNU1-NEXT:0003: 00000000000f0501 0000000000020040 foo + 0x40
+# GNU1-NEXT: 0000000000020050 foo + 0x50
+# GNU1-NEXT: 0000000000020080 foo + 0x80
+# GNU1-NEXT: 0000000000020088 foo + 0x88
+# GNU1-NEXT: 0000000000020090 foo + 0x90
+# GNU1-NEXT: 0000000000020098 foo + 0x98
+# GNU1-NEXT:0004: 000a700550400009 0000000000020210 bar + 0x10
+# GNU1-NEXT: 00000000000202a8 bar + 0xa8
+# GNU1-NEXT: 00000000000202d8 bar + 0xd8
+# GNU1-NEXT: 00000000000202e8 bar + 0xe8
+# GNU1-NEXT: 00000000000202f8 bar + 0xf8
+# GNU1-NEXT: 0000000000020308 bar + 0x108
+# GNU1-NEXT: 0000000000020358 bar + 0x158
+# GNU1-NEXT: 0000000000020360 bar + 0x160
+# GNU1-NEXT: 0000000000020368 bar + 0x168
+# GNU1-NEXT: 0000000000020380 bar + 0x180
+# GNU1-NEXT: 0000000000020390 bar + 0x190
+# GNU1-NOT:{{.}}
+
+## The addresses are not symbolized in the absence of .symtab.
+# RUN: llvm-objcopy --strip-all %t1 %t1.stripped
+# RUN: llvm-readelf --relocations %t1.stripped | FileCheck --check-prefix=GNU1S --match-full-lines --strict-whitespace %s
+# GNU1S:Relocation section '.relr.dyn' at offset 0x40 contains 21 entries:
+# GNU1S-NEXT:Index: Entry Address Symbolic Address
+# GNU1S-NEXT:0000: 0000000000010d60 0000000000010d60
+# GNU1S-NEXT:0001: 0000000000000103 0000000000010d68
--- !ELF
FileHeader:
@@ -83,6 +93,18 @@ Sections:
Flags: [ SHF_ALLOC ]
Entries: [ 0x0000000000010D60, 0x0000000000000103, 0x0000000000020000,
0x00000000000F0501, 0x000A700550400009 ]
+Symbols:
+ - Name: bar
+ Value: 0x20200
+ Section: .relr.dyn
+ - Name: foo
+ Value: 0x20000
+ Section: .relr.dyn
+ - Name: base
+ Value: 0x10d70
+ Section: .relr.dyn
+ - Name: ignored
+ Value: 0x20210
# RUN: yaml2obj --docnum=2 %s -o %t2
# RUN: llvm-readobj --relocations --raw-relr %t2 | \
@@ -124,22 +146,24 @@ Sections:
# RAW-GNU2-NEXT: 000f0501
# RAW-GNU2-NEXT: 50400009
-# RUN: llvm-readelf --relocations %t2 | FileCheck --check-prefix=GNU2 %s
-# GNU2: Relocation section '.relr.dyn' at offset 0x34 contains 14 entries:
-# GNU2: 00010d60 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00010d64 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00010d80 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00020000 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00020020 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00020028 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00020040 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00020044 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00020048 00000008 R_386_RELATIVE
-# GNU2-NEXT: 0002004c 00000008 R_386_RELATIVE
-# GNU2-NEXT: 00020088 00000008 R_386_RELATIVE
-# GNU2-NEXT: 000200d4 00000008 R_386_RELATIVE
-# GNU2-NEXT: 000200ec 00000008 R_386_RELATIVE
-# GNU2-NEXT: 000200f4 00000008 R_386_RELATIVE
+# RUN: llvm-readelf --relocations %t2 | FileCheck --check-prefix=GNU2 --match-full-lines --strict-whitespace %s
+# GNU2:Relocation section '.relr.dyn' at offset 0x34 contains 14 entries:
+# GNU2-NEXT:Index: Entry Address Symbolic Address
+# GNU2-NEXT:0000: 00010d60 00010d60 .relr.dyn
+# GNU2-NEXT:0001: 00000103 00010d64 .relr.dyn + 0x4
+# GNU2-NEXT: 00010d80 .relr.dyn + 0x20
+# GNU2-NEXT:0002: 00020000 00020000 .relr.dyn + 0xf2a0
+# GNU2-NEXT:0003: 000f0501 00020020 .relr.dyn + 0xf2c0
+# GNU2-NEXT: 00020028 .relr.dyn + 0xf2c8
+# GNU2-NEXT: 00020040 .relr.dyn + 0xf2e0
+# GNU2-NEXT: 00020044 .relr.dyn + 0xf2e4
+# GNU2-NEXT: 00020048 .relr.dyn + 0xf2e8
+# GNU2-NEXT: 0002004c .relr.dyn + 0xf2ec
+# GNU2-NEXT:0004: 50400009 00020088 .relr.dyn + 0xf328
+# GNU2-NEXT: 000200d4 .relr.dyn + 0xf374
+# GNU2-NEXT: 000200ec .relr.dyn + 0xf38c
+# GNU2-NEXT: 000200f4 .relr.dyn + 0xf394
+# GNU2-NOT:{{.}}
--- !ELF
FileHeader:
@@ -156,6 +180,11 @@ Sections:
EntSize: [[ENTSIZE=<none>]]
ShType: [[SHTYPE=<none>]]
Link: [[LINK=<none>]]
+Symbols:
+ - Name: .relr.dyn
+ Type: STT_SECTION
+ Value: 0x10D60
+ Section: .relr.dyn
## Check we report a warning when we are unable to dump relocations
## for a SHT_RELR/SHT_ANDROID_RELR/SHT_AARCH64_AUTH_RELR section.
@@ -175,7 +204,6 @@ Sections:
# BROKEN-GNU: warning: '[[FILE]]': unable to get the number of relocations in [[SECNAME]] section with index 1: section [index 1] has invalid sh_entsize: expected 4, but got 1
# BROKEN-GNU: Relocation section '.relr.dyn' at offset 0x34 contains <?> entries:
-# BROKEN-GNU-NEXT: Offset Info Type Sym. Value Symbol's Name
# BROKEN-GNU-NEXT: warning: '[[FILE]]': unable to read relocations from [[SECNAME]] section with index 1: section [index 1] has invalid sh_entsize: expected 4, but got 1
## Case B: check the case when relocations can't be read from an SHT_ANDROID_RELR section.
@@ -218,3 +246,36 @@ Sections:
# RUN: FileCheck -DFILE=%t2.has.link --check-prefix=RAW-LLVM2 %s
# RUN: llvm-readelf --relocations --raw-relr %t2.has.link 2>&1 | \
# RUN: FileCheck -DFILE=%t2.has.link --check-prefix=RAW-GNU2 %s
+
+## .symtab is invalid. Check we report a warning and print entries without symbolization.
+# RUN: yaml2obj --docnum=3 -DENTSIZE=1 %s -o %t3.err1
+# RUN: llvm-readelf -r %t3.err1 2>&1 | FileCheck -DFILE=%t3.err1 --check-prefixes=GNU3,GNU3-ERR1 --match-full-lines %s
+# RUN: yaml2obj --docnum=3 -DLINK=0xff %s -o %t3.err2
+# RUN: llvm-readelf -r %t3.err2 2>&1 | FileCheck -DFILE=%t3.err2 --check-prefixes=GNU3,GNU3-ERR2 --match-full-lines %s
+
+# GNU3:Index: Entry Address Symbolic Address
+# GNU3-ERR1-NEXT:{{.*}}warning: '[[FILE]]': unable to read symbols from the SHT_SYMTAB section: section [index 2] has invalid sh_entsize: expected 24, but got 1
+# GNU3-ERR2-NEXT:{{.*}}warning: '[[FILE]]': unable to get the string table for the SHT_SYMTAB section: invalid section index: 255
+# GNU3-NEXT:0000: 0000000000010d60 0000000000010d60
+# GNU3-NEXT:0001: 0000000000000103 0000000000010d68
+# GNU3-NEXT: 0000000000010da0
+# GNU3-NOT:{{.}}
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .relr.dyn
+ Type: SHT_RELR
+ Flags: [ SHF_ALLOC ]
+ Entries: [ 0x0000000000010D60, 0x0000000000000103 ]
+ - Name: .symtab
+ Type: SHT_SYMTAB
+ Link: [[LINK=.strtab]]
+ EntSize: [[ENTSIZE=0x18]]
+Symbols:
+ - Name: bar
+ Value: 0x10D60
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 1108672..f145653 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -411,6 +411,7 @@ protected:
std::string getStaticSymbolName(uint32_t Index) const;
StringRef getDynamicString(uint64_t Value) const;
+ std::pair<Elf_Sym_Range, std::optional<StringRef>> getSymtabAndStrtab() const;
void printSymbolsHelper(bool IsDynamic, bool ExtraSymInfo) const;
std::string getDynamicEntry(uint64_t Type, uint64_t Value) const;
@@ -513,6 +514,28 @@ ELFDumper<ELFT>::getVersionTable(const Elf_Shdr &Sec, ArrayRef<Elf_Sym> *SymTab,
}
template <class ELFT>
+std::pair<typename ELFDumper<ELFT>::Elf_Sym_Range, std::optional<StringRef>>
+ELFDumper<ELFT>::getSymtabAndStrtab() const {
+ assert(DotSymtabSec);
+ Elf_Sym_Range Syms(nullptr, nullptr);
+ std::optional<StringRef> StrTable;
+ if (Expected<StringRef> StrTableOrErr =
+ Obj.getStringTableForSymtab(*DotSymtabSec))
+ StrTable = *StrTableOrErr;
+ else
+ reportUniqueWarning(
+ "unable to get the string table for the SHT_SYMTAB section: " +
+ toString(StrTableOrErr.takeError()));
+
+ if (Expected<Elf_Sym_Range> SymsOrErr = Obj.symbols(DotSymtabSec))
+ Syms = *SymsOrErr;
+ else
+ reportUniqueWarning("unable to read symbols from the SHT_SYMTAB section: " +
+ toString(SymsOrErr.takeError()));
+ return {Syms, StrTable};
+}
+
+template <class ELFT>
void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic,
bool ExtraSymInfo) const {
std::optional<StringRef> StrTable;
@@ -525,20 +548,7 @@ void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic,
Syms = dynamic_symbols();
Entries = Syms.size();
} else if (DotSymtabSec) {
- if (Expected<StringRef> StrTableOrErr =
- Obj.getStringTableForSymtab(*DotSymtabSec))
- StrTable = *StrTableOrErr;
- else
- reportUniqueWarning(
- "unable to get the string table for the SHT_SYMTAB section: " +
- toString(StrTableOrErr.takeError()));
-
- if (Expected<Elf_Sym_Range> SymsOrErr = Obj.symbols(DotSymtabSec))
- Syms = *SymsOrErr;
- else
- reportUniqueWarning(
- "unable to read symbols from the SHT_SYMTAB section: " +
- toString(SymsOrErr.takeError()));
+ std::tie(Syms, StrTable) = getSymtabAndStrtab();
Entries = DotSymtabSec->getEntityCount();
}
if (Syms.empty())
@@ -658,6 +668,7 @@ private:
void printHashedSymbol(const Elf_Sym *Sym, unsigned SymIndex,
DataRegion<Elf_Word> ShndxTable, StringRef StrTable,
uint32_t Bucket);
+ void printRelr(const Elf_Shdr &Sec);
void printRelrReloc(const Elf_Relr &R) override;
void printRelRelaReloc(const Relocation<ELFT> &R,
const RelSymbol<ELFT> &RelSym) override;
@@ -3882,6 +3893,12 @@ static bool isRelocationSec(const typename ELFT::Shdr &Sec,
}
template <class ELFT> void GNUELFDumper<ELFT>::printRelocations() {
+ auto PrintAsRelr = [&](const Elf_Shdr &Sec) {
+ return !opts::RawRelr && (Sec.sh_type == ELF::SHT_RELR ||
+ Sec.sh_type == ELF::SHT_ANDROID_RELR ||
+ (this->Obj.getHeader().e_machine == EM_AARCH64 &&
+ Sec.sh_type == ELF::SHT_AARCH64_AUTH_RELR));
+ };
auto GetEntriesNum = [&](const Elf_Shdr &Sec) -> Expected<size_t> {
// Android's packed relocation section needs to be unpacked first
// to get the actual number of entries.
@@ -3894,10 +3911,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printRelocations() {
return RelasOrErr->size();
}
- if (!opts::RawRelr &&
- (Sec.sh_type == ELF::SHT_RELR || Sec.sh_type == ELF::SHT_ANDROID_RELR ||
- (this->Obj.getHeader().e_machine == EM_AARCH64 &&
- Sec.sh_type == ELF::SHT_AARCH64_AUTH_RELR))) {
+ if (PrintAsRelr(Sec)) {
Expected<Elf_Relr_Range> RelrsOrErr = this->Obj.relrs(Sec);
if (!RelrsOrErr)
return RelrsOrErr.takeError();
@@ -3926,13 +3940,89 @@ template <class ELFT> void GNUELFDumper<ELFT>::printRelocations() {
OS << "\nRelocation section '" << Name << "' at offset 0x"
<< utohexstr(Offset, /*LowerCase=*/true) << " contains " << EntriesNum
<< " entries:\n";
- printRelocHeaderFields<ELFT>(OS, Sec.sh_type, this->Obj.getHeader());
- this->printRelocationsHelper(Sec);
+
+ if (PrintAsRelr(Sec)) {
+ printRelr(Sec);
+ } else {
+ printRelocHeaderFields<ELFT>(OS, Sec.sh_type, this->Obj.getHeader());
+ this->printRelocationsHelper(Sec);
+ }
}
if (!HasRelocSections)
OS << "\nThere are no relocations in this file.\n";
}
+template <class ELFT> void GNUELFDumper<ELFT>::printRelr(const Elf_Shdr &Sec) {
+ Expected<Elf_Relr_Range> RangeOrErr = this->Obj.relrs(Sec);
+ if (!RangeOrErr) {
+ this->reportUniqueWarning("unable to read relocations from " +
+ this->describe(Sec) + ": " +
+ toString(RangeOrErr.takeError()));
+ return;
+ }
+ if (ELFT::Is64Bits)
+ OS << "Index: Entry Address Symbolic Address\n";
+ else
+ OS << "Index: Entry Address Symbolic Address\n";
+
+ // If .symtab is available, collect its defined symbols and sort them by
+ // st_value.
+ SmallVector<std::pair<uint64_t, std::string>, 0> Syms;
+ if (this->DotSymtabSec) {
+ Elf_Sym_Range Symtab;
+ std::optional<StringRef> Strtab;
+ std::tie(Symtab, Strtab) = this->getSymtabAndStrtab();
+ if (Symtab.size() && Strtab) {
+ for (auto [I, Sym] : enumerate(Symtab)) {
+ if (!Sym.st_shndx)
+ continue;
+ Syms.emplace_back(Sym.st_value,
+ this->getFullSymbolName(Sym, I, ArrayRef<Elf_Word>(),
+ *Strtab, false));
+ }
+ }
+ }
+ llvm::stable_sort(Syms);
+
+ typename ELFT::uint Base = 0;
+ size_t I = 0;
+ auto Print = [&](uint64_t Where) {
+ OS << format_hex_no_prefix(Where, ELFT::Is64Bits ? 16 : 8);
+ for (; I < Syms.size() && Syms[I].first <= Where; ++I)
+ ;
+ // Try symbolizing the address. Find the nearest symbol before or at the
+ // address and print the symbol and the address difference.
+ if (I) {
+ OS << " " << Syms[I - 1].second;
+ if (Syms[I - 1].first < Where)
+ OS << " + 0x" << Twine::utohexstr(Where - Syms[I - 1].first);
+ }
+ OS << '\n';
+ };
+ for (auto [Index, R] : enumerate(*RangeOrErr)) {
+ typename ELFT::uint Entry = R;
+ OS << formatv("{0:4}: ", Index)
+ << format_hex_no_prefix(Entry, ELFT::Is64Bits ? 16 : 8) << ' ';
+ if ((Entry & 1) == 0) {
+ Print(Entry);
+ Base = Entry + sizeof(typename ELFT::uint);
+ } else {
+ bool First = true;
+ for (auto Where = Base; Entry >>= 1;
+ Where += sizeof(typename ELFT::uint)) {
+ if (Entry & 1) {
+ if (First)
+ First = false;
+ else
+ OS.indent(ELFT::Is64Bits ? 24 : 16);
+ Print(Where);
+ }
+ }
+ Base += (CHAR_BIT * sizeof(Entry) - 1) * sizeof(typename ELFT::uint);
+ }
+ }
+}
+
// Print the offset of a particular section from anyone of the ranges:
// [SHT_LOOS, SHT_HIOS], [SHT_LOPROC, SHT_HIPROC], [SHT_LOUSER, SHT_HIUSER].
// If 'Type' does not fall within any of those ranges, then a string is