From: Tim Northover Date: Fri, 1 Aug 2014 13:07:19 +0000 (+0000) Subject: llvm-objdump: implement printing for MachO __compact_unwind info. X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4bd286ab5384749511af13df668d374dab5b1554;p=platform%2Fupstream%2Fllvm.git llvm-objdump: implement printing for MachO __compact_unwind info. llvm-svn: 214509 --- diff --git a/llvm/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 b/llvm/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 new file mode 100644 index 0000000..174d383 Binary files /dev/null and b/llvm/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 differ diff --git a/llvm/test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64 b/llvm/test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64 new file mode 100644 index 0000000..fde1bb5 Binary files /dev/null and b/llvm/test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64 differ diff --git a/llvm/test/tools/llvm-objdump/macho-compact-unwind-i386.test b/llvm/test/tools/llvm-objdump/macho-compact-unwind-i386.test new file mode 100644 index 0000000..9a14c20 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/macho-compact-unwind-i386.test @@ -0,0 +1,27 @@ +# RUN: llvm-objdump -unwind-info %p/Inputs/compact-unwind.macho-i386 | FileCheck %s + +# CHECK: Contents of __compact_unwind section: +# CHECK: Entry at offset 0x0: +# CHECK: start: 0x0 __Z10test_throwv +# CHECK: length: 0x55 +# CHECK: compact encoding: 0x01010005 +# CHECK-NOT: personality function +# CHECK-NOT: LSDA +# CHECK: Entry at offset 0x14: +# CHECK: start: 0x60 __Z11test_catch1v +# CHECK: length: 0x6f +# CHECK: compact encoding: 0x41000000 +# CHECK: personality function: 0x288 __pointers + 0x8 +# CHECK: LSDA: 0x180 GCC_except_table1 +# CHECK: Entry at offset 0x28: +# CHECK: start: 0xd0 __Z11test_catch2v +# CHECK: length: 0x75 +# CHECK: compact encoding: 0x41000000 +# CHECK: personality function: 0x288 __pointers + 0x8 +# CHECK: LSDA: 0x1a8 GCC_except_table2 +# CHECK: Entry at offset 0x3c: +# CHECK: start: 0x150 __Z3foov +# CHECK: length: 0x22 +# CHECK: compact encoding: 0x01000000 +# CHECK-NOT: personality function +# CHECK-NOT: LSDA diff --git a/llvm/test/tools/llvm-objdump/macho-compact-unwind-x86_64.test b/llvm/test/tools/llvm-objdump/macho-compact-unwind-x86_64.test new file mode 100644 index 0000000..852800d --- /dev/null +++ b/llvm/test/tools/llvm-objdump/macho-compact-unwind-x86_64.test @@ -0,0 +1,27 @@ +# RUN: llvm-objdump -unwind-info %p/Inputs/compact-unwind.macho-x86_64 | FileCheck %s + +# CHECK: Contents of __compact_unwind section: +# CHECK: Entry at offset 0x0: +# CHECK: start: 0x1 __Z10test_throwv + 0x1 +# CHECK: length: 0x44 +# CHECK: compact encoding: 0x01000000 +# CHECK-NOT: personality function +# CHECK-NOT: LSDA +# CHECK: Entry at offset 0x20: +# CHECK: start: 0x50 __Z11test_catch1v +# CHECK: length: 0x71 +# CHECK: compact encoding: 0x41000000 +# CHECK: personality function: 0x0 ___gxx_personality_v0 +# CHECK: LSDA: 0x180 GCC_except_table1 +# CHECK: Entry at offset 0x40: +# CHECK: start: 0xd0 __Z11test_catch2v +# CHECK: length: 0x77 +# CHECK: compact encoding: 0x41000000 +# CHECK: personality function: 0x0 ___gxx_personality_v0 +# CHECK: LSDA: 0x1a8 GCC_except_table2 +# CHECK: Entry at offset 0x60: +# CHECK: start: 0x150 __Z3foov +# CHECK: length: 0x25 +# CHECK: compact encoding: 0x01000000 +# CHECK-NOT: personality function +# CHECK-NOT: LSDA diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index 1c893ea..808bc92 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/MachO.h" @@ -460,3 +461,225 @@ static void DisassembleInputMachO2(StringRef Filename, } } } + +namespace { +struct CompactUnwindEntry { + uint32_t OffsetInSection; + + uint64_t FunctionAddr; + uint32_t Length; + uint32_t CompactEncoding; + uint64_t PersonalityAddr; + uint64_t LSDAAddr; + + RelocationRef FunctionReloc; + RelocationRef PersonalityReloc; + RelocationRef LSDAReloc; + + CompactUnwindEntry(StringRef Contents, unsigned Offset, bool Is64) + : OffsetInSection(Offset) { + if (Is64) + read(Contents.data() + Offset); + else + read(Contents.data() + Offset); + } + +private: + template + static uint64_t readNext(const char *&Buf) { + using llvm::support::little; + using llvm::support::unaligned; + + uint64_t Val = support::endian::read(Buf); + Buf += sizeof(T); + return Val; + } + + template + void read(const char *Buf) { + FunctionAddr = readNext(Buf); + Length = readNext(Buf); + CompactEncoding = readNext(Buf); + PersonalityAddr = readNext(Buf); + LSDAAddr = readNext(Buf); + } +}; +} + +/// Given a relocation from __compact_unwind, consisting of the RelocationRef +/// and data being relocated, determine the best base Name and Addend to use for +/// display purposes. +/// +/// 1. An Extern relocation will directly reference a symbol (and the data is +/// then already an addend), so use that. +/// 2. Otherwise the data is an offset in the object file's layout; try to find +// a symbol before it in the same section, and use the offset from there. +/// 3. Finally, if all that fails, fall back to an offset from the start of the +/// referenced section. +static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, + std::map &Symbols, + const RelocationRef &Reloc, + uint64_t Addr, + StringRef &Name, uint64_t &Addend) { + if (Reloc.getSymbol() != Obj->symbol_end()) { + Reloc.getSymbol()->getName(Name); + Addend = Addr; + return; + } + + auto RE = Obj->getRelocation(Reloc.getRawDataRefImpl()); + SectionRef RelocSection = Obj->getRelocationSection(RE); + + uint64_t SectionAddr; + RelocSection.getAddress(SectionAddr); + + auto Sym = Symbols.upper_bound(Addr); + if (Sym == Symbols.begin()) { + // The first symbol in the object is after this reference, the best we can + // do is section-relative notation. + RelocSection.getName(Name); + Addend = Addr - SectionAddr; + return; + } + + // Go back one so that SymbolAddress <= Addr. + --Sym; + + section_iterator SymSection = Obj->section_end(); + Sym->second.getSection(SymSection); + if (RelocSection == *SymSection) { + // There's a valid symbol in the same section before this reference. + Sym->second.getName(Name); + Addend = Addr - Sym->first; + return; + } + + // There is a symbol before this reference, but it's in a different + // section. Probably not helpful to mention it, so use the section name. + RelocSection.getName(Name); + Addend = Addr - SectionAddr; +} + +static void printUnwindRelocDest(const MachOObjectFile *Obj, + std::map &Symbols, + const RelocationRef &Reloc, + uint64_t Addr) { + StringRef Name; + uint64_t Addend; + + findUnwindRelocNameAddend(Obj, Symbols, Reloc, Addr, Name, Addend); + + outs() << Name; + if (Addend) + outs() << " + " << format("0x%x", Addend); +} + +static void +printMachOCompactUnwindSection(const MachOObjectFile *Obj, + std::map &Symbols, + const SectionRef &CompactUnwind) { + + assert(Obj->isLittleEndian() && + "There should not be a big-endian .o with __compact_unwind"); + + bool Is64 = Obj->is64Bit(); + uint32_t PointerSize = Is64 ? sizeof(uint64_t) : sizeof(uint32_t); + uint32_t EntrySize = 3 * PointerSize + 2 * sizeof(uint32_t); + + StringRef Contents; + CompactUnwind.getContents(Contents); + + SmallVector CompactUnwinds; + + // First populate the initial raw offsets, encodings and so on from the entry. + for (unsigned Offset = 0; Offset < Contents.size(); Offset += EntrySize) { + CompactUnwindEntry Entry(Contents.data(), Offset, Is64); + CompactUnwinds.push_back(Entry); + } + + // Next we need to look at the relocations to find out what objects are + // actually being referred to. + for (const RelocationRef &Reloc : CompactUnwind.relocations()) { + uint64_t RelocAddress; + Reloc.getOffset(RelocAddress); + + uint32_t EntryIdx = RelocAddress / EntrySize; + uint32_t OffsetInEntry = RelocAddress - EntryIdx * EntrySize; + CompactUnwindEntry &Entry = CompactUnwinds[EntryIdx]; + + if (OffsetInEntry == 0) + Entry.FunctionReloc = Reloc; + else if (OffsetInEntry == PointerSize + 2 * sizeof(uint32_t)) + Entry.PersonalityReloc = Reloc; + else if (OffsetInEntry == 2 * PointerSize + 2 * sizeof(uint32_t)) + Entry.LSDAReloc = Reloc; + else + llvm_unreachable("Unexpected relocation in __compact_unwind section"); + } + + // Finally, we're ready to print the data we've gathered. + outs() << "Contents of __compact_unwind section:\n"; + for (auto &Entry : CompactUnwinds) { + outs() << " Entry at offset " << format("0x%x", Entry.OffsetInSection) + << ":\n"; + + // 1. Start of the region this entry applies to. + outs() << " start: " + << format("0x%x", Entry.FunctionAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.FunctionReloc, + Entry.FunctionAddr); + outs() << '\n'; + + // 2. Length of the region this entry applies to. + outs() << " length: " + << format("0x%x", Entry.Length) << '\n'; + // 3. The 32-bit compact encoding. + outs() << " compact encoding: " + << format("0x%08x", Entry.CompactEncoding) << '\n'; + + // 4. The personality function, if present. + if (Entry.PersonalityReloc.getObjectFile()) { + outs() << " personality function: " + << format("0x%x", Entry.PersonalityAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.PersonalityReloc, + Entry.PersonalityAddr); + outs() << '\n'; + } + + // 5. This entry's language-specific data area. + if (Entry.LSDAReloc.getObjectFile()) { + outs() << " LSDA: " + << format("0x%x", Entry.LSDAAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.LSDAReloc, Entry.LSDAAddr); + outs() << '\n'; + } + } +} + +void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) { + std::map Symbols; + for (const SymbolRef &SymRef : Obj->symbols()) { + // Discard any undefined or absolute symbols. They're not going to take part + // in the convenience lookup for unwind info and just take up resources. + section_iterator Section = Obj->section_end(); + SymRef.getSection(Section); + if (Section == Obj->section_end()) + continue; + + uint64_t Addr; + SymRef.getAddress(Addr); + Symbols.insert(std::make_pair(Addr, SymRef)); + } + + for (const SectionRef &Section : Obj->sections()) { + StringRef SectName; + Section.getName(SectName); + if (SectName == "__compact_unwind") + printMachOCompactUnwindSection(Obj, Symbols, Section); + else if (SectName == "__unwind_info") + outs() << "llvm-objdump: warning: unhandled __unwind_info section\n"; + else if (SectName == "__eh_frame") + outs() << "llvm-objdump: warning: unhandled __eh_frame section\n"; + + } +} diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 97087a2..8041a88 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -813,10 +813,12 @@ static void PrintUnwindInfo(const ObjectFile *o) { if (const COFFObjectFile *coff = dyn_cast(o)) { printCOFFUnwindInfo(coff); - } else { + } else if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOUnwindInfo(MachO); + else { // TODO: Extract DWARF dump tool to objdump. errs() << "This operation is only currently supported " - "for COFF object files.\n"; + "for COFF and MachO object files.\n"; return; } } diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h index 80f8f58..6d17f0f 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -18,6 +18,7 @@ namespace llvm { namespace object { class COFFObjectFile; + class MachOObjectFile; class ObjectFile; class RelocationRef; } @@ -31,6 +32,8 @@ bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b); void DumpBytes(StringRef bytes); void DisassembleInputMachO(StringRef Filename); void printCOFFUnwindInfo(const object::COFFObjectFile* o); +void printMachOUnwindInfo(const object::MachOObjectFile* o); + void printELFFileHeader(const object::ObjectFile *o); void printCOFFFileHeader(const object::ObjectFile *o);