1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/types.h>
36 #include <ext/hash_set>
39 #include "MachOFileAbstraction.hpp"
40 #include "Architectures.hpp"
43 __attribute__((noreturn
))
44 void throwf(const char* format
, ...)
48 va_start(list
, format
);
49 vasprintf(&p
, format
, list
);
61 static bool validFile(const uint8_t* fileContent
);
62 static UnwindPrinter
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
,
63 const char* path
, bool showFunctionNames
)
64 { return new UnwindPrinter
<A
>(fileContent
, fileLength
,
65 path
, showFunctionNames
); }
66 virtual ~UnwindPrinter() {}
70 typedef typename
A::P P
;
71 typedef typename
A::P::E E
;
72 typedef typename
A::P::uint_t pint_t
;
77 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
80 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
82 UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
,
83 const char* path
, bool showFunctionNames
);
84 bool findUnwindSection();
85 void printUnwindSection(bool showFunctionNames
);
86 void printObjectUnwindSection(bool showFunctionNames
);
87 void getSymbolTableInfo();
88 const char* functionName(pint_t addr
, uint32_t* offset
=NULL
);
89 const char* personalityName(const macho_relocation_info
<typename
A::P
>* reloc
);
90 bool hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
=NULL
);
92 static const char* archName();
93 static void decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
);
96 const macho_header
<P
>* fHeader
;
98 const macho_section
<P
>* fUnwindSection
;
100 const char* fStringsEnd
;
101 const macho_nlist
<P
>* fSymbols
;
102 uint32_t fSymbolCount
;
103 pint_t fMachHeaderAddress
;
107 template <> const char* UnwindPrinter
<x86
>::archName() { return "i386"; }
108 template <> const char* UnwindPrinter
<x86_64
>::archName() { return "x86_64"; }
109 template <> const char* UnwindPrinter
<arm
>::archName() { return "arm"; }
113 bool UnwindPrinter
<x86
>::validFile(const uint8_t* fileContent
)
115 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
116 if ( header
->magic() != MH_MAGIC
)
118 if ( header
->cputype() != CPU_TYPE_I386
)
120 switch (header
->filetype()) {
132 bool UnwindPrinter
<x86_64
>::validFile(const uint8_t* fileContent
)
134 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
135 if ( header
->magic() != MH_MAGIC_64
)
137 if ( header
->cputype() != CPU_TYPE_X86_64
)
139 switch (header
->filetype()) {
151 template <typename A
>
152 UnwindPrinter
<A
>::UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, bool showFunctionNames
)
153 : fHeader(NULL
), fLength(fileLength
), fUnwindSection(NULL
),
154 fStrings(NULL
), fStringsEnd(NULL
), fSymbols(NULL
), fSymbolCount(0), fMachHeaderAddress(0)
157 if ( ! validFile(fileContent
) )
158 throw "not a mach-o file that can be checked";
160 fPath
= strdup(path
);
161 fHeader
= (const macho_header
<P
>*)fileContent
;
163 getSymbolTableInfo();
165 if ( findUnwindSection() ) {
166 if ( fHeader
->filetype() == MH_OBJECT
)
167 printObjectUnwindSection(showFunctionNames
);
169 printUnwindSection(showFunctionNames
);
174 template <typename A
>
175 void UnwindPrinter
<A
>::getSymbolTableInfo()
177 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
178 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
179 const uint32_t cmd_count
= fHeader
->ncmds();
180 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
181 const macho_load_command
<P
>* cmd
= cmds
;
182 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
183 uint32_t size
= cmd
->cmdsize();
184 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
185 if ( endOfCmd
> endOfLoadCommands
)
186 throwf("load command #%d extends beyond the end of the load commands", i
);
187 if ( endOfCmd
> endOfFile
)
188 throwf("load command #%d extends beyond the end of the file", i
);
189 if ( cmd
->cmd() == LC_SYMTAB
) {
190 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
191 fSymbolCount
= symtab
->nsyms();
192 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
193 fStrings
= (char*)fHeader
+ symtab
->stroff();
194 fStringsEnd
= fStrings
+ symtab
->strsize();
196 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
200 template <typename A
>
201 const char* UnwindPrinter
<A
>::functionName(pint_t addr
, uint32_t* offset
)
203 const macho_nlist
<P
>* closestSymbol
= NULL
;
204 if ( offset
!= NULL
)
206 for (uint32_t i
=0; i
< fSymbolCount
; ++i
) {
207 uint8_t type
= fSymbols
[i
].n_type();
208 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
209 if ( fSymbols
[i
].n_value() == addr
) {
210 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
211 //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);
214 else if ( offset
!= NULL
) {
215 if ( closestSymbol
== NULL
) {
216 if ( fSymbols
[i
].n_value() < addr
)
217 closestSymbol
= &fSymbols
[i
];
220 if ( (fSymbols
[i
].n_value() < addr
) && (fSymbols
[i
].n_value() > closestSymbol
->n_value()) )
221 closestSymbol
= &fSymbols
[i
];
226 if ( closestSymbol
!= NULL
) {
227 *offset
= addr
- closestSymbol
->n_value();
228 return &fStrings
[closestSymbol
->n_strx()];
230 return "--anonymous function--";
235 template <typename A
>
236 bool UnwindPrinter
<A
>::findUnwindSection()
238 const char* unwindSectionName
= "__unwind_info";
239 const char* unwindSegmentName
= "__TEXT";
240 if ( fHeader
->filetype() == MH_OBJECT
) {
241 unwindSectionName
= "__compact_unwind";
242 unwindSegmentName
= "__LD";
244 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
245 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
246 const uint32_t cmd_count
= fHeader
->ncmds();
247 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
248 const macho_load_command
<P
>* cmd
= cmds
;
249 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
250 uint32_t size
= cmd
->cmdsize();
251 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
252 if ( endOfCmd
> endOfLoadCommands
)
253 throwf("load command #%d extends beyond the end of the load commands", i
);
254 if ( endOfCmd
> endOfFile
)
255 throwf("load command #%d extends beyond the end of the file", i
);
256 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
257 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
258 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
259 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
260 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
261 if ( (strncmp(sect
->sectname(), unwindSectionName
, 16) == 0) && (strcmp(sect
->segname(), unwindSegmentName
) == 0) ) {
262 fUnwindSection
= sect
;
263 fMachHeaderAddress
= segCmd
->vmaddr();
264 return fUnwindSection
;
268 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
273 #define EXTRACT_BITS(value, mask) \
274 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
278 void UnwindPrinter
<x86_64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
281 switch ( encoding
& UNWIND_X86_64_MODE_MASK
) {
282 case UNWIND_X86_64_MODE_RBP_FRAME
:
284 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
285 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
286 if ( savedRegistersLocations
== 0 ) {
287 strcpy(str
, "rbp frame, no saved registers");
290 sprintf(str
, "rbp frame, at -%d:", savedRegistersOffset
*8);
291 bool needComma
= false;
292 for (int i
=0; i
< 5; ++i
) {
297 switch (savedRegistersLocations
& 0x7) {
298 case UNWIND_X86_64_REG_NONE
:
301 case UNWIND_X86_64_REG_RBX
:
304 case UNWIND_X86_64_REG_R12
:
307 case UNWIND_X86_64_REG_R13
:
310 case UNWIND_X86_64_REG_R14
:
313 case UNWIND_X86_64_REG_R15
:
319 savedRegistersLocations
= (savedRegistersLocations
>> 3);
320 if ( savedRegistersLocations
== 0 )
326 case UNWIND_X86_64_MODE_STACK_IMMD
:
327 case UNWIND_X86_64_MODE_STACK_IND
:
329 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
330 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
331 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
332 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
333 if ( (encoding
& UNWIND_X86_64_MODE_MASK
) == UNWIND_X86_64_MODE_STACK_IND
) {
334 // stack size is encoded in subl $xxx,%esp instruction
335 uint32_t subl
= x86_64::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
336 sprintf(str
, "stack size=0x%08X, ", subl
+ 8*stackAdjust
);
339 sprintf(str
, "stack size=%d, ", stackSize
*8);
341 if ( regCount
== 0 ) {
342 strcat(str
, "no registers saved");
346 switch ( regCount
) {
348 permunreg
[0] = permutation
/120;
349 permutation
-= (permunreg
[0]*120);
350 permunreg
[1] = permutation
/24;
351 permutation
-= (permunreg
[1]*24);
352 permunreg
[2] = permutation
/6;
353 permutation
-= (permunreg
[2]*6);
354 permunreg
[3] = permutation
/2;
355 permutation
-= (permunreg
[3]*2);
356 permunreg
[4] = permutation
;
360 permunreg
[0] = permutation
/120;
361 permutation
-= (permunreg
[0]*120);
362 permunreg
[1] = permutation
/24;
363 permutation
-= (permunreg
[1]*24);
364 permunreg
[2] = permutation
/6;
365 permutation
-= (permunreg
[2]*6);
366 permunreg
[3] = permutation
/2;
367 permutation
-= (permunreg
[3]*2);
368 permunreg
[4] = permutation
;
371 permunreg
[0] = permutation
/60;
372 permutation
-= (permunreg
[0]*60);
373 permunreg
[1] = permutation
/12;
374 permutation
-= (permunreg
[1]*12);
375 permunreg
[2] = permutation
/3;
376 permutation
-= (permunreg
[2]*3);
377 permunreg
[3] = permutation
;
380 permunreg
[0] = permutation
/20;
381 permutation
-= (permunreg
[0]*20);
382 permunreg
[1] = permutation
/4;
383 permutation
-= (permunreg
[1]*4);
384 permunreg
[2] = permutation
;
387 permunreg
[0] = permutation
/5;
388 permutation
-= (permunreg
[0]*5);
389 permunreg
[1] = permutation
;
392 permunreg
[0] = permutation
;
395 // renumber registers back to standard numbers
397 bool used
[7] = { false, false, false, false, false, false, false };
398 for (int i
=0; i
< regCount
; ++i
) {
400 for (int u
=1; u
< 7; ++u
) {
402 if ( renum
== permunreg
[i
] ) {
411 bool needComma
= false;
412 for (int i
=0; i
< regCount
; ++i
) {
417 switch ( registers
[i
] ) {
418 case UNWIND_X86_64_REG_RBX
:
421 case UNWIND_X86_64_REG_R12
:
424 case UNWIND_X86_64_REG_R13
:
427 case UNWIND_X86_64_REG_R14
:
430 case UNWIND_X86_64_REG_R15
:
433 case UNWIND_X86_64_REG_RBP
:
443 case UNWIND_X86_64_MODE_DWARF
:
444 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
448 strcat(str
, "no unwind information");
452 if ( encoding
& UNWIND_HAS_LSDA
) {
453 strcat(str
, " LSDA");
459 void UnwindPrinter
<x86
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
462 switch ( encoding
& UNWIND_X86_MODE_MASK
) {
463 case UNWIND_X86_MODE_EBP_FRAME
:
465 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
466 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
467 if ( savedRegistersLocations
== 0 ) {
468 strcpy(str
, "ebp frame, no saved registers");
471 sprintf(str
, "ebp frame, at -%d:", savedRegistersOffset
*4);
472 bool needComma
= false;
473 for (int i
=0; i
< 5; ++i
) {
478 switch (savedRegistersLocations
& 0x7) {
479 case UNWIND_X86_REG_NONE
:
482 case UNWIND_X86_REG_EBX
:
485 case UNWIND_X86_REG_ECX
:
488 case UNWIND_X86_REG_EDX
:
491 case UNWIND_X86_REG_EDI
:
494 case UNWIND_X86_REG_ESI
:
500 savedRegistersLocations
= (savedRegistersLocations
>> 3);
501 if ( savedRegistersLocations
== 0 )
507 case UNWIND_X86_MODE_STACK_IMMD
:
508 case UNWIND_X86_MODE_STACK_IND
:
510 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
511 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
512 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
513 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
514 if ( (encoding
& UNWIND_X86_MODE_MASK
) == UNWIND_X86_MODE_STACK_IND
) {
515 // stack size is encoded in subl $xxx,%esp instruction
516 uint32_t subl
= x86::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
517 sprintf(str
, "stack size=0x%08X, ", subl
+4*stackAdjust
);
520 sprintf(str
, "stack size=%d, ", stackSize
*4);
522 if ( regCount
== 0 ) {
523 strcat(str
, "no saved regs");
527 switch ( regCount
) {
529 permunreg
[0] = permutation
/120;
530 permutation
-= (permunreg
[0]*120);
531 permunreg
[1] = permutation
/24;
532 permutation
-= (permunreg
[1]*24);
533 permunreg
[2] = permutation
/6;
534 permutation
-= (permunreg
[2]*6);
535 permunreg
[3] = permutation
/2;
536 permutation
-= (permunreg
[3]*2);
537 permunreg
[4] = permutation
;
541 permunreg
[0] = permutation
/120;
542 permutation
-= (permunreg
[0]*120);
543 permunreg
[1] = permutation
/24;
544 permutation
-= (permunreg
[1]*24);
545 permunreg
[2] = permutation
/6;
546 permutation
-= (permunreg
[2]*6);
547 permunreg
[3] = permutation
/2;
548 permutation
-= (permunreg
[3]*2);
549 permunreg
[4] = permutation
;
552 permunreg
[0] = permutation
/60;
553 permutation
-= (permunreg
[0]*60);
554 permunreg
[1] = permutation
/12;
555 permutation
-= (permunreg
[1]*12);
556 permunreg
[2] = permutation
/3;
557 permutation
-= (permunreg
[2]*3);
558 permunreg
[3] = permutation
;
561 permunreg
[0] = permutation
/20;
562 permutation
-= (permunreg
[0]*20);
563 permunreg
[1] = permutation
/4;
564 permutation
-= (permunreg
[1]*4);
565 permunreg
[2] = permutation
;
568 permunreg
[0] = permutation
/5;
569 permutation
-= (permunreg
[0]*5);
570 permunreg
[1] = permutation
;
573 permunreg
[0] = permutation
;
576 // renumber registers back to standard numbers
578 bool used
[7] = { false, false, false, false, false, false, false };
579 for (int i
=0; i
< regCount
; ++i
) {
581 for (int u
=1; u
< 7; ++u
) {
583 if ( renum
== permunreg
[i
] ) {
592 bool needComma
= false;
593 for (int i
=0; i
< regCount
; ++i
) {
598 switch ( registers
[i
] ) {
599 case UNWIND_X86_REG_EBX
:
602 case UNWIND_X86_REG_ECX
:
605 case UNWIND_X86_REG_EDX
:
608 case UNWIND_X86_REG_EDI
:
611 case UNWIND_X86_REG_ESI
:
614 case UNWIND_X86_REG_EBP
:
624 case UNWIND_X86_MODE_DWARF
:
625 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
);
629 strcat(str
, "no unwind information");
633 if ( encoding
& UNWIND_HAS_LSDA
) {
634 strcat(str
, " LSDA");
642 const char* UnwindPrinter
<x86_64
>::personalityName(const macho_relocation_info
<x86_64::P
>* reloc
)
644 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
645 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
646 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
647 return &fStrings
[sym
.n_strx()];
651 const char* UnwindPrinter
<x86
>::personalityName(const macho_relocation_info
<x86::P
>* reloc
)
653 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
654 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
655 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
656 return &fStrings
[sym
.n_strx()];
659 template <typename A
>
660 bool UnwindPrinter
<A
>::hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
)
662 const macho_relocation_info
<P
>* relocs
= (macho_relocation_info
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->reloff());
663 const macho_relocation_info
<P
>* relocsEnd
= &relocs
[fUnwindSection
->nreloc()];
664 for (const macho_relocation_info
<P
>* reloc
= relocs
; reloc
< relocsEnd
; ++reloc
) {
665 if ( reloc
->r_extern() && (reloc
->r_address() == sectionOffset
) ) {
666 *personalityStr
= this->personalityName(reloc
);
668 *addr
= fSymbols
[reloc
->r_symbolnum()].n_value();
676 template <typename A
>
677 void UnwindPrinter
<A
>::printObjectUnwindSection(bool showFunctionNames
)
679 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
680 archName(), fUnwindSection
->size(), fUnwindSection
->size() / sizeof(macho_compact_unwind_entry
<P
>));
682 const macho_compact_unwind_entry
<P
>* const entriesStart
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset());
683 const macho_compact_unwind_entry
<P
>* const entriesEnd
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset() + fUnwindSection
->size());
684 for (const macho_compact_unwind_entry
<P
>* entry
=entriesStart
; entry
< entriesEnd
; ++entry
) {
685 uint64_t entryAddress
= ((char*)entry
- (char*)entriesStart
) + fUnwindSection
->addr();
686 printf("0x%08llX:\n", entryAddress
);
687 const char* functionNameStr
;
689 uint32_t offsetInFunction
;
690 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::codeStartFieldOffset(), &functionNameStr
, &funcAddress
) ) {
691 offsetInFunction
= entry
->codeStart();
694 functionNameStr
= this->functionName(entry
->codeStart(), &offsetInFunction
);
696 if ( offsetInFunction
== 0 )
697 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress
, functionNameStr
);
699 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress
+offsetInFunction
, functionNameStr
, offsetInFunction
);
701 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress
+offsetInFunction
+entry
->codeLen()), entry
->codeLen());
703 char encodingString
[200];
704 this->decode(entry
->compactUnwindInfo(), ((const uint8_t*)fHeader
), encodingString
);
705 printf(" unwind info: 0x%08X %s\n", entry
->compactUnwindInfo(), encodingString
);
707 const char* personalityNameStr
;
708 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::personalityFieldOffset(), &personalityNameStr
) ) {
709 printf(" personality: %s\n", personalityNameStr
);
712 printf(" personality:\n");
714 if ( entry
->lsda() == 0 ) {
719 const char* lsdaName
= this->functionName(entry
->lsda(), &lsdaOffset
);
720 if ( lsdaOffset
== 0 )
721 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry
->lsda(), lsdaName
);
723 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry
->lsda(), lsdaName
, lsdaOffset
);
731 template <typename A
>
732 void UnwindPrinter
<A
>::printUnwindSection(bool showFunctionNames
)
734 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
735 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
737 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
738 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
739 printf("\tversion=0x%08X\n", sectionHeader
->version());
740 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
741 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
742 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
743 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
744 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
745 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
746 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
747 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
748 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
749 printf("\t\tencoding[%3u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
751 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
752 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
753 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
754 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
756 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
757 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
758 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
759 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
760 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
762 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
763 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
764 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
765 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
766 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
767 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
768 const char* name
= showFunctionNames
? functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
) : "";
769 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), name
);
770 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
771 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
773 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
774 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
775 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
776 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
777 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
778 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
779 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
780 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
781 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
782 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
783 uint32_t funcOffset
= entry
[j
].functionOffset();
784 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
785 // verify there is a corresponding entry in lsda table
787 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
788 if ( lindex
[k
].functionOffset() == funcOffset
) {
794 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
797 char encodingString
[100];
798 decode(entry
[j
].encoding(), ((const uint8_t*)fHeader
)+funcOffset
, encodingString
);
799 const char* name
= showFunctionNames
? functionName(funcOffset
+fMachHeaderAddress
) : "";
800 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n",
801 j
, funcOffset
, entry
[j
].encoding(), encodingString
, name
);
804 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
805 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
806 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
807 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
808 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
809 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
810 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
811 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
812 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
813 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
814 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
815 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
817 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
818 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
820 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
821 char encodingString
[100];
822 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
823 decode(encoding
, ((const uint8_t*)fHeader
)+funcOff
, encodingString
);
824 const char* name
= showFunctionNames
? functionName(funcOff
+fMachHeaderAddress
) : "";
825 if ( encoding
& UNWIND_HAS_LSDA
) {
826 // verify there is a corresponding entry in lsda table
828 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
829 if ( lindex
[k
].functionOffset() == funcOff
) {
835 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
838 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n",
839 j
, funcOff
, encodingIndex
, encoding
, encodingString
, name
);
843 fprintf(stderr
, "\t\tbad page header\n");
849 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
, bool showFunctionNames
)
851 struct stat stat_buf
;
854 int fd
= ::open(path
, O_RDONLY
, 0);
856 throw "cannot open file";
857 if ( ::fstat(fd
, &stat_buf
) != 0 )
858 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
859 uint32_t length
= stat_buf
.st_size
;
860 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
861 if ( p
== ((uint8_t*)(-1)) )
862 throw "cannot map file";
864 const mach_header
* mh
= (mach_header
*)p
;
865 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
866 const struct fat_header
* fh
= (struct fat_header
*)p
;
867 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
868 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
869 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
870 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
871 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
872 if ( onlyArchs
.count(cputype
) ) {
875 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
876 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
, showFunctionNames
);
878 throw "in universal file, i386 slice does not contain i386 mach-o";
880 case CPU_TYPE_X86_64
:
881 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
882 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
884 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
887 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
892 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
893 UnwindPrinter
<x86
>::make(p
, length
, path
, showFunctionNames
);
895 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
896 UnwindPrinter
<x86_64
>::make(p
, length
, path
, showFunctionNames
);
899 throw "not a known file type";
902 catch (const char* msg
) {
903 throwf("%s in %s", msg
, path
);
908 int main(int argc
, const char* argv
[])
910 std::set
<cpu_type_t
> onlyArchs
;
911 std::vector
<const char*> files
;
912 bool showFunctionNames
= true;
915 for(int i
=1; i
< argc
; ++i
) {
916 const char* arg
= argv
[i
];
917 if ( arg
[0] == '-' ) {
918 if ( strcmp(arg
, "-arch") == 0 ) {
919 const char* arch
= argv
[++i
];
920 if ( strcmp(arch
, "i386") == 0 )
921 onlyArchs
.insert(CPU_TYPE_I386
);
922 else if ( strcmp(arch
, "x86_64") == 0 )
923 onlyArchs
.insert(CPU_TYPE_X86_64
);
925 throwf("unknown architecture %s", arch
);
927 else if ( strcmp(arg
, "-no_symbols") == 0 ) {
928 showFunctionNames
= false;
931 throwf("unknown option: %s\n", arg
);
935 files
.push_back(arg
);
939 // use all architectures if no restrictions specified
940 if ( onlyArchs
.size() == 0 ) {
941 onlyArchs
.insert(CPU_TYPE_I386
);
942 onlyArchs
.insert(CPU_TYPE_X86_64
);
946 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
947 dump(*it
, onlyArchs
, showFunctionNames
);
951 catch (const char* msg
) {
952 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);