1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 02:33:06 +01:00

[llvm-readelf] Support dumping the BB address map section with --bb-addr-map.

This patch lets llvm-readelf dump the content of the BB address map
section in the following format:
```
Function {
  At: <address>
  BB entries [
    {
      Offset:   <offset>
      Size:     <size>
      Metadata: <metadata>
    },
    ...
  ]
}
...
```

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D95511
This commit is contained in:
Rahman Lavaee 2021-03-08 16:02:00 -08:00
parent 2ab41c0cda
commit 2c790c2f9f
11 changed files with 359 additions and 2 deletions

View File

@ -32,6 +32,11 @@ OPTIONS
Display architecture-specific information, e.g. the ARM attributes section on ARM.
.. option:: --bb-addr-map
Display the contents of the basic block address map section(s), which contain the
address of each function, along with the relative offset of each basic block.
.. option:: --color
Use colors in the output for warnings and errors.

View File

@ -148,6 +148,11 @@ The following options are implemented only for the ELF file format.
Display architecture-specific information, e.g. the ARM attributes section on ARM.
.. option:: --bb-addr-map
Display the contents of the basic block address map section(s), which contain the
address of each function, along with the relative offset of each basic block.
.. option:: --demangle, -C
Display demangled symbol names in the output.

View File

@ -392,6 +392,8 @@ public:
Expected<ArrayRef<T>> getSectionContentsAsArray(const Elf_Shdr &Sec) const;
Expected<ArrayRef<uint8_t>> getSectionContents(const Elf_Shdr &Sec) const;
Expected<ArrayRef<uint8_t>> getSegmentContents(const Elf_Phdr &Phdr) const;
Expected<std::vector<Elf_BBAddrMap>>
decodeBBAddrMap(const Elf_Shdr &Sec) const;
};
using ELF32LEFile = ELFFile<ELF32LE>;

View File

@ -43,6 +43,7 @@ template <class ELFT> struct Elf_Nhdr_Impl;
template <class ELFT> class Elf_Note_Impl;
template <class ELFT> class Elf_Note_Iterator_Impl;
template <class ELFT> struct Elf_CGProfile_Impl;
template <class ELFT> struct Elf_BBAddrMap_Impl;
template <endianness E, bool Is64> struct ELFType {
private:
@ -74,6 +75,7 @@ public:
using Note = Elf_Note_Impl<ELFType<E, Is64>>;
using NoteIterator = Elf_Note_Iterator_Impl<ELFType<E, Is64>>;
using CGProfile = Elf_CGProfile_Impl<ELFType<E, Is64>>;
using BBAddrMap = Elf_BBAddrMap_Impl<ELFType<E, Is64>>;
using DynRange = ArrayRef<Dyn>;
using ShdrRange = ArrayRef<Shdr>;
using SymRange = ArrayRef<Sym>;
@ -128,13 +130,14 @@ using ELF64BE = ELFType<support::big, true>;
using Elf_Note = typename ELFT::Note; \
using Elf_Note_Iterator = typename ELFT::NoteIterator; \
using Elf_CGProfile = typename ELFT::CGProfile; \
using Elf_BBAddrMap = typename ELFT::BBAddrMap; \
using Elf_Dyn_Range = typename ELFT::DynRange; \
using Elf_Shdr_Range = typename ELFT::ShdrRange; \
using Elf_Sym_Range = typename ELFT::SymRange; \
using Elf_Rel_Range = typename ELFT::RelRange; \
using Elf_Rela_Range = typename ELFT::RelaRange; \
using Elf_Relr_Range = typename ELFT::RelrRange; \
using Elf_Phdr_Range = typename ELFT::PhdrRange; \
using Elf_Phdr_Range = typename ELFT::PhdrRange;
#define LLVM_ELF_COMMA ,
#define LLVM_ELF_IMPORT_TYPES(E, W) \
@ -788,6 +791,28 @@ template <class ELFT> struct Elf_Mips_ABIFlags {
Elf_Word flags2; // General flags
};
// Struct representing the BBAddrMap for one function.
template <class ELFT> struct Elf_BBAddrMap_Impl {
LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
uintX_t Addr; // Function address
// Struct representing the BBAddrMap information for one basic block.
struct BBEntry {
uint32_t Offset; // Offset of basic block relative to function start.
uint32_t Size; // Size of the basic block.
// The following fields are decoded from the Metadata field. The encoding
// happens in AsmPrinter.cpp:getBBAddrMapMetadata.
bool HasReturn; // If this block ends with a return (or tail call).
bool HasTailCall; // If this block ends with a tail call.
bool IsEHPad; // If this is an exception handling block.
BBEntry(uint32_t Offset, uint32_t Size, uint32_t Metadata)
: Offset(Offset), Size(Size), HasReturn(Metadata & 1),
HasTailCall(Metadata & (1 << 1)), IsEHPad(Metadata & (1 << 2)){};
};
std::vector<BBEntry> BBEntries; // Basic block entries for this function.
};
} // end namespace object.
} // end namespace llvm.

