mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
llvm-objdump: print contents of MachO __unwind_info sections
llvm-svn: 215437
This commit is contained in:
parent
fd30e29785
commit
3a72195452
BIN
test/tools/llvm-objdump/Inputs/unwind-info.macho-arm64
Executable file
BIN
test/tools/llvm-objdump/Inputs/unwind-info.macho-arm64
Executable file
Binary file not shown.
BIN
test/tools/llvm-objdump/Inputs/unwind-info.macho-x86_64
Executable file
BIN
test/tools/llvm-objdump/Inputs/unwind-info.macho-x86_64
Executable file
Binary file not shown.
28
test/tools/llvm-objdump/macho-unwind-info-arm64.test
Normal file
28
test/tools/llvm-objdump/macho-unwind-info-arm64.test
Normal file
@ -0,0 +1,28 @@
|
||||
# RUN: llvm-objdump -unwind-info %p/Inputs/unwind-info.macho-arm64 2>/dev/null | FileCheck %s
|
||||
|
||||
# The 2nd level index here is "regular", including all offsets & encodings in
|
||||
# full.
|
||||
|
||||
# CHECK: Contents of __unwind_info section:
|
||||
# CHECK: Version: 0x1
|
||||
# CHECK: Common encodings array section offset: 0x1c
|
||||
# CHECK: Number of common encodings in array: 0x2
|
||||
# CHECK: Personality function array section offset: 0x24
|
||||
# CHECK: Number of personality functions in array: 0x1
|
||||
# CHECK: Index array section offset: 0x28
|
||||
# CHECK: Number of indices in array: 0x2
|
||||
# CHECK: Common encodings: (count = 2)
|
||||
# CHECK: encoding[0]: 0x04000000
|
||||
# CHECK: encoding[1]: 0x54000000
|
||||
# CHECK: Personality functions: (count = 1)
|
||||
# CHECK: personality[1]: 0x00008008
|
||||
# CHECK: Top level indices: (count = 2)
|
||||
# CHECK: [0]: function offset=0x00007d64, 2nd level page offset=0x00000050, LSDA offset=0x00000040
|
||||
# CHECK: [1]: function offset=0x00007eb5, 2nd level page offset=0x00000000, LSDA offset=0x00000050
|
||||
# CHECK: LSDA descriptors:
|
||||
# CHECK: [0]: function offset=0x00007d90, LSDA offset=0x00007f44
|
||||
# CHECK: [1]: function offset=0x00007e10, LSDA offset=0x00007f6c
|
||||
# CHECK: Second level indices:
|
||||
# CHECK: Second level index[0]: offset in section=0x00000050, base function offset=0x00007d64
|
||||
# CHECK: [0]: function offset=0x00007d90, encoding=0x78563412
|
||||
# CHECK: [1]: function offset=0x00007e10, encoding=0x21436587
|
29
test/tools/llvm-objdump/macho-unwind-info-x86_64.test
Normal file
29
test/tools/llvm-objdump/macho-unwind-info-x86_64.test
Normal file
@ -0,0 +1,29 @@
|
||||
# RUN: llvm-objdump -unwind-info %p/Inputs/unwind-info.macho-x86_64 2>/dev/null | FileCheck %s
|
||||
|
||||
# The 2nd level index in this file is in compressed form, referring to both
|
||||
# common and packed encodings.
|
||||
|
||||
# CHECK:Contents of __unwind_info section:
|
||||
# CHECK: Version: 0x1
|
||||
# CHECK: Common encodings array section offset: 0x1c
|
||||
# CHECK: Number of common encodings in array: 0x2
|
||||
# CHECK: Personality function array section offset: 0x24
|
||||
# CHECK: Number of personality functions in array: 0x1
|
||||
# CHECK: Index array section offset: 0x28
|
||||
# CHECK: Number of indices in array: 0x2
|
||||
# CHECK: Common encodings: (count = 2)
|
||||
# CHECK: encoding[0]: 0x01000000
|
||||
# CHECK: encoding[1]: 0x51000000
|
||||
# CHECK: Personality functions: (count = 1)
|
||||
# CHECK: personality[1]: 0x00001018
|
||||
# CHECK: Top level indices: (count = 2)
|
||||
# CHECK: [0]: function offset=0x00000d70, 2nd level page offset=0x00000050, LSDA offset=0x00000040
|
||||
# CHECK: [1]: function offset=0x00000eab, 2nd level page offset=0x00000000, LSDA offset=0x00000050
|
||||
# CHECK: LSDA descriptors:
|
||||
# CHECK: [0]: function offset=0x00000db0, LSDA offset=0x00000f0c
|
||||
# CHECK: [1]: function offset=0x00000e20, LSDA offset=0x00000f34
|
||||
# CHECK: Second level indices:
|
||||
# CHECK: Second level index[0]: offset in section=0x00000050, base function offset=0x00000d70
|
||||
# CHECK: [0]: function offset=0x00000d70, encoding[0]=0x01000000
|
||||
# CHECK: [1]: function offset=0x00000db0, encoding[1]=0x51000000
|
||||
# CHECK: [2]: function offset=0x00000e20, encoding[2]=0x01234567
|
@ -471,7 +471,22 @@ static void DisassembleInputMachO2(StringRef Filename,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// __compact_unwind section dumping
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T> static uint64_t readNext(const char *&Buf) {
|
||||
using llvm::support::little;
|
||||
using llvm::support::unaligned;
|
||||
|
||||
uint64_t Val = support::endian::read<T, little, unaligned>(Buf);
|
||||
Buf += sizeof(T);
|
||||
return Val;
|
||||
}
|
||||
|
||||
struct CompactUnwindEntry {
|
||||
uint32_t OffsetInSection;
|
||||
|
||||
@ -494,16 +509,6 @@ struct CompactUnwindEntry {
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static uint64_t readNext(const char *&Buf) {
|
||||
using llvm::support::little;
|
||||
using llvm::support::unaligned;
|
||||
|
||||
uint64_t Val = support::endian::read<T, little, unaligned>(Buf);
|
||||
Buf += sizeof(T);
|
||||
return Val;
|
||||
}
|
||||
|
||||
template<typename UIntPtr>
|
||||
void read(const char *Buf) {
|
||||
FunctionAddr = readNext<UIntPtr>(Buf);
|
||||
@ -665,6 +670,239 @@ printMachOCompactUnwindSection(const MachOObjectFile *Obj,
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// __unwind_info section dumping
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static void printRegularSecondLevelUnwindPage(const char *PageStart) {
|
||||
const char *Pos = PageStart;
|
||||
uint32_t Kind = readNext<uint32_t>(Pos);
|
||||
(void)Kind;
|
||||
assert(Kind == 2 && "kind for a regular 2nd level index should be 2");
|
||||
|
||||
uint16_t EntriesStart = readNext<uint16_t>(Pos);
|
||||
uint16_t NumEntries = readNext<uint16_t>(Pos);
|
||||
|
||||
Pos = PageStart + EntriesStart;
|
||||
for (unsigned i = 0; i < NumEntries; ++i) {
|
||||
uint32_t FunctionOffset = readNext<uint32_t>(Pos);
|
||||
uint32_t Encoding = readNext<uint32_t>(Pos);
|
||||
|
||||
outs() << " [" << i << "]: "
|
||||
<< "function offset="
|
||||
<< format("0x%08" PRIx32, FunctionOffset) << ", "
|
||||
<< "encoding="
|
||||
<< format("0x%08" PRIx32, Encoding)
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
|
||||
static void printCompressedSecondLevelUnwindPage(
|
||||
const char *PageStart, uint32_t FunctionBase,
|
||||
const SmallVectorImpl<uint32_t> &CommonEncodings) {
|
||||
const char *Pos = PageStart;
|
||||
uint32_t Kind = readNext<uint32_t>(Pos);
|
||||
(void)Kind;
|
||||
assert(Kind == 3 && "kind for a compressed 2nd level index should be 3");
|
||||
|
||||
uint16_t EntriesStart = readNext<uint16_t>(Pos);
|
||||
uint16_t NumEntries = readNext<uint16_t>(Pos);
|
||||
|
||||
uint16_t EncodingsStart = readNext<uint16_t>(Pos);
|
||||
readNext<uint16_t>(Pos);
|
||||
auto PageEncodings = (support::ulittle32_t *)(PageStart + EncodingsStart);
|
||||
|
||||
Pos = PageStart + EntriesStart;
|
||||
for (unsigned i = 0; i < NumEntries; ++i) {
|
||||
uint32_t Entry = readNext<uint32_t>(Pos);
|
||||
uint32_t FunctionOffset = FunctionBase + (Entry & 0xffffff);
|
||||
uint32_t EncodingIdx = Entry >> 24;
|
||||
|
||||
uint32_t Encoding;
|
||||
if (EncodingIdx < CommonEncodings.size())
|
||||
Encoding = CommonEncodings[EncodingIdx];
|
||||
else
|
||||
Encoding = PageEncodings[EncodingIdx - CommonEncodings.size()];
|
||||
|
||||
outs() << " [" << i << "]: "
|
||||
<< "function offset="
|
||||
<< format("0x%08" PRIx32, FunctionOffset) << ", "
|
||||
<< "encoding[" << EncodingIdx << "]="
|
||||
<< format("0x%08" PRIx32, Encoding)
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printMachOUnwindInfoSection(const MachOObjectFile *Obj,
|
||||
std::map<uint64_t, SymbolRef> &Symbols,
|
||||
const SectionRef &UnwindInfo) {
|
||||
|
||||
assert(Obj->isLittleEndian() &&
|
||||
"There should not be a big-endian .o with __unwind_info");
|
||||
|
||||
outs() << "Contents of __unwind_info section:\n";
|
||||
|
||||
StringRef Contents;
|
||||
UnwindInfo.getContents(Contents);
|
||||
const char *Pos = Contents.data();
|
||||
|
||||
//===----------------------------------
|
||||
// Section header
|
||||
//===----------------------------------
|
||||
|
||||
uint32_t Version = readNext<uint32_t>(Pos);
|
||||
outs() << " Version: "
|
||||
<< format("0x%" PRIx32, Version) << '\n';
|
||||
assert(Version == 1 && "only understand version 1");
|
||||
|
||||
uint32_t CommonEncodingsStart = readNext<uint32_t>(Pos);
|
||||
outs() << " Common encodings array section offset: "
|
||||
<< format("0x%" PRIx32, CommonEncodingsStart) << '\n';
|
||||
uint32_t NumCommonEncodings = readNext<uint32_t>(Pos);
|
||||
outs() << " Number of common encodings in array: "
|
||||
<< format("0x%" PRIx32, NumCommonEncodings) << '\n';
|
||||
|
||||
uint32_t PersonalitiesStart = readNext<uint32_t>(Pos);
|
||||
outs() << " Personality function array section offset: "
|
||||
<< format("0x%" PRIx32, PersonalitiesStart) << '\n';
|
||||
uint32_t NumPersonalities = readNext<uint32_t>(Pos);
|
||||
outs() << " Number of personality functions in array: "
|
||||
<< format("0x%" PRIx32, NumPersonalities) << '\n';
|
||||
|
||||
uint32_t IndicesStart = readNext<uint32_t>(Pos);
|
||||
outs() << " Index array section offset: "
|
||||
<< format("0x%" PRIx32, IndicesStart) << '\n';
|
||||
uint32_t NumIndices = readNext<uint32_t>(Pos);
|
||||
outs() << " Number of indices in array: "
|
||||
<< format("0x%" PRIx32, NumIndices) << '\n';
|
||||
|
||||
//===----------------------------------
|
||||
// A shared list of common encodings
|
||||
//===----------------------------------
|
||||
|
||||
// These occupy indices in the range [0, N] whenever an encoding is referenced
|
||||
// from a compressed 2nd level index table. In practice the linker only
|
||||
// creates ~128 of these, so that indices are available to embed encodings in
|
||||
// the 2nd level index.
|
||||
|
||||
SmallVector<uint32_t, 64> CommonEncodings;
|
||||
outs() << " Common encodings: (count = " << NumCommonEncodings << ")\n";
|
||||
Pos = Contents.data() + CommonEncodingsStart;
|
||||
for (unsigned i = 0; i < NumCommonEncodings; ++i) {
|
||||
uint32_t Encoding = readNext<uint32_t>(Pos);
|
||||
CommonEncodings.push_back(Encoding);
|
||||
|
||||
outs() << " encoding[" << i << "]: " << format("0x%08" PRIx32, Encoding)
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------
|
||||
// Personality functions used in this executable
|
||||
//===----------------------------------
|
||||
|
||||
// There should be only a handful of these (one per source language,
|
||||
// roughly). Particularly since they only get 2 bits in the compact encoding.
|
||||
|
||||
outs() << " Personality functions: (count = " << NumPersonalities << ")\n";
|
||||
Pos = Contents.data() + PersonalitiesStart;
|
||||
for (unsigned i = 0; i < NumPersonalities; ++i) {
|
||||
uint32_t PersonalityFn = readNext<uint32_t>(Pos);
|
||||
outs() << " personality[" << i + 1
|
||||
<< "]: " << format("0x%08" PRIx32, PersonalityFn) << '\n';
|
||||
}
|
||||
|
||||
//===----------------------------------
|
||||
// The level 1 index entries
|
||||
//===----------------------------------
|
||||
|
||||
// These specify an approximate place to start searching for the more detailed
|
||||
// information, sorted by PC.
|
||||
|
||||
struct IndexEntry {
|
||||
uint32_t FunctionOffset;
|
||||
uint32_t SecondLevelPageStart;
|
||||
uint32_t LSDAStart;
|
||||
};
|
||||
|
||||
SmallVector<IndexEntry, 4> IndexEntries;
|
||||
|
||||
outs() << " Top level indices: (count = " << NumIndices << ")\n";
|
||||
Pos = Contents.data() + IndicesStart;
|
||||
for (unsigned i = 0; i < NumIndices; ++i) {
|
||||
IndexEntry Entry;
|
||||
|
||||
Entry.FunctionOffset = readNext<uint32_t>(Pos);
|
||||
Entry.SecondLevelPageStart = readNext<uint32_t>(Pos);
|
||||
Entry.LSDAStart = readNext<uint32_t>(Pos);
|
||||
IndexEntries.push_back(Entry);
|
||||
|
||||
outs() << " [" << i << "]: "
|
||||
<< "function offset="
|
||||
<< format("0x%08" PRIx32, Entry.FunctionOffset) << ", "
|
||||
<< "2nd level page offset="
|
||||
<< format("0x%08" PRIx32, Entry.SecondLevelPageStart) << ", "
|
||||
<< "LSDA offset="
|
||||
<< format("0x%08" PRIx32, Entry.LSDAStart) << '\n';
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------
|
||||
// Next come the LSDA tables
|
||||
//===----------------------------------
|
||||
|
||||
// The LSDA layout is rather implicit: it's a contiguous array of entries from
|
||||
// the first top-level index's LSDAOffset to the last (sentinel).
|
||||
|
||||
outs() << " LSDA descriptors:\n";
|
||||
Pos = Contents.data() + IndexEntries[0].LSDAStart;
|
||||
int NumLSDAs = (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) /
|
||||
(2 * sizeof(uint32_t));
|
||||
for (int i = 0; i < NumLSDAs; ++i) {
|
||||
uint32_t FunctionOffset = readNext<uint32_t>(Pos);
|
||||
uint32_t LSDAOffset = readNext<uint32_t>(Pos);
|
||||
outs() << " [" << i << "]: "
|
||||
<< "function offset="
|
||||
<< format("0x%08" PRIx32, FunctionOffset) << ", "
|
||||
<< "LSDA offset="
|
||||
<< format("0x%08" PRIx32, LSDAOffset) << '\n';
|
||||
}
|
||||
|
||||
//===----------------------------------
|
||||
// Finally, the 2nd level indices
|
||||
//===----------------------------------
|
||||
|
||||
// Generally these are 4K in size, and have 2 possible forms:
|
||||
// + Regular stores up to 511 entries with disparate encodings
|
||||
// + Compressed stores up to 1021 entries if few enough compact encoding
|
||||
// values are used.
|
||||
outs() << " Second level indices:\n";
|
||||
for (unsigned i = 0; i < IndexEntries.size() - 1; ++i) {
|
||||
// The final sentinel top-level index has no associated 2nd level page
|
||||
if (IndexEntries[i].SecondLevelPageStart == 0)
|
||||
break;
|
||||
|
||||
outs() << " Second level index[" << i << "]: "
|
||||
<< "offset in section="
|
||||
<< format("0x%08" PRIx32, IndexEntries[i].SecondLevelPageStart)
|
||||
<< ", "
|
||||
<< "base function offset="
|
||||
<< format("0x%08" PRIx32, IndexEntries[i].FunctionOffset) << '\n';
|
||||
|
||||
Pos = Contents.data() + IndexEntries[i].SecondLevelPageStart;
|
||||
uint32_t Kind = *(support::ulittle32_t *)Pos;
|
||||
if (Kind == 2)
|
||||
printRegularSecondLevelUnwindPage(Pos);
|
||||
else if (Kind == 3)
|
||||
printCompressedSecondLevelUnwindPage(Pos, IndexEntries[i].FunctionOffset,
|
||||
CommonEncodings);
|
||||
else
|
||||
llvm_unreachable("Do not know how to print this kind of 2nd level page");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) {
|
||||
std::map<uint64_t, SymbolRef> Symbols;
|
||||
for (const SymbolRef &SymRef : Obj->symbols()) {
|
||||
@ -686,7 +924,7 @@ void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) {
|
||||
if (SectName == "__compact_unwind")
|
||||
printMachOCompactUnwindSection(Obj, Symbols, Section);
|
||||
else if (SectName == "__unwind_info")
|
||||
outs() << "llvm-objdump: warning: unhandled __unwind_info section\n";
|
||||
printMachOUnwindInfoSection(Obj, Symbols, Section);
|
||||
else if (SectName == "__eh_frame")
|
||||
outs() << "llvm-objdump: warning: unhandled __eh_frame section\n";
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user