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>
38 #include "configure.h"
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"; }
102 #if SUPPORT_ARCH_arm64
103 template <> const char* UnwindPrinter
<arm64
>::archName() { return "arm64"; }
107 bool UnwindPrinter
<x86
>::validFile(const uint8_t* fileContent
)
109 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
110 if ( header
->magic() != MH_MAGIC
)
112 if ( header
->cputype() != CPU_TYPE_I386
)
114 switch (header
->filetype()) {
126 bool UnwindPrinter
<x86_64
>::validFile(const uint8_t* fileContent
)
128 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
129 if ( header
->magic() != MH_MAGIC_64
)
131 if ( header
->cputype() != CPU_TYPE_X86_64
)
133 switch (header
->filetype()) {
145 #if SUPPORT_ARCH_arm64
147 bool UnwindPrinter
<arm64
>::validFile(const uint8_t* fileContent
)
149 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
150 if ( header
->magic() != MH_MAGIC_64
)
152 if ( header
->cputype() != CPU_TYPE_ARM64
)
154 switch (header
->filetype()) {
166 template <typename A
>
167 UnwindPrinter
<A
>::UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, bool showFunctionNames
)
168 : fHeader(NULL
), fLength(fileLength
), fUnwindSection(NULL
),
169 fStrings(NULL
), fStringsEnd(NULL
), fSymbols(NULL
), fSymbolCount(0), fMachHeaderAddress(0)
172 if ( ! validFile(fileContent
) )
173 throw "not a mach-o file that can be checked";
175 fPath
= strdup(path
);
176 fHeader
= (const macho_header
<P
>*)fileContent
;
178 getSymbolTableInfo();
180 if ( findUnwindSection() ) {
181 if ( fHeader
->filetype() == MH_OBJECT
)
182 printObjectUnwindSection(showFunctionNames
);
184 printUnwindSection(showFunctionNames
);
189 template <typename A
>
190 void UnwindPrinter
<A
>::getSymbolTableInfo()
192 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
193 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
194 const uint32_t cmd_count
= fHeader
->ncmds();
195 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
196 const macho_load_command
<P
>* cmd
= cmds
;
197 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
198 uint32_t size
= cmd
->cmdsize();
199 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
200 if ( endOfCmd
> endOfLoadCommands
)
201 throwf("load command #%d extends beyond the end of the load commands", i
);
202 if ( endOfCmd
> endOfFile
)
203 throwf("load command #%d extends beyond the end of the file", i
);
204 if ( cmd
->cmd() == LC_SYMTAB
) {
205 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
206 fSymbolCount
= symtab
->nsyms();
207 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
208 fStrings
= (char*)fHeader
+ symtab
->stroff();
209 fStringsEnd
= fStrings
+ symtab
->strsize();
211 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
215 template <typename A
>
216 const char* UnwindPrinter
<A
>::functionName(pint_t addr
, uint32_t* offset
)
218 const macho_nlist
<P
>* closestSymbol
= NULL
;
219 if ( offset
!= NULL
)
221 for (uint32_t i
=0; i
< fSymbolCount
; ++i
) {
222 uint8_t type
= fSymbols
[i
].n_type();
223 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
224 uint32_t value
= fSymbols
[i
].n_value();
225 if ( value
== addr
) {
226 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
229 if ( fSymbols
[i
].n_desc() & N_ARM_THUMB_DEF
)
231 if ( value
== addr
) {
232 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
233 //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);
236 else if ( offset
!= NULL
) {
237 if ( closestSymbol
== NULL
) {
238 if ( fSymbols
[i
].n_value() < addr
)
239 closestSymbol
= &fSymbols
[i
];
242 if ( (fSymbols
[i
].n_value() < addr
) && (fSymbols
[i
].n_value() > closestSymbol
->n_value()) )
243 closestSymbol
= &fSymbols
[i
];
248 if ( closestSymbol
!= NULL
) {
249 *offset
= addr
- closestSymbol
->n_value();
250 return &fStrings
[closestSymbol
->n_strx()];
252 return "--anonymous function--";
257 template <typename A
>
258 bool UnwindPrinter
<A
>::findUnwindSection()
260 const char* unwindSectionName
= "__unwind_info";
261 const char* unwindSegmentName
= "__TEXT";
262 if ( fHeader
->filetype() == MH_OBJECT
) {
263 unwindSectionName
= "__compact_unwind";
264 unwindSegmentName
= "__LD";
266 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
267 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
268 const uint32_t cmd_count
= fHeader
->ncmds();
269 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
270 const macho_load_command
<P
>* cmd
= cmds
;
271 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
272 uint32_t size
= cmd
->cmdsize();
273 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
274 if ( endOfCmd
> endOfLoadCommands
)
275 throwf("load command #%d extends beyond the end of the load commands", i
);
276 if ( endOfCmd
> endOfFile
)
277 throwf("load command #%d extends beyond the end of the file", i
);
278 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
279 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
280 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
281 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
282 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
283 if ( (strncmp(sect
->sectname(), unwindSectionName
, 16) == 0) && (strcmp(sect
->segname(), unwindSegmentName
) == 0) ) {
284 fUnwindSection
= sect
;
285 fMachHeaderAddress
= segCmd
->vmaddr();
286 return fUnwindSection
;
290 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
295 #define EXTRACT_BITS(value, mask) \
296 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
300 void UnwindPrinter
<x86_64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
303 switch ( encoding
& UNWIND_X86_64_MODE_MASK
) {
304 case UNWIND_X86_64_MODE_RBP_FRAME
:
306 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
307 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
308 if ( savedRegistersLocations
== 0 ) {
309 strcpy(str
, "rbp frame, no saved registers");
312 sprintf(str
, "rbp frame, at -%d:", savedRegistersOffset
*8);
313 bool needComma
= false;
314 for (int i
=0; i
< 5; ++i
) {
319 switch (savedRegistersLocations
& 0x7) {
320 case UNWIND_X86_64_REG_NONE
:
323 case UNWIND_X86_64_REG_RBX
:
326 case UNWIND_X86_64_REG_R12
:
329 case UNWIND_X86_64_REG_R13
:
332 case UNWIND_X86_64_REG_R14
:
335 case UNWIND_X86_64_REG_R15
:
341 savedRegistersLocations
= (savedRegistersLocations
>> 3);
342 if ( savedRegistersLocations
== 0 )
348 case UNWIND_X86_64_MODE_STACK_IMMD
:
349 case UNWIND_X86_64_MODE_STACK_IND
:
351 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
352 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
353 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
354 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
355 if ( (encoding
& UNWIND_X86_64_MODE_MASK
) == UNWIND_X86_64_MODE_STACK_IND
) {
356 // stack size is encoded in subl $xxx,%esp instruction
357 uint32_t subl
= x86_64::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
358 sprintf(str
, "stack size=0x%08X, ", subl
+ 8*stackAdjust
);
361 sprintf(str
, "stack size=%d, ", stackSize
*8);
363 if ( regCount
== 0 ) {
364 strcat(str
, "no registers saved");
368 switch ( regCount
) {
370 permunreg
[0] = permutation
/120;
371 permutation
-= (permunreg
[0]*120);
372 permunreg
[1] = permutation
/24;
373 permutation
-= (permunreg
[1]*24);
374 permunreg
[2] = permutation
/6;
375 permutation
-= (permunreg
[2]*6);
376 permunreg
[3] = permutation
/2;
377 permutation
-= (permunreg
[3]*2);
378 permunreg
[4] = permutation
;
382 permunreg
[0] = permutation
/120;
383 permutation
-= (permunreg
[0]*120);
384 permunreg
[1] = permutation
/24;
385 permutation
-= (permunreg
[1]*24);
386 permunreg
[2] = permutation
/6;
387 permutation
-= (permunreg
[2]*6);
388 permunreg
[3] = permutation
/2;
389 permutation
-= (permunreg
[3]*2);
390 permunreg
[4] = permutation
;
393 permunreg
[0] = permutation
/60;
394 permutation
-= (permunreg
[0]*60);
395 permunreg
[1] = permutation
/12;
396 permutation
-= (permunreg
[1]*12);
397 permunreg
[2] = permutation
/3;
398 permutation
-= (permunreg
[2]*3);
399 permunreg
[3] = permutation
;
402 permunreg
[0] = permutation
/20;
403 permutation
-= (permunreg
[0]*20);
404 permunreg
[1] = permutation
/4;
405 permutation
-= (permunreg
[1]*4);
406 permunreg
[2] = permutation
;
409 permunreg
[0] = permutation
/5;
410 permutation
-= (permunreg
[0]*5);
411 permunreg
[1] = permutation
;
414 permunreg
[0] = permutation
;
417 // renumber registers back to standard numbers
419 bool used
[7] = { false, false, false, false, false, false, false };
420 for (int i
=0; i
< regCount
; ++i
) {
422 for (int u
=1; u
< 7; ++u
) {
424 if ( renum
== permunreg
[i
] ) {
433 bool needComma
= false;
434 for (int i
=0; i
< regCount
; ++i
) {
439 switch ( registers
[i
] ) {
440 case UNWIND_X86_64_REG_RBX
:
443 case UNWIND_X86_64_REG_R12
:
446 case UNWIND_X86_64_REG_R13
:
449 case UNWIND_X86_64_REG_R14
:
452 case UNWIND_X86_64_REG_R15
:
455 case UNWIND_X86_64_REG_RBP
:
465 case UNWIND_X86_64_MODE_DWARF
:
466 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
470 strcat(str
, "no unwind information");
474 if ( encoding
& UNWIND_HAS_LSDA
) {
475 strcat(str
, " LSDA");
481 void UnwindPrinter
<x86
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
484 switch ( encoding
& UNWIND_X86_MODE_MASK
) {
485 case UNWIND_X86_MODE_EBP_FRAME
:
487 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
488 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
489 if ( savedRegistersLocations
== 0 ) {
490 strcpy(str
, "ebp frame, no saved registers");
493 sprintf(str
, "ebp frame, at -%d:", savedRegistersOffset
*4);
494 bool needComma
= false;
495 for (int i
=0; i
< 5; ++i
) {
500 switch (savedRegistersLocations
& 0x7) {
501 case UNWIND_X86_REG_NONE
:
504 case UNWIND_X86_REG_EBX
:
507 case UNWIND_X86_REG_ECX
:
510 case UNWIND_X86_REG_EDX
:
513 case UNWIND_X86_REG_EDI
:
516 case UNWIND_X86_REG_ESI
:
522 savedRegistersLocations
= (savedRegistersLocations
>> 3);
523 if ( savedRegistersLocations
== 0 )
529 case UNWIND_X86_MODE_STACK_IMMD
:
530 case UNWIND_X86_MODE_STACK_IND
:
532 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
533 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
534 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
535 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
536 if ( (encoding
& UNWIND_X86_MODE_MASK
) == UNWIND_X86_MODE_STACK_IND
) {
537 // stack size is encoded in subl $xxx,%esp instruction
538 uint32_t subl
= x86::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
539 sprintf(str
, "stack size=0x%08X, ", subl
+4*stackAdjust
);
542 sprintf(str
, "stack size=%d, ", stackSize
*4);
544 if ( regCount
== 0 ) {
545 strcat(str
, "no saved regs");
549 switch ( regCount
) {
551 permunreg
[0] = permutation
/120;
552 permutation
-= (permunreg
[0]*120);
553 permunreg
[1] = permutation
/24;
554 permutation
-= (permunreg
[1]*24);
555 permunreg
[2] = permutation
/6;
556 permutation
-= (permunreg
[2]*6);
557 permunreg
[3] = permutation
/2;
558 permutation
-= (permunreg
[3]*2);
559 permunreg
[4] = permutation
;
563 permunreg
[0] = permutation
/120;
564 permutation
-= (permunreg
[0]*120);
565 permunreg
[1] = permutation
/24;
566 permutation
-= (permunreg
[1]*24);
567 permunreg
[2] = permutation
/6;
568 permutation
-= (permunreg
[2]*6);
569 permunreg
[3] = permutation
/2;
570 permutation
-= (permunreg
[3]*2);
571 permunreg
[4] = permutation
;
574 permunreg
[0] = permutation
/60;
575 permutation
-= (permunreg
[0]*60);
576 permunreg
[1] = permutation
/12;
577 permutation
-= (permunreg
[1]*12);
578 permunreg
[2] = permutation
/3;
579 permutation
-= (permunreg
[2]*3);
580 permunreg
[3] = permutation
;
583 permunreg
[0] = permutation
/20;
584 permutation
-= (permunreg
[0]*20);
585 permunreg
[1] = permutation
/4;
586 permutation
-= (permunreg
[1]*4);
587 permunreg
[2] = permutation
;
590 permunreg
[0] = permutation
/5;
591 permutation
-= (permunreg
[0]*5);
592 permunreg
[1] = permutation
;
595 permunreg
[0] = permutation
;
598 // renumber registers back to standard numbers
600 bool used
[7] = { false, false, false, false, false, false, false };
601 for (int i
=0; i
< regCount
; ++i
) {
603 for (int u
=1; u
< 7; ++u
) {
605 if ( renum
== permunreg
[i
] ) {
614 bool needComma
= false;
615 for (int i
=0; i
< regCount
; ++i
) {
620 switch ( registers
[i
] ) {
621 case UNWIND_X86_REG_EBX
:
624 case UNWIND_X86_REG_ECX
:
627 case UNWIND_X86_REG_EDX
:
630 case UNWIND_X86_REG_EDI
:
633 case UNWIND_X86_REG_ESI
:
636 case UNWIND_X86_REG_EBP
:
646 case UNWIND_X86_MODE_DWARF
:
647 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
);
651 strcat(str
, "no unwind information");
655 if ( encoding
& UNWIND_HAS_LSDA
) {
656 strcat(str
, " LSDA");
661 #if SUPPORT_ARCH_arm64
663 void UnwindPrinter
<arm64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
666 switch ( encoding
& UNWIND_ARM64_MODE_MASK
) {
667 case UNWIND_ARM64_MODE_FRAMELESS
:
668 stackSize
= EXTRACT_BITS(encoding
, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
);
669 if ( stackSize
== 0 )
670 strcpy(str
, "no frame, no saved registers ");
672 sprintf(str
, "stack size=%d: ", 16 * stackSize
);
673 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
674 strcat(str
, "x19/20 ");
675 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
676 strcat(str
, "x21/22 ");
677 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
678 strcat(str
, "x23/24 ");
679 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
680 strcat(str
, "x25/26 ");
681 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
682 strcat(str
, "x27/28 ");
683 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
684 strcat(str
, "d8/9 ");
685 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
686 strcat(str
, "d10/11 ");
687 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
688 strcat(str
, "d12/13 ");
689 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
690 strcat(str
, "d14/15 ");
693 case UNWIND_ARM64_MODE_DWARF
:
694 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
696 case UNWIND_ARM64_MODE_FRAME
:
697 strcpy(str
, "std frame: ");
698 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
699 strcat(str
, "x19/20 ");
700 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
701 strcat(str
, "x21/22 ");
702 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
703 strcat(str
, "x23/24 ");
704 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
705 strcat(str
, "x25/26 ");
706 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
707 strcat(str
, "x27/28 ");
708 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
709 strcat(str
, "d8/9 ");
710 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
711 strcat(str
, "d10/11 ");
712 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
713 strcat(str
, "d12/13 ");
714 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
715 strcat(str
, "d14/15 ");
717 case UNWIND_ARM64_MODE_FRAME_OLD
:
718 strcpy(str
, "old frame: ");
719 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD
)
720 strcat(str
, "x21/22 ");
721 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD
)
722 strcat(str
, "x23/24 ");
723 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD
)
724 strcat(str
, "x25/26 ");
725 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD
)
726 strcat(str
, "x27/28 ");
727 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD
)
728 strcat(str
, "d8/9 ");
729 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD
)
730 strcat(str
, "d10/11 ");
731 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD
)
732 strcat(str
, "d12/13 ");
733 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD
)
734 strcat(str
, "d14/15 ");
743 const char* UnwindPrinter
<x86_64
>::personalityName(const macho_relocation_info
<x86_64::P
>* reloc
)
745 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
746 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
747 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
748 return &fStrings
[sym
.n_strx()];
752 const char* UnwindPrinter
<x86
>::personalityName(const macho_relocation_info
<x86::P
>* reloc
)
754 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
755 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
756 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
757 return &fStrings
[sym
.n_strx()];
760 #if SUPPORT_ARCH_arm64
762 const char* UnwindPrinter
<arm64
>::personalityName(const macho_relocation_info
<arm64::P
>* reloc
)
764 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
765 //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
766 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
767 return &fStrings
[sym
.n_strx()];
772 template <typename A
>
773 bool UnwindPrinter
<A
>::hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
)
775 const macho_relocation_info
<P
>* relocs
= (macho_relocation_info
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->reloff());
776 const macho_relocation_info
<P
>* relocsEnd
= &relocs
[fUnwindSection
->nreloc()];
777 for (const macho_relocation_info
<P
>* reloc
= relocs
; reloc
< relocsEnd
; ++reloc
) {
778 if ( reloc
->r_extern() && (reloc
->r_address() == sectionOffset
) ) {
779 *personalityStr
= this->personalityName(reloc
);
781 *addr
= fSymbols
[reloc
->r_symbolnum()].n_value();
789 template <typename A
>
790 void UnwindPrinter
<A
>::printObjectUnwindSection(bool showFunctionNames
)
792 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
793 archName(), fUnwindSection
->size(), fUnwindSection
->size() / sizeof(macho_compact_unwind_entry
<P
>));
795 const macho_compact_unwind_entry
<P
>* const entriesStart
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset());
796 const macho_compact_unwind_entry
<P
>* const entriesEnd
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset() + fUnwindSection
->size());
797 for (const macho_compact_unwind_entry
<P
>* entry
=entriesStart
; entry
< entriesEnd
; ++entry
) {
798 uint64_t entryAddress
= ((char*)entry
- (char*)entriesStart
) + fUnwindSection
->addr();
799 printf("0x%08llX:\n", entryAddress
);
800 const char* functionNameStr
;
802 uint32_t offsetInFunction
;
803 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::codeStartFieldOffset(), &functionNameStr
, &funcAddress
) ) {
804 offsetInFunction
= entry
->codeStart();
807 functionNameStr
= this->functionName(entry
->codeStart(), &offsetInFunction
);
808 funcAddress
= entry
->codeStart();
810 if ( offsetInFunction
== 0 )
811 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress
, functionNameStr
);
813 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress
+offsetInFunction
, functionNameStr
, offsetInFunction
);
815 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress
+offsetInFunction
+entry
->codeLen()), entry
->codeLen());
817 char encodingString
[200];
818 this->decode(entry
->compactUnwindInfo(), ((const uint8_t*)fHeader
), encodingString
);
819 printf(" unwind info: 0x%08X %s\n", entry
->compactUnwindInfo(), encodingString
);
821 const char* personalityNameStr
;
822 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::personalityFieldOffset(), &personalityNameStr
) ) {
823 printf(" personality: %s\n", personalityNameStr
);
826 printf(" personality:\n");
828 if ( entry
->lsda() == 0 ) {
833 const char* lsdaName
= this->functionName(entry
->lsda(), &lsdaOffset
);
834 if ( lsdaOffset
== 0 )
835 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry
->lsda(), lsdaName
);
837 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry
->lsda(), lsdaName
, lsdaOffset
);
844 template <typename A
>
845 void UnwindPrinter
<A
>::printUnwindSection(bool showFunctionNames
)
847 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
848 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
850 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
851 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
852 printf("\tversion=0x%08X\n", sectionHeader
->version());
853 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
854 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
855 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
856 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
857 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
858 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
859 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
860 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
861 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
862 printf("\t\tencoding[%3u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
864 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
865 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
866 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
867 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
869 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
870 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
871 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
872 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
873 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
875 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
876 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
877 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
878 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
879 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
880 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
881 const char* name
= showFunctionNames
? functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
) : "";
882 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), name
);
883 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
884 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
886 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
887 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
888 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
889 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
890 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
891 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
892 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
893 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
894 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
895 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
896 uint32_t funcOffset
= entry
[j
].functionOffset();
897 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
898 // verify there is a corresponding entry in lsda table
900 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
901 if ( lindex
[k
].functionOffset() == funcOffset
) {
907 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
910 char encodingString
[100];
911 decode(entry
[j
].encoding(), ((const uint8_t*)fHeader
)+funcOffset
, encodingString
);
912 const char* name
= showFunctionNames
? functionName(funcOffset
+fMachHeaderAddress
) : "";
913 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
914 j
, funcOffset
, entry
[j
].encoding(), encodingString
, name
);
917 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
918 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
919 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
920 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
921 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
922 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
923 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
924 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
925 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
926 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
927 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
928 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
930 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
931 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
933 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
934 char encodingString
[100];
935 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
936 decode(encoding
, ((const uint8_t*)fHeader
)+funcOff
, encodingString
);
937 const char* name
= showFunctionNames
? functionName(funcOff
+fMachHeaderAddress
) : "";
938 if ( encoding
& UNWIND_HAS_LSDA
) {
939 // verify there is a corresponding entry in lsda table
941 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
942 if ( lindex
[k
].functionOffset() == funcOff
) {
948 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
951 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
952 j
, funcOff
, encodingIndex
, encoding
, encodingString
, name
);
956 fprintf(stderr
, "\t\tbad page header\n");
962 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
, bool showFunctionNames
)
964 struct stat stat_buf
;
967 int fd
= ::open(path
, O_RDONLY
, 0);
969 throw "cannot open file";
970 if ( ::fstat(fd
, &stat_buf
) != 0 )
971 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
972 uint32_t length
= stat_buf
.st_size
;
973 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
974 if ( p
== ((uint8_t*)(-1)) )
975 throw "cannot map file";
977 const mach_header
* mh
= (mach_header
*)p
;
978 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
979 const struct fat_header
* fh
= (struct fat_header
*)p
;
980 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
981 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
982 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
983 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
984 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
985 if ( onlyArchs
.count(cputype
) ) {
988 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
989 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
, showFunctionNames
);
991 throw "in universal file, i386 slice does not contain i386 mach-o";
993 case CPU_TYPE_X86_64
:
994 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
995 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
997 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
999 #if SUPPORT_ARCH_arm64
1000 case CPU_TYPE_ARM64
:
1001 if ( UnwindPrinter
<arm64
>::validFile(p
+ offset
) )
1002 UnwindPrinter
<arm64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1004 throw "in universal file, arm64 slice does not contain arm64 mach-o";
1008 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1013 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
1014 UnwindPrinter
<x86
>::make(p
, length
, path
, showFunctionNames
);
1016 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
1017 UnwindPrinter
<x86_64
>::make(p
, length
, path
, showFunctionNames
);
1019 #if SUPPORT_ARCH_arm64
1020 else if ( UnwindPrinter
<arm64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM64
) ) {
1021 UnwindPrinter
<arm64
>::make(p
, length
, path
, showFunctionNames
);
1025 throw "not a known file type";
1028 catch (const char* msg
) {
1029 throwf("%s in %s", msg
, path
);
1034 int main(int argc
, const char* argv
[])
1036 std::set
<cpu_type_t
> onlyArchs
;
1037 std::vector
<const char*> files
;
1038 bool showFunctionNames
= true;
1041 for(int i
=1; i
< argc
; ++i
) {
1042 const char* arg
= argv
[i
];
1043 if ( arg
[0] == '-' ) {
1044 if ( strcmp(arg
, "-arch") == 0 ) {
1045 const char* arch
= argv
[++i
];
1046 if ( strcmp(arch
, "i386") == 0 )
1047 onlyArchs
.insert(CPU_TYPE_I386
);
1048 else if ( strcmp(arch
, "x86_64") == 0 )
1049 onlyArchs
.insert(CPU_TYPE_X86_64
);
1050 #if SUPPORT_ARCH_arm64
1051 else if ( strcmp(arch
, "arm64") == 0 )
1052 onlyArchs
.insert(CPU_TYPE_ARM64
);
1055 throwf("unknown architecture %s", arch
);
1057 else if ( strcmp(arg
, "-no_symbols") == 0 ) {
1058 showFunctionNames
= false;
1061 throwf("unknown option: %s\n", arg
);
1065 files
.push_back(arg
);
1069 // use all architectures if no restrictions specified
1070 if ( onlyArchs
.size() == 0 ) {
1071 onlyArchs
.insert(CPU_TYPE_I386
);
1072 onlyArchs
.insert(CPU_TYPE_X86_64
);
1073 #if SUPPORT_ARCH_arm64
1074 onlyArchs
.insert(CPU_TYPE_ARM64
);
1076 onlyArchs
.insert(CPU_TYPE_ARM
);
1079 // process each file
1080 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
1081 dump(*it
, onlyArchs
, showFunctionNames
);
1085 catch (const char* msg
) {
1086 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);