View File

@ -161,7 +161,7 @@ struct BBAddrMapEntry {
llvm::yaml::Hex64 Metadata;
};
llvm::yaml::Hex64 Address;
Optional<uint32_t> NumBlocks;
Optional<uint64_t> NumBlocks;
Optional<std::vector<BBEntry>> BBEntries;
};

View File

@ -612,6 +612,58 @@ ELFFile<ELFT>::toMappedAddr(uint64_t VAddr, WarningHandler WarnHandler) const {
return base() + Offset;
}
template <class ELFT>
Expected<std::vector<typename ELFT::BBAddrMap>>
ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec) const {
Expected<ArrayRef<uint8_t>> ContentsOrErr = getSectionContents(Sec);
if (!ContentsOrErr)
return ContentsOrErr.takeError();
ArrayRef<uint8_t> Content = *ContentsOrErr;
DataExtractor Data(Content, isLE(), ELFT::Is64Bits ? 8 : 4);
std::vector<Elf_BBAddrMap> FunctionEntries;
DataExtractor::Cursor Cur(0);
Error ULEBSizeErr = Error::success();
// Helper to extract and decode the next ULEB128 value as uint32_t.
// Returns zero and sets ULEBSizeErr if the ULEB128 value exceeds the uint32_t
// limit.
// Also returns zero if ULEBSizeErr is already in an error state.
auto ReadULEB128AsUInt32 = [&Data, &Cur, &ULEBSizeErr]() -> uint32_t {
// Bail out and do not extract data if ULEBSizeErr is already set.
if (ULEBSizeErr)
return 0;
uint64_t Offset = Cur.tell();
uint64_t Value = Data.getULEB128(Cur);
if (Value > UINT32_MAX) {
ULEBSizeErr = createError(
"ULEB128 value at offset 0x" + Twine::utohexstr(Offset) +
" exceeds UINT32_MAX (0x" + Twine::utohexstr(Value) + ")");
return 0;
}
return static_cast<uint32_t>(Value);
};
while (!ULEBSizeErr && Cur && Cur.tell() < Content.size()) {
uintX_t Address = static_cast<uintX_t>(Data.getAddress(Cur));
uint32_t NumBlocks = ReadULEB128AsUInt32();
std::vector<typename Elf_BBAddrMap::BBEntry> BBEntries;
for (uint32_t BlockID = 0; !ULEBSizeErr && Cur && (BlockID < NumBlocks);
++BlockID) {
uint32_t Offset = ReadULEB128AsUInt32();
uint32_t Size = ReadULEB128AsUInt32();
uint32_t Metadata = ReadULEB128AsUInt32();
BBEntries.push_back({Offset, Size, Metadata});
}
FunctionEntries.push_back({Address, BBEntries});
}
// Either Cur is in the error state, or ULEBSizeError is set (not both), but
// we join the two errors here to be safe.
if (!Cur || ULEBSizeErr)
return joinErrors(Cur.takeError(), std::move(ULEBSizeErr));
return FunctionEntries;
}
template class llvm::object::ELFFile<ELF32LE>;
template class llvm::object::ELFFile<ELF32BE>;
template class llvm::object::ELFFile<ELF64LE>;

View File

