llvm-objdump: implement printing for MachO __compact_unwind info.
authorTim Northover <tnorthover@apple.com>
Fri, 1 Aug 2014 13:07:19 +0000 (13:07 +0000)
committerTim Northover <tnorthover@apple.com>
Fri, 1 Aug 2014 13:07:19 +0000 (13:07 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@214509 91177308-0d34-0410-b5e6-96231b3b80d8

test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 [new file with mode: 0644]
test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64 [new file with mode: 0644]
test/tools/llvm-objdump/macho-compact-unwind-i386.test [new file with mode: 0644]
test/tools/llvm-objdump/macho-compact-unwind-x86_64.test [new file with mode: 0644]
tools/llvm-objdump/MachODump.cpp
tools/llvm-objdump/llvm-objdump.cpp
tools/llvm-objdump/llvm-objdump.h

diff --git a/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 b/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386
new file mode 100644 (file)
index 0000000..174d383
Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 differ
diff --git a/test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64 b/test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64
new file mode 100644 (file)
index 0000000..fde1bb5
Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64 differ
diff --git a/test/tools/llvm-objdump/macho-compact-unwind-i386.test b/test/tools/llvm-objdump/macho-compact-unwind-i386.test
new file mode 100644 (file)
index 0000000..9a14c20
--- /dev/null
@@ -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/test/tools/llvm-objdump/macho-compact-unwind-x86_64.test b/test/tools/llvm-objdump/macho-compact-unwind-x86_64.test
new file mode 100644 (file)
index 0000000..852800d
--- /dev/null
@@ -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
index 1c893ea3fad94f0364f02013ce5bce3d2108aa74..808bc9296d830d5df983afc968a62f5c5c647a6b 100644 (file)
@@ -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<uint64_t>(Contents.data() + Offset);
+    else
+      read<uint32_t>(Contents.data() + Offset);
+  }
+
+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);
+    Length = readNext<uint32_t>(Buf);
+    CompactEncoding = readNext<uint32_t>(Buf);
+    PersonalityAddr = readNext<UIntPtr>(Buf);
+    LSDAAddr = readNext<UIntPtr>(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<uint64_t, SymbolRef> &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<uint64_t, SymbolRef> &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<uint64_t, SymbolRef> &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<CompactUnwindEntry, 4> 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<uint64_t, SymbolRef> 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";
+
+  }
+}
index 97087a276c70eaf7fa261267e4119e82d5480882..8041a88396aa33dbcd1fa72b88e229f5c5aa865b 100644 (file)
@@ -813,10 +813,12 @@ static void PrintUnwindInfo(const ObjectFile *o) {
 
   if (const COFFObjectFile *coff = dyn_cast<COFFObjectFile>(o)) {
     printCOFFUnwindInfo(coff);
-  } else {
+  } else if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(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;
   }
 }
index 80f8f581a880e3c61664eaa39cbcb4dfb97ed52c..6d17f0f09fb86855b0ae9f36296487a03bfb1a54 100644 (file)
@@ -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);