/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <vector>
#include <set>
-#include <ext/hash_set>
-
+#include <unordered_set>
+#include "configure.h"
#include "MachOFileAbstraction.hpp"
#include "Architectures.hpp"
typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
-
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
-
UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength,
const char* path, bool showFunctionNames);
bool findUnwindSection();
void printUnwindSection(bool showFunctionNames);
+ void printObjectUnwindSection(bool showFunctionNames);
void getSymbolTableInfo();
- const char* functionName(pint_t addr);
+ const char* functionName(pint_t addr, uint32_t* offset=NULL);
+ const char* personalityName(const macho_relocation_info<typename A::P>* reloc);
+ bool hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr=NULL);
+
static const char* archName();
static void decode(uint32_t encoding, const uint8_t* funcStart, char* str);
};
-template <> const char* UnwindPrinter<ppc>::archName() { return "ppc"; }
-template <> const char* UnwindPrinter<ppc64>::archName() { return "ppc64"; }
template <> const char* UnwindPrinter<x86>::archName() { return "i386"; }
template <> const char* UnwindPrinter<x86_64>::archName() { return "x86_64"; }
template <> const char* UnwindPrinter<arm>::archName() { return "arm"; }
-
-template <>
-bool UnwindPrinter<ppc>::validFile(const uint8_t* fileContent)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
- return false;
- if ( header->cputype() != CPU_TYPE_POWERPC )
- return false;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
- return true;
- }
- return false;
-}
-
-template <>
-bool UnwindPrinter<ppc64>::validFile(const uint8_t* fileContent)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC_64 )
- return false;
- if ( header->cputype() != CPU_TYPE_POWERPC64 )
- return false;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
- return true;
- }
- return false;
-}
+#if SUPPORT_ARCH_arm64
+template <> const char* UnwindPrinter<arm64>::archName() { return "arm64"; }
+#endif
template <>
bool UnwindPrinter<x86>::validFile(const uint8_t* fileContent)
case MH_DYLIB:
case MH_BUNDLE:
case MH_DYLINKER:
+ case MH_OBJECT:
return true;
}
return false;
case MH_DYLIB:
case MH_BUNDLE:
case MH_DYLINKER:
+ case MH_OBJECT:
return true;
}
return false;
}
+
+#if SUPPORT_ARCH_arm64
template <>
-bool UnwindPrinter<arm>::validFile(const uint8_t* fileContent)
+bool UnwindPrinter<arm64>::validFile(const uint8_t* fileContent)
{
const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
+ if ( header->magic() != MH_MAGIC_64 )
return false;
- if ( header->cputype() != CPU_TYPE_ARM )
+ if ( header->cputype() != CPU_TYPE_ARM64 )
return false;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
case MH_BUNDLE:
case MH_DYLINKER:
+ case MH_OBJECT:
return true;
}
return false;
}
-
+#endif
template <typename A>
UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames)
getSymbolTableInfo();
- if ( findUnwindSection() )
- printUnwindSection(showFunctionNames);
+ if ( findUnwindSection() ) {
+ if ( fHeader->filetype() == MH_OBJECT )
+ printObjectUnwindSection(showFunctionNames);
+ else
+ printUnwindSection(showFunctionNames);
+ }
}
}
template <typename A>
-const char* UnwindPrinter<A>::functionName(pint_t addr)
+const char* UnwindPrinter<A>::functionName(pint_t addr, uint32_t* offset)
{
+ const macho_nlist<P>* closestSymbol = NULL;
+ if ( offset != NULL )
+ *offset = 0;
for (uint32_t i=0; i < fSymbolCount; ++i) {
uint8_t type = fSymbols[i].n_type();
if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) {
//fprintf(stderr, "addr=0x%08llX, i=%u, n_type=0x%0X, r=%s\n", (long long)(fSymbols[i].n_value()), i, fSymbols[i].n_type(), r);
return r;
}
+ else if ( offset != NULL ) {
+ if ( closestSymbol == NULL ) {
+ if ( fSymbols[i].n_value() < addr )
+ closestSymbol = &fSymbols[i];
+ }
+ else {
+ if ( (fSymbols[i].n_value() < addr) && (fSymbols[i].n_value() > closestSymbol->n_value()) )
+ closestSymbol = &fSymbols[i];
+ }
+ }
}
}
+ if ( closestSymbol != NULL ) {
+ *offset = addr - closestSymbol->n_value();
+ return &fStrings[closestSymbol->n_strx()];
+ }
return "--anonymous function--";
}
template <typename A>
bool UnwindPrinter<A>::findUnwindSection()
{
+ const char* unwindSectionName = "__unwind_info";
+ const char* unwindSegmentName = "__TEXT";
+ if ( fHeader->filetype() == MH_OBJECT ) {
+ unwindSectionName = "__compact_unwind";
+ unwindSegmentName = "__LD";
+ }
const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
const uint32_t cmd_count = fHeader->ncmds();
const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- if ( (strcmp(sect->sectname(), "__unwind_info") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) {
+ if ( (strncmp(sect->sectname(), unwindSectionName, 16) == 0) && (strcmp(sect->segname(), unwindSegmentName) == 0) ) {
fUnwindSection = sect;
fMachHeaderAddress = segCmd->vmaddr();
return fUnwindSection;
}
+#if SUPPORT_ARCH_arm64
+template <>
+void UnwindPrinter<arm64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
+{
+ uint32_t stackSize;
+ switch ( encoding & UNWIND_ARM64_MODE_MASK ) {
+ case UNWIND_ARM64_MODE_FRAMELESS:
+ stackSize = EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
+ if ( stackSize == 0 )
+ strcpy(str, "no frame, no saved registers ");
+ else
+ sprintf(str, "stack size=%d: ", 16 * stackSize);
+ if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR )
+ strcat(str, "x19/20 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR )
+ strcat(str, "x21/22 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR )
+ strcat(str, "x23/24 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR )
+ strcat(str, "x25/26 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR )
+ strcat(str, "x27/28 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR )
+ strcat(str, "d8/9 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR )
+ strcat(str, "d10/11 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR )
+ strcat(str, "d12/13 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR )
+ strcat(str, "d14/15 ");
+ break;
+ break;
+ case UNWIND_ARM64_MODE_DWARF:
+ sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
+ break;
+ case UNWIND_ARM64_MODE_FRAME:
+ strcpy(str, "std frame: ");
+ if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR )
+ strcat(str, "x19/20 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR )
+ strcat(str, "x21/22 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR )
+ strcat(str, "x23/24 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR )
+ strcat(str, "x25/26 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR )
+ strcat(str, "x27/28 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR )
+ strcat(str, "d8/9 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR )
+ strcat(str, "d10/11 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR )
+ strcat(str, "d12/13 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR )
+ strcat(str, "d14/15 ");
+ break;
+ case UNWIND_ARM64_MODE_FRAME_OLD:
+ strcpy(str, "old frame: ");
+ if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD )
+ strcat(str, "x21/22 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD )
+ strcat(str, "x23/24 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD )
+ strcat(str, "x25/26 ");
+ if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD )
+ strcat(str, "x27/28 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD )
+ strcat(str, "d8/9 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD )
+ strcat(str, "d10/11 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD )
+ strcat(str, "d12/13 ");
+ if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD )
+ strcat(str, "d14/15 ");
+ break;
+ }
+}
+#endif
+
+template <>
+const char* UnwindPrinter<x86_64>::personalityName(const macho_relocation_info<x86_64::P>* reloc)
+{
+ //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
+ //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
+ const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
+ return &fStrings[sym.n_strx()];
+}
+
+template <>
+const char* UnwindPrinter<x86>::personalityName(const macho_relocation_info<x86::P>* reloc)
+{
+ //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
+ //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
+ const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
+ return &fStrings[sym.n_strx()];
+}
+
+#if SUPPORT_ARCH_arm64
+template <>
+const char* UnwindPrinter<arm64>::personalityName(const macho_relocation_info<arm64::P>* reloc)
+{
+ //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
+ //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
+ const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
+ return &fStrings[sym.n_strx()];
+}
+#endif
template <typename A>
-void UnwindPrinter<A>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
+bool UnwindPrinter<A>::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr)
{
-
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((uint8_t*)fHeader + fUnwindSection->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[fUnwindSection->nreloc()];
+ for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+ if ( reloc->r_extern() && (reloc->r_address() == sectionOffset) ) {
+ *personalityStr = this->personalityName(reloc);
+ if ( addr != NULL )
+ *addr = fSymbols[reloc->r_symbolnum()].n_value();
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename A>
+void UnwindPrinter<A>::printObjectUnwindSection(bool showFunctionNames)
+{
+ printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
+ archName(), fUnwindSection->size(), fUnwindSection->size() / sizeof(macho_compact_unwind_entry<P>));
+
+ const macho_compact_unwind_entry<P>* const entriesStart = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset());
+ const macho_compact_unwind_entry<P>* const entriesEnd = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset() + fUnwindSection->size());
+ for (const macho_compact_unwind_entry<P>* entry=entriesStart; entry < entriesEnd; ++entry) {
+ uint64_t entryAddress = ((char*)entry - (char*)entriesStart) + fUnwindSection->addr();
+ printf("0x%08llX:\n", entryAddress);
+ const char* functionNameStr;
+ pint_t funcAddress;
+ uint32_t offsetInFunction;
+ if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::codeStartFieldOffset(), &functionNameStr, &funcAddress) ) {
+ offsetInFunction = entry->codeStart();
+ }
+ else {
+ functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction);
+ }
+ if ( offsetInFunction == 0 )
+ printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr);
+ else
+ printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress+offsetInFunction, functionNameStr, offsetInFunction);
+
+ printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress+offsetInFunction+entry->codeLen()), entry->codeLen());
+
+ char encodingString[200];
+ this->decode(entry->compactUnwindInfo(), ((const uint8_t*)fHeader), encodingString);
+ printf(" unwind info: 0x%08X %s\n", entry->compactUnwindInfo(), encodingString);
+
+ const char* personalityNameStr;
+ if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::personalityFieldOffset(), &personalityNameStr) ) {
+ printf(" personality: %s\n", personalityNameStr);
+ }
+ else {
+ printf(" personality:\n");
+ }
+ if ( entry->lsda() == 0 ) {
+ printf(" lsda:\n");
+ }
+ else {
+ uint32_t lsdaOffset;
+ const char* lsdaName = this->functionName(entry->lsda(), &lsdaOffset);
+ if ( lsdaOffset == 0 )
+ printf(" lsda: 0x%08llX %s\n", (uint64_t)entry->lsda(), lsdaName);
+ else
+ printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset);
+ }
+ }
}
+
+
template <typename A>
void UnwindPrinter<A>::printUnwindSection(bool showFunctionNames)
{
char encodingString[100];
decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString);
const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : "";
- printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n",
+ printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
j, funcOffset, entry[j].encoding(), encodingString, name);
}
}
fprintf(stderr, "MISSING LSDA entry for %s\n", name);
}
}
- printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n",
+ printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
j, funcOff, encodingIndex, encoding, encodingString, name);
}
}
unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
if ( onlyArchs.count(cputype) ) {
switch(cputype) {
- case CPU_TYPE_POWERPC:
- if ( UnwindPrinter<ppc>::validFile(p + offset) )
- UnwindPrinter<ppc>::make(p + offset, size, path, showFunctionNames);
- else
- throw "in universal file, ppc slice does not contain ppc mach-o";
- break;
case CPU_TYPE_I386:
if ( UnwindPrinter<x86>::validFile(p + offset) )
UnwindPrinter<x86>::make(p + offset, size, path, showFunctionNames);
else
throw "in universal file, i386 slice does not contain i386 mach-o";
break;
- case CPU_TYPE_POWERPC64:
- if ( UnwindPrinter<ppc64>::validFile(p + offset) )
- UnwindPrinter<ppc64>::make(p + offset, size, path, showFunctionNames);
- else
- throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
- break;
case CPU_TYPE_X86_64:
if ( UnwindPrinter<x86_64>::validFile(p + offset) )
UnwindPrinter<x86_64>::make(p + offset, size, path, showFunctionNames);
else
throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
break;
- case CPU_TYPE_ARM:
- if ( UnwindPrinter<arm>::validFile(p + offset) )
- UnwindPrinter<arm>::make(p + offset, size, path, showFunctionNames);
+#if SUPPORT_ARCH_arm64
+ case CPU_TYPE_ARM64:
+ if ( UnwindPrinter<arm64>::validFile(p + offset) )
+ UnwindPrinter<arm64>::make(p + offset, size, path, showFunctionNames);
else
- throw "in universal file, arm slice does not contain arm mach-o";
+ throw "in universal file, arm64 slice does not contain arm mach-o";
break;
+#endif
default:
throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
}
else if ( UnwindPrinter<x86>::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) {
UnwindPrinter<x86>::make(p, length, path, showFunctionNames);
}
- else if ( UnwindPrinter<ppc>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC) ) {
- UnwindPrinter<ppc>::make(p, length, path, showFunctionNames);
- }
- else if ( UnwindPrinter<ppc64>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC64) ) {
- UnwindPrinter<ppc64>::make(p, length, path, showFunctionNames);
- }
else if ( UnwindPrinter<x86_64>::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) {
UnwindPrinter<x86_64>::make(p, length, path, showFunctionNames);
}
- else if ( UnwindPrinter<arm>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) {
- UnwindPrinter<arm>::make(p, length, path, showFunctionNames);
+#if SUPPORT_ARCH_arm64
+ else if ( UnwindPrinter<arm64>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) {
+ UnwindPrinter<arm64>::make(p, length, path, showFunctionNames);
}
+#endif
else {
throw "not a known file type";
}
if ( arg[0] == '-' ) {
if ( strcmp(arg, "-arch") == 0 ) {
const char* arch = argv[++i];
- if ( strcmp(arch, "ppc") == 0 )
- onlyArchs.insert(CPU_TYPE_POWERPC);
- else if ( strcmp(arch, "ppc64") == 0 )
- onlyArchs.insert(CPU_TYPE_POWERPC64);
- else if ( strcmp(arch, "i386") == 0 )
+ if ( strcmp(arch, "i386") == 0 )
onlyArchs.insert(CPU_TYPE_I386);
else if ( strcmp(arch, "x86_64") == 0 )
onlyArchs.insert(CPU_TYPE_X86_64);
- else if ( strcmp(arch, "arm") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
+#if SUPPORT_ARCH_arm64
+ else if ( strcmp(arch, "arm64") == 0 )
+ onlyArchs.insert(CPU_TYPE_ARM64);
+#endif
else
throwf("unknown architecture %s", arch);
}
// use all architectures if no restrictions specified
if ( onlyArchs.size() == 0 ) {
- onlyArchs.insert(CPU_TYPE_POWERPC);
- onlyArchs.insert(CPU_TYPE_POWERPC64);
onlyArchs.insert(CPU_TYPE_I386);
onlyArchs.insert(CPU_TYPE_X86_64);
- onlyArchs.insert(CPU_TYPE_ARM);
+#if SUPPORT_ARCH_arm64
+ onlyArchs.insert(CPU_TYPE_ARM64);
+#endif
}
// process each file