@ -0,0 +1,119 @@
## This test checks how we handle the --bb-addr-map option.
## Check 64-bit:
# RUN: yaml2obj %s -DBITS=64 -DADDR=0xFFFFFFFF1 -o %t1.x64.o
# RUN: llvm-readobj %t1.x64.o --bb-addr-map | FileCheck %s -DADDR=0xFFFFFFFF1 --check-prefix=LLVM
# RUN: llvm-readelf %t1.x64.o --bb-addr-map | FileCheck %s --check-prefix=GNU
## Check 32-bit:
# RUN: yaml2obj %s -DBITS=32 -o %t1.x32.o
# RUN: llvm-readobj %t1.x32.o --bb-addr-map | FileCheck -DADDR=0x11111 %s --check-prefix=LLVM
# RUN: llvm-readelf %t1.x32.o --bb-addr-map | FileCheck %s --check-prefix=GNU
## Check that a malformed section can be handled.
# RUN: yaml2obj %s -DBITS=32 -DSIZE=4 -o %t2.o
# RUN: llvm-readobj %t2.o --bb-addr-map 2>&1 | FileCheck %s -DOFFSET=0x00000004 -DFILE=%t2.o --check-prefix=TRUNCATED
# LLVM: BBAddrMap [
# LLVM-NEXT: Function {
# LLVM-NEXT: At: [[ADDR]]
# LLVM-NEXT: BB entries [
# LLVM-NEXT: {
# LLVM-NEXT: Offset: 0x0
# LLVM-NEXT: Size: 0x1
# LLVM-NEXT: HasReturn: No
# LLVM-NEXT: HasTailCall: Yes
# LLVM-NEXT: IsEHPad: No
# LLVM-NEXT: }
# LLVM-NEXT: {
# LLVM-NEXT: Offset: 0x3
# LLVM-NEXT: Size: 0x4
# LLVM-NEXT: HasReturn: Yes
# LLVM-NEXT: HasTailCall: No
# LLVM-NEXT: IsEHPad: Yes
# LLVM-NEXT: }
# LLVM-NEXT: ]
# LLVM-NEXT: }
# LLVM-NEXT: Function {
# LLVM-NEXT: At: 0x22222
# LLVM-NEXT: BB entries [
# LLVM-NEXT: {
# LLVM-NEXT: Offset: 0x6
# LLVM-NEXT: Size: 0x7
# LLVM-NEXT: HasReturn: No
# LLVM-NEXT: HasTailCall: No
# LLVM-NEXT: IsEHPad: No
# LLVM-NEXT: }
# LLVM-NEXT: ]
# LLVM-NEXT: }
# LLVM-NEXT: ]
# LLVM-NEXT: BBAddrMap [
# LLVM-NEXT: Function {
# LLVM-NEXT: At: 0x33333
# LLVM-NEXT: BB entries [
# LLVM-NEXT: {
# LLVM-NEXT: Offset: 0x9
# LLVM-NEXT: Size: 0xA
# LLVM-NEXT: HasReturn: Yes
# LLVM-NEXT: HasTailCall: Yes
# LLVM-NEXT: IsEHPad: No
# LLVM-NEXT: }
# LLVM-NEXT: ]
# LLVM-NEXT: }
# LLVM-NEXT: ]
# GNU: GNUStyle::printBBAddrMaps not implemented
# TRUNCATED: BBAddrMap [
# TRUNCATED-NEXT: warning: '[[FILE]]': unable to dump SHT_LLVM_BB_ADDR_MAP section with index 1: unable to decode LEB128 at offset [[OFFSET]]: malformed uleb128, extends past end
# TRUNCATED-NEXT: ]
## Check that the other valid section is properly dumped.
# TRUNCATED-NEXT: BBAddrMap [
# TRUNCATED-NEXT: Function {
# TRUNCATED-NEXT: At: 0x33333
# TRUNCATED-NEXT: BB entries [
# TRUNCATED-NEXT: {
# TRUNCATED-NEXT: Offset: 0x9
# TRUNCATED-NEXT: Size: 0xA
# TRUNCATED-NEXT: HasReturn: Yes
# TRUNCATED-NEXT: HasTailCall: Yes
# TRUNCATED-NEXT: IsEHPad: No
# TRUNCATED-NEXT: }
# TRUNCATED-NEXT: ]
# TRUNCATED-NEXT: }
# TRUNCATED-NEXT: ]
--- !ELF
FileHeader:
Class: ELFCLASS[[BITS]]
Data: ELFDATA2LSB
Type: ET_EXEC
Sections:
- Name: bb_addr_map_1
Type: SHT_LLVM_BB_ADDR_MAP
ShSize: [[SIZE=<none>]]
Entries:
- Address: [[ADDR=0x11111]]
BBEntries:
- AddressOffset: 0x0
Size: 0x1
Metadata: 0xF0000002
- AddressOffset: 0x3
Size: 0x4
Metadata: 0x5
- Address: 0x22222
BBEntries:
- AddressOffset: 0x6
Size: 0x7
Metadata: 0x8
- Name: dummy_section
Type: SHT_PROGBITS
Size: 16
- Name: bb_addr_map_2
Type: SHT_LLVM_BB_ADDR_MAP
Entries:
- Address: 0x33333
BBEntries:
- AddressOffset: 0x9
Size: 0xa
Metadata: 0xb

