From bc41190a74eabd0020c089c67beb9cb706f630ed Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 8 Jan 2014 03:28:09 +0000 Subject: [PATCH] llvm-readobj: add support for ARM EHABI unwind info 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 | 203 +++++++++++++++++++ tools/llvm-readobj/ARMEHABIPrinter.h | 278 +++++++++++++++++++++++++++ tools/llvm-readobj/ELFDumper.cpp | 13 ++ 3 files changed, 494 insertions(+) create mode 100644 test/tools/llvm-readobj/arm-unwind.s create mode 100644 tools/llvm-readobj/ARMEHABIPrinter.h diff --git a/test/tools/llvm-readobj/arm-unwind.s b/test/tools/llvm-readobj/arm-unwind.s new file mode 100644 index 00000000000..7d23a05f1ad --- /dev/null +++ b/test/tools/llvm-readobj/arm-unwind.s @@ -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 index 00000000000..f6c14b9610d --- /dev/null +++ b/tools/llvm-readobj/ARMEHABIPrinter.h @@ -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 +class PrinterContext { + StreamWriter &SW; + const object::ELFFile *ELF; + + typedef typename object::ELFFile::Elf_Sym Elf_Sym; + typedef typename object::ELFFile::Elf_Shdr Elf_Shdr; + + typedef typename object::ELFFile::Elf_Rel_Iter Elf_Rel_iterator; + typedef typename object::ELFFile::Elf_Sym_Iter Elf_Sym_iterator; + typedef typename object::ELFFile::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 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 ByteCode) const; + +public: + PrinterContext(StreamWriter &Writer, const object::ELFFile *File) + : SW(Writer), ELF(File) {} + + void PrintUnwindInformation() const; +}; + +template +const size_t PrinterContext::IndexTableEntrySize = 8; + +template +ErrorOr PrinterContext::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 +const typename object::ELFFile::Elf_Shdr * +PrinterContext::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::Elf_Rela RelA; + RelA.r_offset = RI->r_offset; + RelA.r_info = RI->r_info; + RelA.r_addend = 0; + + std::pair Symbol = + ELF->getRelocationSymbol(&(*SI), &RelA); + + return ELF->getSection(Symbol.second); + } + } + } + } + return NULL; +} + +template +void PrinterContext::PrintExceptionTable(const Elf_Shdr *IT, + const Elf_Shdr *EHT, + uint64_t TableEntryOffset) const { + ErrorOr > 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(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 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(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 Name = FunctionAtAddress(EHT->sh_link, Address)) + SW.printString("PersonalityRoutineName", *Name); + } +} + +template +void PrinterContext::PrintByteCode(const ArrayRef ByteCode) const { + ListScope BC(SW, "ByteCode"); + for (unsigned BCI = 0, BCE = ByteCode.size(); BCI != BCE; ++BCI) + SW.printHex("Instruction", ByteCode[BCE - BCI - 1]); +} + +template +void PrinterContext::PrintIndexTable(unsigned SectionIndex, + const Elf_Shdr *IT) const { + ErrorOr > 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(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 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 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 +void PrinterContext::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 SectionName = ELF->getSectionName(IT)) + SW.printString("SectionName", *SectionName); + SW.printHex("SectionOffset", IT->sh_offset); + + PrintIndexTable(SectionIndex, IT); + } + } +} +} +} +} + +#endif + diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 0e0c0e08c09..b82cecdd4d9 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -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::printUnwindInfo() { W.startLine() << "UnwindInfo not implemented.\n"; } +namespace { +template <> +void ELFDumper >::printUnwindInfo() { + const unsigned Machine = Obj->getHeader()->e_machine; + if (Machine == EM_ARM) { + ARM::EHABI::PrinterContext > Ctx(W, Obj); + return Ctx.PrintUnwindInformation(); + } + W.startLine() << "UnwindInfo not implemented.\n"; +} +} + template void ELFDumper::printDynamicTable() { typedef typename ELFO::Elf_Dyn_Iter EDI; -- 2.34.1