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 <unordered_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
;
74 UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
,
75 const char* path
, bool showFunctionNames
);
76 bool findUnwindSection();
77 void printUnwindSection(bool showFunctionNames
);
78 void printObjectUnwindSection(bool showFunctionNames
);
79 void getSymbolTableInfo();
80 const char* functionName(pint_t addr
, uint32_t* offset
=NULL
);
81 const char* personalityName(const macho_relocation_info
<typename
A::P
>* reloc
);
82 bool hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
=NULL
);
84 static const char* archName();
85 static void decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
);
88 const macho_header
<P
>* fHeader
;
90 const macho_section
<P
>* fUnwindSection
;
92 const char* fStringsEnd
;
93 const macho_nlist
<P
>* fSymbols
;
94 uint32_t fSymbolCount
;
95 pint_t fMachHeaderAddress
;
99 template <> const char* UnwindPrinter
<x86
>::archName() { return "i386"; }
100 template <> const char* UnwindPrinter
<x86_64
>::archName() { return "x86_64"; }
101 template <> const char* UnwindPrinter
<arm
>::archName() { return "arm"; }
105 bool UnwindPrinter
<x86
>::validFile(const uint8_t* fileContent
)
107 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
108 if ( header
->magic() != MH_MAGIC
)
110 if ( header
->cputype() != CPU_TYPE_I386
)
112 switch (header
->filetype()) {
124 bool UnwindPrinter
<x86_64
>::validFile(const uint8_t* fileContent
)
126 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
127 if ( header
->magic() != MH_MAGIC_64
)
129 if ( header
->cputype() != CPU_TYPE_X86_64
)
131 switch (header
->filetype()) {
143 template <typename A
>
144 UnwindPrinter
<A
>::UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, bool showFunctionNames
)
145 : fHeader(NULL
), fLength(fileLength
), fUnwindSection(NULL
),
146 fStrings(NULL
), fStringsEnd(NULL
), fSymbols(NULL
), fSymbolCount(0), fMachHeaderAddress(0)
149 if ( ! validFile(fileContent
) )
150 throw "not a mach-o file that can be checked";
152 fPath
= strdup(path
);
153 fHeader
= (const macho_header
<P
>*)fileContent
;
155 getSymbolTableInfo();
157 if ( findUnwindSection() ) {
158 if ( fHeader
->filetype() == MH_OBJECT
)
159 printObjectUnwindSection(showFunctionNames
);
161 printUnwindSection(showFunctionNames
);
166 template <typename A
>
167 void UnwindPrinter
<A
>::getSymbolTableInfo()
169 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
170 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
171 const uint32_t cmd_count
= fHeader
->ncmds();
172 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
173 const macho_load_command
<P
>* cmd
= cmds
;
174 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
175 uint32_t size
= cmd
->cmdsize();
176 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
177 if ( endOfCmd
> endOfLoadCommands
)
178 throwf("load command #%d extends beyond the end of the load commands", i
);
179 if ( endOfCmd
> endOfFile
)
180 throwf("load command #%d extends beyond the end of the file", i
);
181 if ( cmd
->cmd() == LC_SYMTAB
) {
182 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
183 fSymbolCount
= symtab
->nsyms();
184 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
185 fStrings
= (char*)fHeader
+ symtab
->stroff();
186 fStringsEnd
= fStrings
+ symtab
->strsize();
188 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
192 template <typename A
>
193 const char* UnwindPrinter
<A
>::functionName(pint_t addr
, uint32_t* offset
)
195 const macho_nlist
<P
>* closestSymbol
= NULL
;
196 if ( offset
!= NULL
)
198 for (uint32_t i
=0; i
< fSymbolCount
; ++i
) {
199 uint8_t type
= fSymbols
[i
].n_type();
200 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
201 if ( fSymbols
[i
].n_value() == addr
) {
202 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
203 //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);
206 else if ( offset
!= NULL
) {
207 if ( closestSymbol
== NULL
) {
208 if ( fSymbols
[i
].n_value() < addr
)
209 closestSymbol
= &fSymbols
[i
];
212 if ( (fSymbols
[i
].n_value() < addr
) && (fSymbols
[i
].n_value() > closestSymbol
->n_value()) )
213 closestSymbol
= &fSymbols
[i
];
218 if ( closestSymbol
!= NULL
) {
219 *offset
= addr
- closestSymbol
->n_value();
220 return &fStrings
[closestSymbol
->n_strx()];
222 return "--anonymous function--";
227 template <typename A
>
228 bool UnwindPrinter
<A
>::findUnwindSection()
230 const char* unwindSectionName
= "__unwind_info";
231 const char* unwindSegmentName
= "__TEXT";
232 if ( fHeader
->filetype() == MH_OBJECT
) {
233 unwindSectionName
= "__compact_unwind";
234 unwindSegmentName
= "__LD";
236 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
237 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
238 const uint32_t cmd_count
= fHeader
->ncmds();
239 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
240 const macho_load_command
<P
>* cmd
= cmds
;
241 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
242 uint32_t size
= cmd
->cmdsize();
243 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
244 if ( endOfCmd
> endOfLoadCommands
)
245 throwf("load command #%d extends beyond the end of the load commands", i
);
246 if ( endOfCmd
> endOfFile
)
247 throwf("load command #%d extends beyond the end of the file", i
);
248 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
249 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
250 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
251 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
252 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
253 if ( (strncmp(sect
->sectname(), unwindSectionName
, 16) == 0) && (strcmp(sect
->segname(), unwindSegmentName
) == 0) ) {
254 fUnwindSection
= sect
;
255 fMachHeaderAddress
= segCmd
->vmaddr();
256 return fUnwindSection
;
260 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
265 #define EXTRACT_BITS(value, mask) \
266 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
270 void UnwindPrinter
<x86_64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
273 switch ( encoding
& UNWIND_X86_64_MODE_MASK
) {
274 case UNWIND_X86_64_MODE_RBP_FRAME
:
276 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
277 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
278 if ( savedRegistersLocations
== 0 ) {
279 strcpy(str
, "rbp frame, no saved registers");
282 sprintf(str
, "rbp frame, at -%d:", savedRegistersOffset
*8);
283 bool needComma
= false;
284 for (int i
=0; i
< 5; ++i
) {
289 switch (savedRegistersLocations
& 0x7) {
290 case UNWIND_X86_64_REG_NONE
:
293 case UNWIND_X86_64_REG_RBX
:
296 case UNWIND_X86_64_REG_R12
:
299 case UNWIND_X86_64_REG_R13
:
302 case UNWIND_X86_64_REG_R14
:
305 case UNWIND_X86_64_REG_R15
:
311 savedRegistersLocations
= (savedRegistersLocations
>> 3);
312 if ( savedRegistersLocations
== 0 )
318 case UNWIND_X86_64_MODE_STACK_IMMD
:
319 case UNWIND_X86_64_MODE_STACK_IND
:
321 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
322 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
323 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
324 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
325 if ( (encoding
& UNWIND_X86_64_MODE_MASK
) == UNWIND_X86_64_MODE_STACK_IND
) {
326 // stack size is encoded in subl $xxx,%esp instruction
327 uint32_t subl
= x86_64::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
328 sprintf(str
, "stack size=0x%08X, ", subl
+ 8*stackAdjust
);
331 sprintf(str
, "stack size=%d, ", stackSize
*8);
333 if ( regCount
== 0 ) {
334 strcat(str
, "no registers saved");
338 switch ( regCount
) {
340 permunreg
[0] = permutation
/120;
341 permutation
-= (permunreg
[0]*120);
342 permunreg
[1] = permutation
/24;
343 permutation
-= (permunreg
[1]*24);
344 permunreg
[2] = permutation
/6;
345 permutation
-= (permunreg
[2]*6);
346 permunreg
[3] = permutation
/2;
347 permutation
-= (permunreg
[3]*2);
348 permunreg
[4] = permutation
;
352 permunreg
[0] = permutation
/120;
353 permutation
-= (permunreg
[0]*120);
354 permunreg
[1] = permutation
/24;
355 permutation
-= (permunreg
[1]*24);
356 permunreg
[2] = permutation
/6;
357 permutation
-= (permunreg
[2]*6);
358 permunreg
[3] = permutation
/2;
359 permutation
-= (permunreg
[3]*2);
360 permunreg
[4] = permutation
;
363 permunreg
[0] = permutation
/60;
364 permutation
-= (permunreg
[0]*60);
365 permunreg
[1] = permutation
/12;
366 permutation
-= (permunreg
[1]*12);
367 permunreg
[2] = permutation
/3;
368 permutation
-= (permunreg
[2]*3);
369 permunreg
[3] = permutation
;
372 permunreg
[0] = permutation
/20;
373 permutation
-= (permunreg
[0]*20);
374 permunreg
[1] = permutation
/4;
375 permutation
-= (permunreg
[1]*4);
376 permunreg
[2] = permutation
;
379 permunreg
[0] = permutation
/5;
380 permutation
-= (permunreg
[0]*5);
381 permunreg
[1] = permutation
;
384 permunreg
[0] = permutation
;
387 // renumber registers back to standard numbers
389 bool used
[7] = { false, false, false, false, false, false, false };
390 for (int i
=0; i
< regCount
; ++i
) {
392 for (int u
=1; u
< 7; ++u
) {
394 if ( renum
== permunreg
[i
] ) {
403 bool needComma
= false;
404 for (int i
=0; i
< regCount
; ++i
) {
409 switch ( registers
[i
] ) {
410 case UNWIND_X86_64_REG_RBX
:
413 case UNWIND_X86_64_REG_R12
:
416 case UNWIND_X86_64_REG_R13
:
419 case UNWIND_X86_64_REG_R14
:
422 case UNWIND_X86_64_REG_R15
:
425 case UNWIND_X86_64_REG_RBP
:
435 case UNWIND_X86_64_MODE_DWARF
:
436 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
440 strcat(str
, "no unwind information");
444 if ( encoding
& UNWIND_HAS_LSDA
) {
445 strcat(str
, " LSDA");
451 void UnwindPrinter
<x86
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
454 switch ( encoding
& UNWIND_X86_MODE_MASK
) {
455 case UNWIND_X86_MODE_EBP_FRAME
:
457 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
458 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
459 if ( savedRegistersLocations
== 0 ) {
460 strcpy(str
, "ebp frame, no saved registers");
463 sprintf(str
, "ebp frame, at -%d:", savedRegistersOffset
*4);
464 bool needComma
= false;
465 for (int i
=0; i
< 5; ++i
) {
470 switch (savedRegistersLocations
& 0x7) {
471 case UNWIND_X86_REG_NONE
:
474 case UNWIND_X86_REG_EBX
:
477 case UNWIND_X86_REG_ECX
:
480 case UNWIND_X86_REG_EDX
:
483 case UNWIND_X86_REG_EDI
:
486 case UNWIND_X86_REG_ESI
:
492 savedRegistersLocations
= (savedRegistersLocations
>> 3);
493 if ( savedRegistersLocations
== 0 )
499 case UNWIND_X86_MODE_STACK_IMMD
:
500 case UNWIND_X86_MODE_STACK_IND
:
502 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
503 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
504 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
505 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
506 if ( (encoding
& UNWIND_X86_MODE_MASK
) == UNWIND_X86_MODE_STACK_IND
) {
507 // stack size is encoded in subl $xxx,%esp instruction
508 uint32_t subl
= x86::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
509 sprintf(str
, "stack size=0x%08X, ", subl
+4*stackAdjust
);
512 sprintf(str
, "stack size=%d, ", stackSize
*4);
514 if ( regCount
== 0 ) {
515 strcat(str
, "no saved regs");
519 switch ( regCount
) {
521 permunreg
[0] = permutation
/120;
522 permutation
-= (permunreg
[0]*120);
523 permunreg
[1] = permutation
/24;
524 permutation
-= (permunreg
[1]*24);
525 permunreg
[2] = permutation
/6;
526 permutation
-= (permunreg
[2]*6);
527 permunreg
[3] = permutation
/2;
528 permutation
-= (permunreg
[3]*2);
529 permunreg
[4] = permutation
;
533 permunreg
[0] = permutation
/120;
534 permutation
-= (permunreg
[0]*120);
535 permunreg
[1] = permutation
/24;
536 permutation
-= (permunreg
[1]*24);
537 permunreg
[2] = permutation
/6;
538 permutation
-= (permunreg
[2]*6);
539 permunreg
[3] = permutation
/2;
540 permutation
-= (permunreg
[3]*2);
541 permunreg
[4] = permutation
;
544 permunreg
[0] = permutation
/60;
545 permutation
-= (permunreg
[0]*60);
546 permunreg
[1] = permutation
/12;
547 permutation
-= (permunreg
[1]*12);
548 permunreg
[2] = permutation
/3;
549 permutation
-= (permunreg
[2]*3);
550 permunreg
[3] = permutation
;
553 permunreg
[0] = permutation
/20;
554 permutation
-= (permunreg
[0]*20);
555 permunreg
[1] = permutation
/4;
556 permutation
-= (permunreg
[1]*4);
557 permunreg
[2] = permutation
;
560 permunreg
[0] = permutation
/5;
561 permutation
-= (permunreg
[0]*5);
562 permunreg
[1] = permutation
;
565 permunreg
[0] = permutation
;
568 // renumber registers back to standard numbers
570 bool used
[7] = { false, false, false, false, false, false, false };
571 for (int i
=0; i
< regCount
; ++i
) {
573 for (int u
=1; u
< 7; ++u
) {
575 if ( renum
== permunreg
[i
] ) {
584 bool needComma
= false;
585 for (int i
=0; i
< regCount
; ++i
) {
590 switch ( registers
[i
] ) {
591 case UNWIND_X86_REG_EBX
:
594 case UNWIND_X86_REG_ECX
:
597 case UNWIND_X86_REG_EDX
:
600 case UNWIND_X86_REG_EDI
:
603 case UNWIND_X86_REG_ESI
:
606 case UNWIND_X86_REG_EBP
:
616 case UNWIND_X86_MODE_DWARF
:
617 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
);
621 strcat(str
, "no unwind information");
625 if ( encoding
& UNWIND_HAS_LSDA
) {
626 strcat(str
, " LSDA");
634 const char* UnwindPrinter
<x86_64
>::personalityName(const macho_relocation_info
<x86_64::P
>* reloc
)
636 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
637 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
638 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
639 return &fStrings
[sym
.n_strx()];
643 const char* UnwindPrinter
<x86
>::personalityName(const macho_relocation_info
<x86::P
>* reloc
)
645 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
646 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
647 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
648 return &fStrings
[sym
.n_strx()];
651 template <typename A
>
652 bool UnwindPrinter
<A
>::hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
)
654 const macho_relocation_info
<P
>* relocs
= (macho_relocation_info
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->reloff());
655 const macho_relocation_info
<P
>* relocsEnd
= &relocs
[fUnwindSection
->nreloc()];
656 for (const macho_relocation_info
<P
>* reloc
= relocs
; reloc
< relocsEnd
; ++reloc
) {
657 if ( reloc
->r_extern() && (reloc
->r_address() == sectionOffset
) ) {
658 *personalityStr
= this->personalityName(reloc
);
660 *addr
= fSymbols
[reloc
->r_symbolnum()].n_value();
668 template <typename A
>
669 void UnwindPrinter
<A
>::printObjectUnwindSection(bool showFunctionNames
)
671 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
672 archName(), fUnwindSection
->size(), fUnwindSection
->size() / sizeof(macho_compact_unwind_entry
<P
>));
674 const macho_compact_unwind_entry
<P
>* const entriesStart
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset());
675 const macho_compact_unwind_entry
<P
>* const entriesEnd
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset() + fUnwindSection
->size());
676 for (const macho_compact_unwind_entry
<P
>* entry
=entriesStart
; entry
< entriesEnd
; ++entry
) {
677 uint64_t entryAddress
= ((char*)entry
- (char*)entriesStart
) + fUnwindSection
->addr();
678 printf("0x%08llX:\n", entryAddress
);
679 const char* functionNameStr
;
681 uint32_t offsetInFunction
;
682 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::codeStartFieldOffset(), &functionNameStr
, &funcAddress
) ) {
683 offsetInFunction
= entry
->codeStart();
686 functionNameStr
= this->functionName(entry
->codeStart(), &offsetInFunction
);
688 if ( offsetInFunction
== 0 )
689 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress
, functionNameStr
);
691 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress
+offsetInFunction
, functionNameStr
, offsetInFunction
);
693 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress
+offsetInFunction
+entry
->codeLen()), entry
->codeLen());
695 char encodingString
[200];
696 this->decode(entry
->compactUnwindInfo(), ((const uint8_t*)fHeader
), encodingString
);
697 printf(" unwind info: 0x%08X %s\n", entry
->compactUnwindInfo(), encodingString
);
699 const char* personalityNameStr
;
700 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::personalityFieldOffset(), &personalityNameStr
) ) {
701 printf(" personality: %s\n", personalityNameStr
);
704 printf(" personality:\n");
706 if ( entry
->lsda() == 0 ) {
711 const char* lsdaName
= this->functionName(entry
->lsda(), &lsdaOffset
);
712 if ( lsdaOffset
== 0 )
713 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry
->lsda(), lsdaName
);
715 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry
->lsda(), lsdaName
, lsdaOffset
);
723 template <typename A
>
724 void UnwindPrinter
<A
>::printUnwindSection(bool showFunctionNames
)
726 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
727 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
729 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
730 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
731 printf("\tversion=0x%08X\n", sectionHeader
->version());
732 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
733 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
734 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
735 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
736 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
737 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
738 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
739 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
740 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
741 printf("\t\tencoding[%3u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
743 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
744 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
745 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
746 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
748 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
749 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
750 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
751 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
752 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
754 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
755 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
756 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
757 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
758 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
759 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
760 const char* name
= showFunctionNames
? functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
) : "";
761 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), name
);
762 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
763 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
765 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
766 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
767 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
768 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
769 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
770 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
771 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
772 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
773 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
774 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
775 uint32_t funcOffset
= entry
[j
].functionOffset();
776 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
777 // verify there is a corresponding entry in lsda table
779 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
780 if ( lindex
[k
].functionOffset() == funcOffset
) {
786 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
789 char encodingString
[100];
790 decode(entry
[j
].encoding(), ((const uint8_t*)fHeader
)+funcOffset
, encodingString
);
791 const char* name
= showFunctionNames
? functionName(funcOffset
+fMachHeaderAddress
) : "";
792 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n",
793 j
, funcOffset
, entry
[j
].encoding(), encodingString
, name
);
796 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
797 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
798 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
799 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
800 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
801 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
802 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
803 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
804 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
805 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
806 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
807 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
809 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
810 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
812 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
813 char encodingString
[100];
814 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
815 decode(encoding
, ((const uint8_t*)fHeader
)+funcOff
, encodingString
);
816 const char* name
= showFunctionNames
? functionName(funcOff
+fMachHeaderAddress
) : "";
817 if ( encoding
& UNWIND_HAS_LSDA
) {
818 // verify there is a corresponding entry in lsda table
820 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
821 if ( lindex
[k
].functionOffset() == funcOff
) {
827 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
830 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n",
831 j
, funcOff
, encodingIndex
, encoding
, encodingString
, name
);
835 fprintf(stderr
, "\t\tbad page header\n");
841 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
, bool showFunctionNames
)
843 struct stat stat_buf
;
846 int fd
= ::open(path
, O_RDONLY
, 0);
848 throw "cannot open file";
849 if ( ::fstat(fd
, &stat_buf
) != 0 )
850 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
851 uint32_t length
= stat_buf
.st_size
;
852 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
853 if ( p
== ((uint8_t*)(-1)) )
854 throw "cannot map file";
856 const mach_header
* mh
= (mach_header
*)p
;
857 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
858 const struct fat_header
* fh
= (struct fat_header
*)p
;
859 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
860 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
861 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
862 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
863 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
864 if ( onlyArchs
.count(cputype
) ) {
867 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
868 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
, showFunctionNames
);
870 throw "in universal file, i386 slice does not contain i386 mach-o";
872 case CPU_TYPE_X86_64
:
873 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
874 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
876 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
879 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
884 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
885 UnwindPrinter
<x86
>::make(p
, length
, path
, showFunctionNames
);
887 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
888 UnwindPrinter
<x86_64
>::make(p
, length
, path
, showFunctionNames
);
891 throw "not a known file type";
894 catch (const char* msg
) {
895 throwf("%s in %s", msg
, path
);
900 int main(int argc
, const char* argv
[])
902 std::set
<cpu_type_t
> onlyArchs
;
903 std::vector
<const char*> files
;
904 bool showFunctionNames
= true;
907 for(int i
=1; i
< argc
; ++i
) {
908 const char* arg
= argv
[i
];
909 if ( arg
[0] == '-' ) {
910 if ( strcmp(arg
, "-arch") == 0 ) {
911 const char* arch
= argv
[++i
];
912 if ( strcmp(arch
, "i386") == 0 )
913 onlyArchs
.insert(CPU_TYPE_I386
);
914 else if ( strcmp(arch
, "x86_64") == 0 )
915 onlyArchs
.insert(CPU_TYPE_X86_64
);
917 throwf("unknown architecture %s", arch
);
919 else if ( strcmp(arg
, "-no_symbols") == 0 ) {
920 showFunctionNames
= false;
923 throwf("unknown option: %s\n", arg
);
927 files
.push_back(arg
);
931 // use all architectures if no restrictions specified
932 if ( onlyArchs
.size() == 0 ) {
933 onlyArchs
.insert(CPU_TYPE_I386
);
934 onlyArchs
.insert(CPU_TYPE_X86_64
);
938 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
939 dump(*it
, onlyArchs
, showFunctionNames
);
943 catch (const char* msg
) {
944 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);