View File

@ -550,6 +550,7 @@ public:
void printVersionDependencySection(const Elf_Shdr *Sec) override;
void printHashHistograms() override;
void printCGProfile() override;
void printBBAddrMaps() override;
void printAddrsig() override;
void printNotes() override;
void printELFLinkerOptions() override;
@ -660,6 +661,7 @@ public:
void printVersionDependencySection(const Elf_Shdr *Sec) override;
void printHashHistograms() override;
void printCGProfile() override;
void printBBAddrMaps() override;
void printAddrsig() override;
void printNotes() override;
void printELFLinkerOptions() override;
@ -4617,6 +4619,10 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
OS << "GNUStyle::printCGProfile not implemented\n";
}
template <class ELFT> void GNUELFDumper<ELFT>::printBBAddrMaps() {
OS << "GNUStyle::printBBAddrMaps not implemented\n";
}
static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) {
std::vector<uint64_t> Ret;
const uint8_t *Cur = Data.begin();
@ -6520,6 +6526,34 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
}
}
template <class ELFT> void LLVMELFDumper<ELFT>::printBBAddrMaps() {
for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
if (Sec.sh_type != SHT_LLVM_BB_ADDR_MAP)
continue;
ListScope L(W, "BBAddrMap");
Expected<std::vector<Elf_BBAddrMap>> BBAddrMapOrErr =
this->Obj.decodeBBAddrMap(Sec);
if (!BBAddrMapOrErr) {
this->reportUniqueWarning("unable to dump " + this->describe(Sec) + ": " +
toString(BBAddrMapOrErr.takeError()));
continue;
}
for (const Elf_BBAddrMap &AM : *BBAddrMapOrErr) {
DictScope D(W, "Function");
W.printHex("At", AM.Addr);
ListScope L(W, "BB entries");
for (const typename Elf_BBAddrMap::BBEntry &BBE : AM.BBEntries) {
DictScope L(W);
W.printHex("Offset", BBE.Offset);
W.printHex("Size", BBE.Size);
W.printBoolean("HasReturn", BBE.HasReturn);
W.printBoolean("HasTailCall", BBE.HasTailCall);
W.printBoolean("IsEHPad", BBE.IsEHPad);
}
}
}
}
template <class ELFT> void LLVMELFDumper<ELFT>::printAddrsig() {
ListScope L(W, "Addrsig");
if (!this->DotAddrsigSec)

View File

@ -72,6 +72,7 @@ public:
virtual void printGroupSections() {}
virtual void printHashHistograms() {}
virtual void printCGProfile() {}
virtual void printBBAddrMaps() {}
virtual void printAddrsig() {}
virtual void printNotes() {}
virtual void printELFLinkerOptions() {}

View File

@ -367,6 +367,10 @@ namespace opts {
cl::alias ELFCGProfile("elf-cg-profile", cl::desc("Alias for --cg-profile"),
cl::aliasopt(CGProfile));
// --bb-addr-map
cl::opt<bool> BBAddrMap("bb-addr-map",
cl::desc("Display the BB address map section"));
// -addrsig
cl::opt<bool> Addrsig("addrsig",
cl::desc("Display address-significance table"));
@ -542,6 +546,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
Dumper->printHashHistograms();
if (opts::CGProfile)
Dumper->printCGProfile();
if (opts::BBAddrMap)
Dumper->printBBAddrMaps();
if (opts::Addrsig)
Dumper->printAddrsig();
if (opts::Notes)

View File

@ -482,3 +482,111 @@ Sections:
DoCheck(0xFFFFFFFF, "can't read an entry at 0x17ffffffe8: it goes past the "
"end of the section (0x18)");
}
// Tests for error paths of the ELFFile::decodeBBAddrMap API.
TEST(ELFObjectFileTest, InvalidBBAddrMap) {
StringRef CommonYamlString(R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Sections:
- Name: .llvm_bb_addr_map
Type: SHT_LLVM_BB_ADDR_MAP
Entries:
- Address: 0x11111
BBEntries:
- AddressOffset: 0x0
Size: 0x1
Metadata: 0x2
)");
auto DoCheck = [&](StringRef YamlString, const char *ErrMsg) {
SmallString<0> Storage;
Expected<ELFObjectFile<ELF64LE>> ElfOrErr =
toBinary<ELF64LE>(Storage, YamlString);
ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded());
const ELFFile<ELF64LE> &Elf = ElfOrErr->getELFFile();
Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr =
Elf.getSection(1);
ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded());
EXPECT_THAT_ERROR(Elf.decodeBBAddrMap(**BBAddrMapSecOrErr).takeError(),
FailedWithMessage(ErrMsg));
};
// Check that we can detect the malformed encoding when the section is
// truncated.
SmallString<128> TruncatedYamlString(CommonYamlString);
TruncatedYamlString += R"(
ShSize: 0x8
)";
DoCheck(TruncatedYamlString, "unable to decode LEB128 at offset 0x00000008: "
"malformed uleb128, extends past end");
// Check that we can detect when the encoded BB entry fields exceed the UINT32
// limit.
SmallVector<SmallString<128>, 3> OverInt32LimitYamlStrings(3,
CommonYamlString);
OverInt32LimitYamlStrings[0] += R"(
- AddressOffset: 0x100000000
Size: 0xFFFFFFFF
Metadata: 0xFFFFFFFF
)";
OverInt32LimitYamlStrings[1] += R"(
- AddressOffset: 0xFFFFFFFF
Size: 0x100000000
Metadata: 0xFFFFFFFF
)";
OverInt32LimitYamlStrings[2] += R"(
- AddressOffset: 0xFFFFFFFF
Size: 0xFFFFFFFF
Metadata: 0x100000000
)";
DoCheck(OverInt32LimitYamlStrings[0],
"ULEB128 value at offset 0xc exceeds UINT32_MAX (0x100000000)");
DoCheck(OverInt32LimitYamlStrings[1],
"ULEB128 value at offset 0x11 exceeds UINT32_MAX (0x100000000)");
DoCheck(OverInt32LimitYamlStrings[2],
"ULEB128 value at offset 0x16 exceeds UINT32_MAX (0x100000000)");
// Check the proper error handling when the section has fields exceeding
// UINT32 and is also truncated. This is for checking that we don't generate
// unhandled errors.
SmallVector<SmallString<128>, 3> OverInt32LimitAndTruncated(
3, OverInt32LimitYamlStrings[1]);
// Truncate before the end of the 5-byte field.
OverInt32LimitAndTruncated[0] += R"(
ShSize: 0x15
)";
// Truncate at the end of the 5-byte field.
OverInt32LimitAndTruncated[1] += R"(
ShSize: 0x16
)";
// Truncate after the end of the 5-byte field.
OverInt32LimitAndTruncated[2] += R"(
ShSize: 0x17
)";
DoCheck(OverInt32LimitAndTruncated[0],
"unable to decode LEB128 at offset 0x00000011: malformed uleb128, "
"extends past end");
DoCheck(OverInt32LimitAndTruncated[1],
"ULEB128 value at offset 0x11 exceeds UINT32_MAX (0x100000000)");
DoCheck(OverInt32LimitAndTruncated[2],
"ULEB128 value at offset 0x11 exceeds UINT32_MAX (0x100000000)");
// Check for proper error handling when the 'NumBlocks' field is overridden
// with an out-of-range value.
SmallString<128> OverLimitNumBlocks(CommonYamlString);
OverLimitNumBlocks += R"(
NumBlocks: 0x100000000
)";
DoCheck(OverLimitNumBlocks,
"ULEB128 value at offset 0x8 exceeds UINT32_MAX (0x100000000)");
}