llvm-readobj: add support for ARM EHABI unwind info
authorSaleem Abdulrasool <compnerd@compnerd.org>
Wed, 8 Jan 2014 03:28:09 +0000 (03:28 +0000)
committerSaleem Abdulrasool <compnerd@compnerd.org>
Wed, 8 Jan 2014 03:28:09 +0000 (03:28 +0000)
This adds some preliminary support for decoding ARM EHABI unwinding information.
The major functionality that remains from complete support is bytecode
translation.

Each Unwind Index Table is printed out as a separate entity along with its
section index, name, offset, and entries.

Each entry lists the function address, and if possible, the name, of the
function to which it corresponds.  The encoding model, personality routine or
index, and byte code is also listed.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@198734 91177308-0d34-0410-b5e6-96231b3b80d8

test/tools/llvm-readobj/arm-unwind.s [new file with mode: 0644]
tools/llvm-readobj/ARMEHABIPrinter.h [new file with mode: 0644]
tools/llvm-readobj/ELFDumper.cpp

diff --git a/test/tools/llvm-readobj/arm-unwind.s b/test/tools/llvm-readobj/arm-unwind.s
new file mode 100644 (file)
index 0000000..7d23a05
--- /dev/null
@@ -0,0 +1,203 @@
+@ RUN: llvm-mc -triple armv7-eabi -filetype obj -o - %s | llvm-readobj -u - \
+@ RUN:   | FileCheck %s
+
+       .syntax unified
+
+       .cpu cortex-a8
+       .fpu neon
+
+       .section .personality
+
+       .type __personality,%function
+__personality:
+       .fnstart
+       bkpt
+       .fnend
+
+
+       .section .personality0
+
+       .type personality0,%function
+personality0:
+       .fnstart
+       bx lr
+       .fnend
+
+
+       .section .personality1
+
+       .type personality1,%function
+personality1:
+       .fnstart
+       .pad #0x100
+       sub sp, sp, #0x100
+       .save {r0-r11}
+       push {r0-r11}
+       pop {r0-r11}
+       add sp, sp, #0x100
+       bx lr
+       .fnend
+
+
+       .section .custom_personality
+
+       .type custom_personality,%function
+custom_personality:
+       .fnstart
+       .personality __personality
+       bx lr
+       .fnend
+
+
+       .section .opcodes
+
+       .type opcodes,%function
+opcodes:
+       .fnstart
+       .vsave {d8-d12}
+       vpush {d8-d12}
+       vpop {d8-d12}
+       bx lr
+       .fnend
+
+
+       .section .multiple
+
+       .type function0,%function
+function0:
+       .fnstart
+       bx lr
+       .fnend
+
+       .type function1,%function
+function1:
+       .fnstart
+       .personality __personality
+       bx lr
+       .fnend
+
+       .type function2,%function
+function2:
+       .fnstart
+       bx lr
+       .fnend
+
+@ CHECK: UnwindInformation {
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.personality
+@ CHECK:     Entries [
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x0
+@ CHECK:         FunctionName: __personality
+@ CHECK:         Model: Compact (Inline)
+@ CHECK:         PersonalityIndex: 0
+@ CHECK:         ByteCode [
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:         ]
+@ CHECK:       }
+@ CHECK:     ]
+@ CHECK:   }
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.personality0
+@ CHECK:     Entries [
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x0
+@ CHECK:         FunctionName: personality0
+@ CHECK:         Model: Compact (Inline)
+@ CHECK:         PersonalityIndex: 0
+@ CHECK:         ByteCode [
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:         ]
+@ CHECK:       }
+@ CHECK:     ]
+@ CHECK:   }
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.personality1
+@ CHECK:     Entries [
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x0
+@ CHECK:         FunctionName: personality1
+@ CHECK:         ExceptionHandlingTable: .ARM.extab.personality1
+@ CHECK:         TableEntryOffset: 0x0
+@ CHECK:         Model: Compact
+@ CHECK:         PersonalityIndex: 1
+@ CHECK:         ByteCode [
+@ CHECK:           Instruction: 0xB1
+@ CHECK:           Instruction: 0xF
+@ CHECK:           Instruction: 0xA7
+@ CHECK:           Instruction: 0x3F
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:         ]
+@ CHECK:       }
+@ CHECK:     ]
+@ CHECK:   }
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.custom_personality
+@ CHECK:     Entries [
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x0
+@ CHECK:         FunctionName: custom_personality
+@ CHECK:         ExceptionHandlingTable: .ARM.extab.custom_personality
+@ CHECK:         TableEntryOffset: 0x0
+@ CHECK:         Model: Generic
+@ CHECK:         PersonalityRoutineAddress: 0x0
+@ CHECK:       }
+@ CHECK:     ]
+@ CHECK:   }
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.opcodes
+@ CHECK:     Entries [
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x0
+@ CHECK:         FunctionName: opcodes
+@ CHECK:         Model: Compact (Inline)
+@ CHECK:         PersonalityIndex: 0
+@ CHECK:         ByteCode [
+@ CHECK:           Instruction: 0xC9
+@ CHECK:           Instruction: 0x84
+@ CHECK:           Instruction: 0xB0
+@ CHECK:         ]
+@ CHECK:       }
+@ CHECK:     ]
+@ CHECK:   }
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.multiple
+@ CHECK:     Entries [
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x0
+@ CHECK:         FunctionName: function0
+@ CHECK:         Model: Compact (Inline)
+@ CHECK:         PersonalityIndex: 0
+@ CHECK:         ByteCode [
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:         ]
+@ CHECK:       }
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x4
+@ CHECK:         FunctionName: function1
+@ CHECK:         ExceptionHandlingTable: .ARM.extab.multiple
+@ CHECK:         Model: Generic
+@ CHECK:         PersonalityRoutineAddress: 0x0
+@ CHECK:       }
+@ CHECK:       Entry {
+@ CHECK:         FunctionAddress: 0x8
+@ CHECK:         FunctionName: function2
+@ CHECK:         Model: Compact (Inline)
+@ CHECK:         PersonalityIndex: 0
+@ CHECK:         ByteCode [
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:           Instruction: 0xB0
+@ CHECK:         ]
+@ CHECK:       }
+@ CHECK:     ]
+@ CHECK:   }
+@ CHECK: }
+
diff --git a/tools/llvm-readobj/ARMEHABIPrinter.h b/tools/llvm-readobj/ARMEHABIPrinter.h
new file mode 100644 (file)
index 0000000..f6c14b9
--- /dev/null
@@ -0,0 +1,278 @@
+//===--- ARMEHABIPrinter.h - ARM EHABI Unwind Information Printer ----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_READOBJ_ARMEHABI_PRINTER_H
+#define LLVM_READOBJ_ARMEHABI_PRINTER_H
+
+#include "StreamWriter.h"
+
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/ARMEHABI.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/type_traits.h"
+
+namespace llvm {
+namespace ARM {
+namespace EHABI {
+template <typename ET>
+class PrinterContext {
+  StreamWriter &SW;
+  const object::ELFFile<ET> *ELF;
+
+  typedef typename object::ELFFile<ET>::Elf_Sym Elf_Sym;
+  typedef typename object::ELFFile<ET>::Elf_Shdr Elf_Shdr;
+
+  typedef typename object::ELFFile<ET>::Elf_Rel_Iter Elf_Rel_iterator;
+  typedef typename object::ELFFile<ET>::Elf_Sym_Iter Elf_Sym_iterator;
+  typedef typename object::ELFFile<ET>::Elf_Shdr_Iter Elf_Shdr_iterator;
+
+  static const size_t IndexTableEntrySize;
+
+  static uint64_t PREL31(uint32_t Address, uint32_t Place) {
+    uint64_t Location = Address & 0x7fffffff;
+    if (Location & 0x04000000)
+      Location |= (uint64_t) ~0x7fffffff;
+    return Location + Place;
+  }
+
+  ErrorOr<StringRef> FunctionAtAddress(unsigned Section, uint64_t Address) const;
+  const Elf_Shdr *FindExceptionTable(unsigned IndexTableIndex,
+                                     off_t IndexTableOffset) const;
+
+  void PrintIndexTable(unsigned SectionIndex, const Elf_Shdr *IT) const;
+  void PrintExceptionTable(const Elf_Shdr *IT, const Elf_Shdr *EHT,
+                           uint64_t TableEntryOffset) const;
+  void PrintByteCode(const ArrayRef<uint8_t> ByteCode) const;
+
+public:
+  PrinterContext(StreamWriter &Writer, const object::ELFFile<ET> *File)
+    : SW(Writer), ELF(File) {}
+
+  void PrintUnwindInformation() const;
+};
+
+template <typename ET>
+const size_t PrinterContext<ET>::IndexTableEntrySize = 8;
+
+template <typename ET>
+ErrorOr<StringRef> PrinterContext<ET>::FunctionAtAddress(unsigned Section,
+                                                         uint64_t Address) const {
+  for (Elf_Sym_iterator SI = ELF->begin_symbols(), SE = ELF->end_symbols();
+       SI != SE; ++SI)
+    if (SI->st_shndx == Section && SI->st_value == Address &&
+        SI->getType() == ELF::STT_FUNC)
+      return ELF->getSymbolName(SI);
+  return readobj_error::unknown_symbol;
+}
+
+template <typename ET>
+const typename object::ELFFile<ET>::Elf_Shdr *
+PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex,
+                                       off_t IndexTableOffset) const {
+  /// Iterate through the sections, searching for the relocation section
+  /// associated with the unwind index table section specified by
+  /// IndexSectionIndex.  Iterate the associated section searching for the
+  /// relocation associated with the index table entry specified by
+  /// IndexTableOffset.  The symbol is the section symbol for the exception
+  /// handling table.  Use this symbol to recover the actual exception handling
+  /// table.
+
+  for (Elf_Shdr_iterator SI = ELF->begin_sections(), SE = ELF->end_sections();
+       SI != SE; ++SI) {
+    if (SI->sh_type == ELF::SHT_REL && SI->sh_info == IndexSectionIndex) {
+      for (Elf_Rel_iterator RI = ELF->begin_rel(&*SI), RE = ELF->end_rel(&*SI);
+           RI != RE; ++RI) {
+        if (RI->r_offset == IndexTableOffset) {
+          typename object::ELFFile<ET>::Elf_Rela RelA;
+          RelA.r_offset = RI->r_offset;
+          RelA.r_info = RI->r_info;
+          RelA.r_addend = 0;
+
+          std::pair<const Elf_Shdr *, const Elf_Sym *> Symbol =
+            ELF->getRelocationSymbol(&(*SI), &RelA);
+
+          return ELF->getSection(Symbol.second);
+        }
+      }
+    }
+  }
+  return NULL;
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintExceptionTable(const Elf_Shdr *IT,
+                                             const Elf_Shdr *EHT,
+                                             uint64_t TableEntryOffset) const {
+  ErrorOr<ArrayRef<uint8_t> > Contents = ELF->getSectionContents(EHT);
+  if (!Contents)
+    return;
+
+  /// ARM EHABI Section 6.2 - The generic model
+  ///
+  /// An exception-handling table entry for the generic model is laid out as:
+  ///
+  ///  3 3
+  ///  1 0                            0
+  /// +-+------------------------------+
+  /// |0|  personality routine offset  |
+  /// +-+------------------------------+
+  /// |  personality routine data ...  |
+  ///
+  ///
+  /// ARM EHABI Section 6.3 - The ARM-defined compact model
+  ///
+  /// An exception-handling table entry for the compact model looks like:
+  ///
+  ///  3 3 2 2  2 2
+  ///  1 0 8 7  4 3                     0
+  /// +-+---+----+-----------------------+
+  /// |1| 0 | Ix | data for pers routine |
+  /// +-+---+----+-----------------------+
+  /// |  more personality routine data   |
+
+  const uint32_t Word =
+    *reinterpret_cast<const uint32_t *>(Contents->data() + TableEntryOffset);
+
+  if (Word & 0x80000000) {
+    SW.printString("Model", StringRef("Compact"));
+
+    unsigned PersonalityIndex = (Word & 0x0f000000) >> 24;
+    SW.printNumber("PersonalityIndex", PersonalityIndex);
+
+    switch (PersonalityIndex) {
+    case AEABI_UNWIND_CPP_PR0:
+      PrintByteCode(Contents->slice(TableEntryOffset + 1, 3));
+      break;
+    case AEABI_UNWIND_CPP_PR1:
+    case AEABI_UNWIND_CPP_PR2:
+      unsigned AdditionalWords = (Word & 0x00ff0000) >> 16;
+
+      SmallVector<uint8_t, 10> ByteCode;
+      ByteCode.reserve(2 + 4 * AdditionalWords);
+
+      for (unsigned WI = 1, WE = AdditionalWords; WI <= WE; ++WI)
+        ByteCode.append(Contents->data() + TableEntryOffset + 4 * WI,
+                        Contents->data() + TableEntryOffset + 4 * WI + 4);
+      ByteCode.append(Contents->data() + TableEntryOffset,
+                      Contents->data() + TableEntryOffset + 2);
+
+      PrintByteCode(ArrayRef<uint8_t>(ByteCode.begin(), ByteCode.end()));
+      break;
+    }
+  } else {
+    SW.printString("Model", StringRef("Generic"));
+
+    uint64_t Address = PREL31(Word, EHT->sh_addr);
+    SW.printHex("PersonalityRoutineAddress", Address);
+    if (ErrorOr<StringRef> Name = FunctionAtAddress(EHT->sh_link, Address))
+      SW.printString("PersonalityRoutineName", *Name);
+  }
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintByteCode(const ArrayRef<uint8_t> ByteCode) const {
+  ListScope BC(SW, "ByteCode");
+  for (unsigned BCI = 0, BCE = ByteCode.size(); BCI != BCE; ++BCI)
+    SW.printHex("Instruction", ByteCode[BCE - BCI - 1]);
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintIndexTable(unsigned SectionIndex,
+                                         const Elf_Shdr *IT) const {
+  ErrorOr<ArrayRef<uint8_t> > Contents = ELF->getSectionContents(IT);
+  if (!Contents)
+    return;
+
+  /// ARM EHABI Section 5 - Index Table Entries
+  /// * The first word contains a PREL31 offset to the start of a function with
+  ///   bit 31 clear
+  /// * The second word contains one of:
+  ///   - The PREL31 offset of the start of the table entry for the function,
+  ///     with bit 31 clear
+  ///   - The exception-handling table entry itself with bit 31 set
+  ///   - The special bit pattern EXIDX_CANTUNWIND, indicating that associated
+  ///     frames cannot be unwound
+
+  const uint32_t *Data = reinterpret_cast<const uint32_t *>(Contents->data());
+  const unsigned Entries = IT->sh_size / IndexTableEntrySize;
+
+  ListScope E(SW, "Entries");
+  for (unsigned Entry = 0; Entry < Entries; ++Entry) {
+    DictScope E(SW, "Entry");
+
+    const uint32_t Word0 =
+      Data[Entry * (IndexTableEntrySize / sizeof(*Data)) + 0];
+    const uint32_t Word1 =
+      Data[Entry * (IndexTableEntrySize / sizeof(*Data)) + 1];
+
+    if (Word0 & 0x80000000) {
+      errs() << "corrupt unwind data in section " << SectionIndex << "\n";
+      continue;
+    }
+
+    const uint64_t Offset = PREL31(Word0, IT->sh_addr);
+    SW.printHex("FunctionAddress", Offset);
+    if (ErrorOr<StringRef> Name = FunctionAtAddress(IT->sh_link, Offset))
+      SW.printString("FunctionName", *Name);
+
+    if (Word1 == EXIDX_CANTUNWIND) {
+      SW.printString("Model", StringRef("CantUnwind"));
+      continue;
+    }
+
+    if (Word1 & 0x80000000) {
+      SW.printString("Model", StringRef("Compact (Inline)"));
+
+      unsigned PersonalityIndex = (Word1 & 0x0f000000) >> 24;
+      SW.printNumber("PersonalityIndex", PersonalityIndex);
+
+      PrintByteCode(Contents->slice(Entry * IndexTableEntrySize + 4, 3));
+    } else {
+      const Elf_Shdr *EHT =
+        FindExceptionTable(SectionIndex, Entry * IndexTableEntrySize + 4);
+
+      if (ErrorOr<StringRef> Name = ELF->getSectionName(EHT))
+        SW.printString("ExceptionHandlingTable", *Name);
+
+      uint64_t TableEntryOffset = PREL31(Word1, IT->sh_addr);
+      SW.printHex("TableEntryOffset", TableEntryOffset);
+
+      PrintExceptionTable(IT, EHT, TableEntryOffset);
+    }
+  }
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintUnwindInformation() const {
+  DictScope UI(SW, "UnwindInformation");
+
+  int SectionIndex = 0;
+  for (Elf_Shdr_iterator SI = ELF->begin_sections(), SE = ELF->end_sections();
+       SI != SE; ++SI, ++SectionIndex) {
+    if (SI->sh_type == ELF::SHT_ARM_EXIDX) {
+      const Elf_Shdr *IT = &(*SI);
+
+      DictScope UIT(SW, "UnwindIndexTable");
+
+      SW.printNumber("SectionIndex", SectionIndex);
+      if (ErrorOr<StringRef> SectionName = ELF->getSectionName(IT))
+        SW.printString("SectionName", *SectionName);
+      SW.printHex("SectionOffset", IT->sh_offset);
+
+      PrintIndexTable(SectionIndex, IT);
+    }
+  }
+}
+}
+}
+}
+
+#endif
+
index 0e0c0e08c09698c7a51e4bbba545ed4937ff5ff2..b82cecdd4d978eebde8c8ca91223545e7764f663 100644 (file)
@@ -16,6 +16,7 @@
 #include "Error.h"
 #include "ObjDumper.h"
 #include "StreamWriter.h"
+#include "ARMEHABIPrinter.h"
 
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Object/ELFObjectFile.h"
@@ -774,6 +775,18 @@ void ELFDumper<ELFT>::printUnwindInfo() {
   W.startLine() << "UnwindInfo not implemented.\n";
 }
 
+namespace {
+template <>
+void ELFDumper<ELFType<support::little, 2, false> >::printUnwindInfo() {
+  const unsigned Machine = Obj->getHeader()->e_machine;
+  if (Machine == EM_ARM) {
+    ARM::EHABI::PrinterContext<ELFType<support::little, 2, false> > Ctx(W, Obj);
+    return Ctx.PrintUnwindInformation();
+  }
+  W.startLine() << "UnwindInfo not implemented.\n";
+}
+}
+
 template<class ELFT>
 void ELFDumper<ELFT>::printDynamicTable() {
   typedef typename ELFO::Elf_Dyn_Iter EDI;