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 if ( fSymbols
[i
].n_value() == addr
) {
225 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
226 //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);
229 else if ( offset
!= NULL
) {
230 if ( closestSymbol
== NULL
) {
231 if ( fSymbols
[i
].n_value() < addr
)
232 closestSymbol
= &fSymbols
[i
];
235 if ( (fSymbols
[i
].n_value() < addr
) && (fSymbols
[i
].n_value() > closestSymbol
->n_value()) )
236 closestSymbol
= &fSymbols
[i
];
241 if ( closestSymbol
!= NULL
) {
242 *offset
= addr
- closestSymbol
->n_value();
243 return &fStrings
[closestSymbol
->n_strx()];
245 return "--anonymous function--";
250 template <typename A
>
251 bool UnwindPrinter
<A
>::findUnwindSection()
253 const char* unwindSectionName
= "__unwind_info";
254 const char* unwindSegmentName
= "__TEXT";
255 if ( fHeader
->filetype() == MH_OBJECT
) {
256 unwindSectionName
= "__compact_unwind";
257 unwindSegmentName
= "__LD";
259 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
260 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
261 const uint32_t cmd_count
= fHeader
->ncmds();
262 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
263 const macho_load_command
<P
>* cmd
= cmds
;
264 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
265 uint32_t size
= cmd
->cmdsize();
266 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
267 if ( endOfCmd
> endOfLoadCommands
)
268 throwf("load command #%d extends beyond the end of the load commands", i
);
269 if ( endOfCmd
> endOfFile
)
270 throwf("load command #%d extends beyond the end of the file", i
);
271 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
272 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
273 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
274 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
275 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
276 if ( (strncmp(sect
->sectname(), unwindSectionName
, 16) == 0) && (strcmp(sect
->segname(), unwindSegmentName
) == 0) ) {
277 fUnwindSection
= sect
;
278 fMachHeaderAddress
= segCmd
->vmaddr();
279 return fUnwindSection
;
283 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
288 #define EXTRACT_BITS(value, mask) \
289 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
293 void UnwindPrinter
<x86_64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
296 switch ( encoding
& UNWIND_X86_64_MODE_MASK
) {
297 case UNWIND_X86_64_MODE_RBP_FRAME
:
299 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
300 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
301 if ( savedRegistersLocations
== 0 ) {
302 strcpy(str
, "rbp frame, no saved registers");
305 sprintf(str
, "rbp frame, at -%d:", savedRegistersOffset
*8);
306 bool needComma
= false;
307 for (int i
=0; i
< 5; ++i
) {
312 switch (savedRegistersLocations
& 0x7) {
313 case UNWIND_X86_64_REG_NONE
:
316 case UNWIND_X86_64_REG_RBX
:
319 case UNWIND_X86_64_REG_R12
:
322 case UNWIND_X86_64_REG_R13
:
325 case UNWIND_X86_64_REG_R14
:
328 case UNWIND_X86_64_REG_R15
:
334 savedRegistersLocations
= (savedRegistersLocations
>> 3);
335 if ( savedRegistersLocations
== 0 )
341 case UNWIND_X86_64_MODE_STACK_IMMD
:
342 case UNWIND_X86_64_MODE_STACK_IND
:
344 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
345 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
346 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
347 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
348 if ( (encoding
& UNWIND_X86_64_MODE_MASK
) == UNWIND_X86_64_MODE_STACK_IND
) {
349 // stack size is encoded in subl $xxx,%esp instruction
350 uint32_t subl
= x86_64::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
351 sprintf(str
, "stack size=0x%08X, ", subl
+ 8*stackAdjust
);
354 sprintf(str
, "stack size=%d, ", stackSize
*8);
356 if ( regCount
== 0 ) {
357 strcat(str
, "no registers saved");
361 switch ( regCount
) {
363 permunreg
[0] = permutation
/120;
364 permutation
-= (permunreg
[0]*120);
365 permunreg
[1] = permutation
/24;
366 permutation
-= (permunreg
[1]*24);
367 permunreg
[2] = permutation
/6;
368 permutation
-= (permunreg
[2]*6);
369 permunreg
[3] = permutation
/2;
370 permutation
-= (permunreg
[3]*2);
371 permunreg
[4] = permutation
;
375 permunreg
[0] = permutation
/120;
376 permutation
-= (permunreg
[0]*120);
377 permunreg
[1] = permutation
/24;
378 permutation
-= (permunreg
[1]*24);
379 permunreg
[2] = permutation
/6;
380 permutation
-= (permunreg
[2]*6);
381 permunreg
[3] = permutation
/2;
382 permutation
-= (permunreg
[3]*2);
383 permunreg
[4] = permutation
;
386 permunreg
[0] = permutation
/60;
387 permutation
-= (permunreg
[0]*60);
388 permunreg
[1] = permutation
/12;
389 permutation
-= (permunreg
[1]*12);
390 permunreg
[2] = permutation
/3;
391 permutation
-= (permunreg
[2]*3);
392 permunreg
[3] = permutation
;
395 permunreg
[0] = permutation
/20;
396 permutation
-= (permunreg
[0]*20);
397 permunreg
[1] = permutation
/4;
398 permutation
-= (permunreg
[1]*4);
399 permunreg
[2] = permutation
;
402 permunreg
[0] = permutation
/5;
403 permutation
-= (permunreg
[0]*5);
404 permunreg
[1] = permutation
;
407 permunreg
[0] = permutation
;
410 // renumber registers back to standard numbers
412 bool used
[7] = { false, false, false, false, false, false, false };
413 for (int i
=0; i
< regCount
; ++i
) {
415 for (int u
=1; u
< 7; ++u
) {
417 if ( renum
== permunreg
[i
] ) {
426 bool needComma
= false;
427 for (int i
=0; i
< regCount
; ++i
) {
432 switch ( registers
[i
] ) {
433 case UNWIND_X86_64_REG_RBX
:
436 case UNWIND_X86_64_REG_R12
:
439 case UNWIND_X86_64_REG_R13
:
442 case UNWIND_X86_64_REG_R14
:
445 case UNWIND_X86_64_REG_R15
:
448 case UNWIND_X86_64_REG_RBP
:
458 case UNWIND_X86_64_MODE_DWARF
:
459 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
463 strcat(str
, "no unwind information");
467 if ( encoding
& UNWIND_HAS_LSDA
) {
468 strcat(str
, " LSDA");
474 void UnwindPrinter
<x86
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
477 switch ( encoding
& UNWIND_X86_MODE_MASK
) {
478 case UNWIND_X86_MODE_EBP_FRAME
:
480 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
481 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
482 if ( savedRegistersLocations
== 0 ) {
483 strcpy(str
, "ebp frame, no saved registers");
486 sprintf(str
, "ebp frame, at -%d:", savedRegistersOffset
*4);
487 bool needComma
= false;
488 for (int i
=0; i
< 5; ++i
) {
493 switch (savedRegistersLocations
& 0x7) {
494 case UNWIND_X86_REG_NONE
:
497 case UNWIND_X86_REG_EBX
:
500 case UNWIND_X86_REG_ECX
:
503 case UNWIND_X86_REG_EDX
:
506 case UNWIND_X86_REG_EDI
:
509 case UNWIND_X86_REG_ESI
:
515 savedRegistersLocations
= (savedRegistersLocations
>> 3);
516 if ( savedRegistersLocations
== 0 )
522 case UNWIND_X86_MODE_STACK_IMMD
:
523 case UNWIND_X86_MODE_STACK_IND
:
525 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
526 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
527 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
528 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
529 if ( (encoding
& UNWIND_X86_MODE_MASK
) == UNWIND_X86_MODE_STACK_IND
) {
530 // stack size is encoded in subl $xxx,%esp instruction
531 uint32_t subl
= x86::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
532 sprintf(str
, "stack size=0x%08X, ", subl
+4*stackAdjust
);
535 sprintf(str
, "stack size=%d, ", stackSize
*4);
537 if ( regCount
== 0 ) {
538 strcat(str
, "no saved regs");
542 switch ( regCount
) {
544 permunreg
[0] = permutation
/120;
545 permutation
-= (permunreg
[0]*120);
546 permunreg
[1] = permutation
/24;
547 permutation
-= (permunreg
[1]*24);
548 permunreg
[2] = permutation
/6;
549 permutation
-= (permunreg
[2]*6);
550 permunreg
[3] = permutation
/2;
551 permutation
-= (permunreg
[3]*2);
552 permunreg
[4] = permutation
;
556 permunreg
[0] = permutation
/120;
557 permutation
-= (permunreg
[0]*120);
558 permunreg
[1] = permutation
/24;
559 permutation
-= (permunreg
[1]*24);
560 permunreg
[2] = permutation
/6;
561 permutation
-= (permunreg
[2]*6);
562 permunreg
[3] = permutation
/2;
563 permutation
-= (permunreg
[3]*2);
564 permunreg
[4] = permutation
;
567 permunreg
[0] = permutation
/60;
568 permutation
-= (permunreg
[0]*60);
569 permunreg
[1] = permutation
/12;
570 permutation
-= (permunreg
[1]*12);
571 permunreg
[2] = permutation
/3;
572 permutation
-= (permunreg
[2]*3);
573 permunreg
[3] = permutation
;
576 permunreg
[0] = permutation
/20;
577 permutation
-= (permunreg
[0]*20);
578 permunreg
[1] = permutation
/4;
579 permutation
-= (permunreg
[1]*4);
580 permunreg
[2] = permutation
;
583 permunreg
[0] = permutation
/5;
584 permutation
-= (permunreg
[0]*5);
585 permunreg
[1] = permutation
;
588 permunreg
[0] = permutation
;
591 // renumber registers back to standard numbers
593 bool used
[7] = { false, false, false, false, false, false, false };
594 for (int i
=0; i
< regCount
; ++i
) {
596 for (int u
=1; u
< 7; ++u
) {
598 if ( renum
== permunreg
[i
] ) {
607 bool needComma
= false;
608 for (int i
=0; i
< regCount
; ++i
) {
613 switch ( registers
[i
] ) {
614 case UNWIND_X86_REG_EBX
:
617 case UNWIND_X86_REG_ECX
:
620 case UNWIND_X86_REG_EDX
:
623 case UNWIND_X86_REG_EDI
:
626 case UNWIND_X86_REG_ESI
:
629 case UNWIND_X86_REG_EBP
:
639 case UNWIND_X86_MODE_DWARF
:
640 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
);
644 strcat(str
, "no unwind information");
648 if ( encoding
& UNWIND_HAS_LSDA
) {
649 strcat(str
, " LSDA");
654 #if SUPPORT_ARCH_arm64
656 void UnwindPrinter
<arm64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
659 switch ( encoding
& UNWIND_ARM64_MODE_MASK
) {
660 case UNWIND_ARM64_MODE_FRAMELESS
:
661 stackSize
= EXTRACT_BITS(encoding
, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
);
662 if ( stackSize
== 0 )
663 strcpy(str
, "no frame, no saved registers ");
665 sprintf(str
, "stack size=%d: ", 16 * stackSize
);
666 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
667 strcat(str
, "x19/20 ");
668 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
669 strcat(str
, "x21/22 ");
670 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
671 strcat(str
, "x23/24 ");
672 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
673 strcat(str
, "x25/26 ");
674 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
675 strcat(str
, "x27/28 ");
676 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
677 strcat(str
, "d8/9 ");
678 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
679 strcat(str
, "d10/11 ");
680 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
681 strcat(str
, "d12/13 ");
682 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
683 strcat(str
, "d14/15 ");
686 case UNWIND_ARM64_MODE_DWARF
:
687 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
689 case UNWIND_ARM64_MODE_FRAME
:
690 strcpy(str
, "std frame: ");
691 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
692 strcat(str
, "x19/20 ");
693 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
694 strcat(str
, "x21/22 ");
695 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
696 strcat(str
, "x23/24 ");
697 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
698 strcat(str
, "x25/26 ");
699 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
700 strcat(str
, "x27/28 ");
701 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
702 strcat(str
, "d8/9 ");
703 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
704 strcat(str
, "d10/11 ");
705 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
706 strcat(str
, "d12/13 ");
707 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
708 strcat(str
, "d14/15 ");
710 case UNWIND_ARM64_MODE_FRAME_OLD
:
711 strcpy(str
, "old frame: ");
712 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD
)
713 strcat(str
, "x21/22 ");
714 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD
)
715 strcat(str
, "x23/24 ");
716 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD
)
717 strcat(str
, "x25/26 ");
718 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD
)
719 strcat(str
, "x27/28 ");
720 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD
)
721 strcat(str
, "d8/9 ");
722 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD
)
723 strcat(str
, "d10/11 ");
724 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD
)
725 strcat(str
, "d12/13 ");
726 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD
)
727 strcat(str
, "d14/15 ");
734 const char* UnwindPrinter
<x86_64
>::personalityName(const macho_relocation_info
<x86_64::P
>* reloc
)
736 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
737 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
738 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
739 return &fStrings
[sym
.n_strx()];
743 const char* UnwindPrinter
<x86
>::personalityName(const macho_relocation_info
<x86::P
>* reloc
)
745 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
746 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "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()];
751 #if SUPPORT_ARCH_arm64
753 const char* UnwindPrinter
<arm64
>::personalityName(const macho_relocation_info
<arm64::P
>* reloc
)
755 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
756 //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
757 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
758 return &fStrings
[sym
.n_strx()];
762 template <typename A
>
763 bool UnwindPrinter
<A
>::hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
)
765 const macho_relocation_info
<P
>* relocs
= (macho_relocation_info
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->reloff());
766 const macho_relocation_info
<P
>* relocsEnd
= &relocs
[fUnwindSection
->nreloc()];
767 for (const macho_relocation_info
<P
>* reloc
= relocs
; reloc
< relocsEnd
; ++reloc
) {
768 if ( reloc
->r_extern() && (reloc
->r_address() == sectionOffset
) ) {
769 *personalityStr
= this->personalityName(reloc
);
771 *addr
= fSymbols
[reloc
->r_symbolnum()].n_value();
779 template <typename A
>
780 void UnwindPrinter
<A
>::printObjectUnwindSection(bool showFunctionNames
)
782 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
783 archName(), fUnwindSection
->size(), fUnwindSection
->size() / sizeof(macho_compact_unwind_entry
<P
>));
785 const macho_compact_unwind_entry
<P
>* const entriesStart
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset());
786 const macho_compact_unwind_entry
<P
>* const entriesEnd
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset() + fUnwindSection
->size());
787 for (const macho_compact_unwind_entry
<P
>* entry
=entriesStart
; entry
< entriesEnd
; ++entry
) {
788 uint64_t entryAddress
= ((char*)entry
- (char*)entriesStart
) + fUnwindSection
->addr();
789 printf("0x%08llX:\n", entryAddress
);
790 const char* functionNameStr
;
792 uint32_t offsetInFunction
;
793 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::codeStartFieldOffset(), &functionNameStr
, &funcAddress
) ) {
794 offsetInFunction
= entry
->codeStart();
797 functionNameStr
= this->functionName(entry
->codeStart(), &offsetInFunction
);
799 if ( offsetInFunction
== 0 )
800 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress
, functionNameStr
);
802 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress
+offsetInFunction
, functionNameStr
, offsetInFunction
);
804 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress
+offsetInFunction
+entry
->codeLen()), entry
->codeLen());
806 char encodingString
[200];
807 this->decode(entry
->compactUnwindInfo(), ((const uint8_t*)fHeader
), encodingString
);
808 printf(" unwind info: 0x%08X %s\n", entry
->compactUnwindInfo(), encodingString
);
810 const char* personalityNameStr
;
811 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::personalityFieldOffset(), &personalityNameStr
) ) {
812 printf(" personality: %s\n", personalityNameStr
);
815 printf(" personality:\n");
817 if ( entry
->lsda() == 0 ) {
822 const char* lsdaName
= this->functionName(entry
->lsda(), &lsdaOffset
);
823 if ( lsdaOffset
== 0 )
824 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry
->lsda(), lsdaName
);
826 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry
->lsda(), lsdaName
, lsdaOffset
);
833 template <typename A
>
834 void UnwindPrinter
<A
>::printUnwindSection(bool showFunctionNames
)
836 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
837 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
839 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
840 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
841 printf("\tversion=0x%08X\n", sectionHeader
->version());
842 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
843 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
844 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
845 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
846 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
847 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
848 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
849 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
850 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
851 printf("\t\tencoding[%3u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
853 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
854 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
855 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
856 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
858 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
859 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
860 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
861 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
862 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
864 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
865 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
866 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
867 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
868 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
869 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
870 const char* name
= showFunctionNames
? functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
) : "";
871 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), name
);
872 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
873 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
875 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
876 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
877 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
878 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
879 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
880 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
881 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
882 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
883 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
884 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
885 uint32_t funcOffset
= entry
[j
].functionOffset();
886 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
887 // verify there is a corresponding entry in lsda table
889 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
890 if ( lindex
[k
].functionOffset() == funcOffset
) {
896 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
899 char encodingString
[100];
900 decode(entry
[j
].encoding(), ((const uint8_t*)fHeader
)+funcOffset
, encodingString
);
901 const char* name
= showFunctionNames
? functionName(funcOffset
+fMachHeaderAddress
) : "";
902 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
903 j
, funcOffset
, entry
[j
].encoding(), encodingString
, name
);
906 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
907 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
908 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
909 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
910 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
911 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
912 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
913 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
914 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
915 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
916 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
917 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
919 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
920 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
922 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
923 char encodingString
[100];
924 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
925 decode(encoding
, ((const uint8_t*)fHeader
)+funcOff
, encodingString
);
926 const char* name
= showFunctionNames
? functionName(funcOff
+fMachHeaderAddress
) : "";
927 if ( encoding
& UNWIND_HAS_LSDA
) {
928 // verify there is a corresponding entry in lsda table
930 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
931 if ( lindex
[k
].functionOffset() == funcOff
) {
937 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
940 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
941 j
, funcOff
, encodingIndex
, encoding
, encodingString
, name
);
945 fprintf(stderr
, "\t\tbad page header\n");
951 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
, bool showFunctionNames
)
953 struct stat stat_buf
;
956 int fd
= ::open(path
, O_RDONLY
, 0);
958 throw "cannot open file";
959 if ( ::fstat(fd
, &stat_buf
) != 0 )
960 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
961 uint32_t length
= stat_buf
.st_size
;
962 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
963 if ( p
== ((uint8_t*)(-1)) )
964 throw "cannot map file";
966 const mach_header
* mh
= (mach_header
*)p
;
967 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
968 const struct fat_header
* fh
= (struct fat_header
*)p
;
969 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
970 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
971 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
972 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
973 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
974 if ( onlyArchs
.count(cputype
) ) {
977 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
978 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
, showFunctionNames
);
980 throw "in universal file, i386 slice does not contain i386 mach-o";
982 case CPU_TYPE_X86_64
:
983 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
984 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
986 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
988 #if SUPPORT_ARCH_arm64
990 if ( UnwindPrinter
<arm64
>::validFile(p
+ offset
) )
991 UnwindPrinter
<arm64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
993 throw "in universal file, arm64 slice does not contain arm mach-o";
997 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1002 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
1003 UnwindPrinter
<x86
>::make(p
, length
, path
, showFunctionNames
);
1005 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
1006 UnwindPrinter
<x86_64
>::make(p
, length
, path
, showFunctionNames
);
1008 #if SUPPORT_ARCH_arm64
1009 else if ( UnwindPrinter
<arm64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM64
) ) {
1010 UnwindPrinter
<arm64
>::make(p
, length
, path
, showFunctionNames
);
1014 throw "not a known file type";
1017 catch (const char* msg
) {
1018 throwf("%s in %s", msg
, path
);
1023 int main(int argc
, const char* argv
[])
1025 std::set
<cpu_type_t
> onlyArchs
;
1026 std::vector
<const char*> files
;
1027 bool showFunctionNames
= true;
1030 for(int i
=1; i
< argc
; ++i
) {
1031 const char* arg
= argv
[i
];
1032 if ( arg
[0] == '-' ) {
1033 if ( strcmp(arg
, "-arch") == 0 ) {
1034 const char* arch
= argv
[++i
];
1035 if ( strcmp(arch
, "i386") == 0 )
1036 onlyArchs
.insert(CPU_TYPE_I386
);
1037 else if ( strcmp(arch
, "x86_64") == 0 )
1038 onlyArchs
.insert(CPU_TYPE_X86_64
);
1039 #if SUPPORT_ARCH_arm64
1040 else if ( strcmp(arch
, "arm64") == 0 )
1041 onlyArchs
.insert(CPU_TYPE_ARM64
);
1044 throwf("unknown architecture %s", arch
);
1046 else if ( strcmp(arg
, "-no_symbols") == 0 ) {
1047 showFunctionNames
= false;
1050 throwf("unknown option: %s\n", arg
);
1054 files
.push_back(arg
);
1058 // use all architectures if no restrictions specified
1059 if ( onlyArchs
.size() == 0 ) {
1060 onlyArchs
.insert(CPU_TYPE_I386
);
1061 onlyArchs
.insert(CPU_TYPE_X86_64
);
1062 #if SUPPORT_ARCH_arm64
1063 onlyArchs
.insert(CPU_TYPE_ARM64
);
1067 // process each file
1068 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
1069 dump(*it
, onlyArchs
, showFunctionNames
);
1073 catch (const char* msg
) {
1074 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);