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()) {
167 bool UnwindPrinter
<arm
>::validFile(const uint8_t* fileContent
)
169 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
170 if ( header
->magic() != MH_MAGIC
)
172 if ( header
->cputype() != CPU_TYPE_ARM
)
174 switch (header
->filetype()) {
185 template <typename A
>
186 UnwindPrinter
<A
>::UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, bool showFunctionNames
)
187 : fHeader(NULL
), fLength(fileLength
), fUnwindSection(NULL
),
188 fStrings(NULL
), fStringsEnd(NULL
), fSymbols(NULL
), fSymbolCount(0), fMachHeaderAddress(0)
191 if ( ! validFile(fileContent
) )
192 throw "not a mach-o file that can be checked";
194 fPath
= strdup(path
);
195 fHeader
= (const macho_header
<P
>*)fileContent
;
197 getSymbolTableInfo();
199 if ( findUnwindSection() ) {
200 if ( fHeader
->filetype() == MH_OBJECT
)
201 printObjectUnwindSection(showFunctionNames
);
203 printUnwindSection(showFunctionNames
);
208 template <typename A
>
209 void UnwindPrinter
<A
>::getSymbolTableInfo()
211 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
212 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
213 const uint32_t cmd_count
= fHeader
->ncmds();
214 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
215 const macho_load_command
<P
>* cmd
= cmds
;
216 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
217 uint32_t size
= cmd
->cmdsize();
218 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
219 if ( endOfCmd
> endOfLoadCommands
)
220 throwf("load command #%d extends beyond the end of the load commands", i
);
221 if ( endOfCmd
> endOfFile
)
222 throwf("load command #%d extends beyond the end of the file", i
);
223 if ( cmd
->cmd() == LC_SYMTAB
) {
224 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
225 fSymbolCount
= symtab
->nsyms();
226 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
227 fStrings
= (char*)fHeader
+ symtab
->stroff();
228 fStringsEnd
= fStrings
+ symtab
->strsize();
230 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
234 template <typename A
>
235 const char* UnwindPrinter
<A
>::functionName(pint_t addr
, uint32_t* offset
)
237 const macho_nlist
<P
>* closestSymbol
= NULL
;
238 if ( offset
!= NULL
)
240 for (uint32_t i
=0; i
< fSymbolCount
; ++i
) {
241 uint8_t type
= fSymbols
[i
].n_type();
242 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
243 uint32_t value
= fSymbols
[i
].n_value();
244 if ( value
== addr
) {
245 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
248 if ( fSymbols
[i
].n_desc() & N_ARM_THUMB_DEF
)
250 if ( value
== addr
) {
251 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
252 //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);
255 else if ( offset
!= NULL
) {
256 if ( closestSymbol
== NULL
) {
257 if ( fSymbols
[i
].n_value() < addr
)
258 closestSymbol
= &fSymbols
[i
];
261 if ( (fSymbols
[i
].n_value() < addr
) && (fSymbols
[i
].n_value() > closestSymbol
->n_value()) )
262 closestSymbol
= &fSymbols
[i
];
267 if ( closestSymbol
!= NULL
) {
268 *offset
= addr
- closestSymbol
->n_value();
269 return &fStrings
[closestSymbol
->n_strx()];
271 return "--anonymous function--";
276 template <typename A
>
277 bool UnwindPrinter
<A
>::findUnwindSection()
279 const char* unwindSectionName
= "__unwind_info";
280 const char* unwindSegmentName
= "__TEXT";
281 if ( fHeader
->filetype() == MH_OBJECT
) {
282 unwindSectionName
= "__compact_unwind";
283 unwindSegmentName
= "__LD";
285 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
286 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
287 const uint32_t cmd_count
= fHeader
->ncmds();
288 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
289 const macho_load_command
<P
>* cmd
= cmds
;
290 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
291 uint32_t size
= cmd
->cmdsize();
292 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
293 if ( endOfCmd
> endOfLoadCommands
)
294 throwf("load command #%d extends beyond the end of the load commands", i
);
295 if ( endOfCmd
> endOfFile
)
296 throwf("load command #%d extends beyond the end of the file", i
);
297 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
298 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
299 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
300 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
301 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
302 if ( (strncmp(sect
->sectname(), unwindSectionName
, 16) == 0) && (strcmp(sect
->segname(), unwindSegmentName
) == 0) ) {
303 fUnwindSection
= sect
;
304 fMachHeaderAddress
= segCmd
->vmaddr();
305 return fUnwindSection
;
309 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
314 #define EXTRACT_BITS(value, mask) \
315 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
319 void UnwindPrinter
<x86_64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
322 switch ( encoding
& UNWIND_X86_64_MODE_MASK
) {
323 case UNWIND_X86_64_MODE_RBP_FRAME
:
325 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
326 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
327 if ( savedRegistersLocations
== 0 ) {
328 strcpy(str
, "rbp frame, no saved registers");
331 sprintf(str
, "rbp frame, at -%d:", savedRegistersOffset
*8);
332 bool needComma
= false;
333 for (int i
=0; i
< 5; ++i
) {
338 switch (savedRegistersLocations
& 0x7) {
339 case UNWIND_X86_64_REG_NONE
:
342 case UNWIND_X86_64_REG_RBX
:
345 case UNWIND_X86_64_REG_R12
:
348 case UNWIND_X86_64_REG_R13
:
351 case UNWIND_X86_64_REG_R14
:
354 case UNWIND_X86_64_REG_R15
:
360 savedRegistersLocations
= (savedRegistersLocations
>> 3);
361 if ( savedRegistersLocations
== 0 )
367 case UNWIND_X86_64_MODE_STACK_IMMD
:
368 case UNWIND_X86_64_MODE_STACK_IND
:
370 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
371 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
372 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
373 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
374 if ( (encoding
& UNWIND_X86_64_MODE_MASK
) == UNWIND_X86_64_MODE_STACK_IND
) {
375 // stack size is encoded in subl $xxx,%esp instruction
376 uint32_t subl
= x86_64::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
377 sprintf(str
, "stack size=0x%08X, ", subl
+ 8*stackAdjust
);
380 sprintf(str
, "stack size=%d, ", stackSize
*8);
382 if ( regCount
== 0 ) {
383 strcat(str
, "no registers saved");
387 switch ( regCount
) {
389 permunreg
[0] = permutation
/120;
390 permutation
-= (permunreg
[0]*120);
391 permunreg
[1] = permutation
/24;
392 permutation
-= (permunreg
[1]*24);
393 permunreg
[2] = permutation
/6;
394 permutation
-= (permunreg
[2]*6);
395 permunreg
[3] = permutation
/2;
396 permutation
-= (permunreg
[3]*2);
397 permunreg
[4] = permutation
;
401 permunreg
[0] = permutation
/120;
402 permutation
-= (permunreg
[0]*120);
403 permunreg
[1] = permutation
/24;
404 permutation
-= (permunreg
[1]*24);
405 permunreg
[2] = permutation
/6;
406 permutation
-= (permunreg
[2]*6);
407 permunreg
[3] = permutation
/2;
408 permutation
-= (permunreg
[3]*2);
409 permunreg
[4] = permutation
;
412 permunreg
[0] = permutation
/60;
413 permutation
-= (permunreg
[0]*60);
414 permunreg
[1] = permutation
/12;
415 permutation
-= (permunreg
[1]*12);
416 permunreg
[2] = permutation
/3;
417 permutation
-= (permunreg
[2]*3);
418 permunreg
[3] = permutation
;
421 permunreg
[0] = permutation
/20;
422 permutation
-= (permunreg
[0]*20);
423 permunreg
[1] = permutation
/4;
424 permutation
-= (permunreg
[1]*4);
425 permunreg
[2] = permutation
;
428 permunreg
[0] = permutation
/5;
429 permutation
-= (permunreg
[0]*5);
430 permunreg
[1] = permutation
;
433 permunreg
[0] = permutation
;
436 // renumber registers back to standard numbers
438 bool used
[7] = { false, false, false, false, false, false, false };
439 for (int i
=0; i
< regCount
; ++i
) {
441 for (int u
=1; u
< 7; ++u
) {
443 if ( renum
== permunreg
[i
] ) {
452 bool needComma
= false;
453 for (int i
=0; i
< regCount
; ++i
) {
458 switch ( registers
[i
] ) {
459 case UNWIND_X86_64_REG_RBX
:
462 case UNWIND_X86_64_REG_R12
:
465 case UNWIND_X86_64_REG_R13
:
468 case UNWIND_X86_64_REG_R14
:
471 case UNWIND_X86_64_REG_R15
:
474 case UNWIND_X86_64_REG_RBP
:
484 case UNWIND_X86_64_MODE_DWARF
:
485 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
489 strcat(str
, "no unwind information");
493 if ( encoding
& UNWIND_HAS_LSDA
) {
494 strcat(str
, " LSDA");
500 void UnwindPrinter
<x86
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
503 switch ( encoding
& UNWIND_X86_MODE_MASK
) {
504 case UNWIND_X86_MODE_EBP_FRAME
:
506 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
507 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
508 if ( savedRegistersLocations
== 0 ) {
509 strcpy(str
, "ebp frame, no saved registers");
512 sprintf(str
, "ebp frame, at -%d:", savedRegistersOffset
*4);
513 bool needComma
= false;
514 for (int i
=0; i
< 5; ++i
) {
519 switch (savedRegistersLocations
& 0x7) {
520 case UNWIND_X86_REG_NONE
:
523 case UNWIND_X86_REG_EBX
:
526 case UNWIND_X86_REG_ECX
:
529 case UNWIND_X86_REG_EDX
:
532 case UNWIND_X86_REG_EDI
:
535 case UNWIND_X86_REG_ESI
:
541 savedRegistersLocations
= (savedRegistersLocations
>> 3);
542 if ( savedRegistersLocations
== 0 )
548 case UNWIND_X86_MODE_STACK_IMMD
:
549 case UNWIND_X86_MODE_STACK_IND
:
551 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
552 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
553 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
554 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
555 if ( (encoding
& UNWIND_X86_MODE_MASK
) == UNWIND_X86_MODE_STACK_IND
) {
556 // stack size is encoded in subl $xxx,%esp instruction
557 uint32_t subl
= x86::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
558 sprintf(str
, "stack size=0x%08X, ", subl
+4*stackAdjust
);
561 sprintf(str
, "stack size=%d, ", stackSize
*4);
563 if ( regCount
== 0 ) {
564 strcat(str
, "no saved regs");
568 switch ( regCount
) {
570 permunreg
[0] = permutation
/120;
571 permutation
-= (permunreg
[0]*120);
572 permunreg
[1] = permutation
/24;
573 permutation
-= (permunreg
[1]*24);
574 permunreg
[2] = permutation
/6;
575 permutation
-= (permunreg
[2]*6);
576 permunreg
[3] = permutation
/2;
577 permutation
-= (permunreg
[3]*2);
578 permunreg
[4] = permutation
;
582 permunreg
[0] = permutation
/120;
583 permutation
-= (permunreg
[0]*120);
584 permunreg
[1] = permutation
/24;
585 permutation
-= (permunreg
[1]*24);
586 permunreg
[2] = permutation
/6;
587 permutation
-= (permunreg
[2]*6);
588 permunreg
[3] = permutation
/2;
589 permutation
-= (permunreg
[3]*2);
590 permunreg
[4] = permutation
;
593 permunreg
[0] = permutation
/60;
594 permutation
-= (permunreg
[0]*60);
595 permunreg
[1] = permutation
/12;
596 permutation
-= (permunreg
[1]*12);
597 permunreg
[2] = permutation
/3;
598 permutation
-= (permunreg
[2]*3);
599 permunreg
[3] = permutation
;
602 permunreg
[0] = permutation
/20;
603 permutation
-= (permunreg
[0]*20);
604 permunreg
[1] = permutation
/4;
605 permutation
-= (permunreg
[1]*4);
606 permunreg
[2] = permutation
;
609 permunreg
[0] = permutation
/5;
610 permutation
-= (permunreg
[0]*5);
611 permunreg
[1] = permutation
;
614 permunreg
[0] = permutation
;
617 // renumber registers back to standard numbers
619 bool used
[7] = { false, false, false, false, false, false, false };
620 for (int i
=0; i
< regCount
; ++i
) {
622 for (int u
=1; u
< 7; ++u
) {
624 if ( renum
== permunreg
[i
] ) {
633 bool needComma
= false;
634 for (int i
=0; i
< regCount
; ++i
) {
639 switch ( registers
[i
] ) {
640 case UNWIND_X86_REG_EBX
:
643 case UNWIND_X86_REG_ECX
:
646 case UNWIND_X86_REG_EDX
:
649 case UNWIND_X86_REG_EDI
:
652 case UNWIND_X86_REG_ESI
:
655 case UNWIND_X86_REG_EBP
:
665 case UNWIND_X86_MODE_DWARF
:
666 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
);
670 strcat(str
, "no unwind information");
674 if ( encoding
& UNWIND_HAS_LSDA
) {
675 strcat(str
, " LSDA");
680 #if SUPPORT_ARCH_arm64
682 void UnwindPrinter
<arm64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
685 switch ( encoding
& UNWIND_ARM64_MODE_MASK
) {
686 case UNWIND_ARM64_MODE_FRAMELESS
:
687 stackSize
= EXTRACT_BITS(encoding
, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
);
688 if ( stackSize
== 0 )
689 strcpy(str
, "no frame, no saved registers ");
691 sprintf(str
, "stack size=%d: ", 16 * stackSize
);
692 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
693 strcat(str
, "x19/20 ");
694 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
695 strcat(str
, "x21/22 ");
696 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
697 strcat(str
, "x23/24 ");
698 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
699 strcat(str
, "x25/26 ");
700 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
701 strcat(str
, "x27/28 ");
702 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
703 strcat(str
, "d8/9 ");
704 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
705 strcat(str
, "d10/11 ");
706 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
707 strcat(str
, "d12/13 ");
708 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
709 strcat(str
, "d14/15 ");
712 case UNWIND_ARM64_MODE_DWARF
:
713 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
715 case UNWIND_ARM64_MODE_FRAME
:
716 strcpy(str
, "std frame: ");
717 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
718 strcat(str
, "x19/20 ");
719 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
720 strcat(str
, "x21/22 ");
721 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
722 strcat(str
, "x23/24 ");
723 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
724 strcat(str
, "x25/26 ");
725 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
726 strcat(str
, "x27/28 ");
727 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
728 strcat(str
, "d8/9 ");
729 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
730 strcat(str
, "d10/11 ");
731 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
732 strcat(str
, "d12/13 ");
733 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
734 strcat(str
, "d14/15 ");
736 case UNWIND_ARM64_MODE_FRAME_OLD
:
737 strcpy(str
, "old frame: ");
738 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD
)
739 strcat(str
, "x21/22 ");
740 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD
)
741 strcat(str
, "x23/24 ");
742 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD
)
743 strcat(str
, "x25/26 ");
744 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD
)
745 strcat(str
, "x27/28 ");
746 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD
)
747 strcat(str
, "d8/9 ");
748 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD
)
749 strcat(str
, "d10/11 ");
750 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD
)
751 strcat(str
, "d12/13 ");
752 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD
)
753 strcat(str
, "d14/15 ");
760 void UnwindPrinter
<arm
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
763 switch ( encoding
& UNWIND_ARM_MODE_MASK
) {
764 case UNWIND_ARM_MODE_DWARF
:
765 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_ARM_DWARF_SECTION_OFFSET
);
767 case UNWIND_ARM_MODE_FRAME
:
768 case UNWIND_ARM_MODE_FRAME_D
:
769 switch ( encoding
& UNWIND_ARM_FRAME_STACK_ADJUST_MASK
) {
771 strcpy(str
, "std frame: ");
774 strcat(str
, "std frame(sp adj 4): ");
777 strcat(str
, "std frame(sp adj 8): ");
780 strcat(str
, "std frame(sp adj 12): ");
783 if ( encoding
& UNWIND_ARM_FRAME_FIRST_PUSH_R4
)
785 if ( encoding
& UNWIND_ARM_FRAME_FIRST_PUSH_R5
)
787 if ( encoding
& UNWIND_ARM_FRAME_FIRST_PUSH_R6
)
790 if ( encoding
& 0x000000F8)
792 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R8
)
794 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R9
)
796 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R10
)
798 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R11
)
800 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R12
)
803 if ( (encoding
& UNWIND_ARM_MODE_MASK
) == UNWIND_ARM_MODE_FRAME_D
) {
804 switch ( encoding
& UNWIND_ARM_FRAME_D_REG_COUNT_MASK
) {
806 strcat(str
, " / d8 ");
809 strcat(str
, " / d8,d10 ");
812 strcat(str
, " / d8,d10,d12 ");
815 strcat(str
, " / d8,d10,d12,d14 ");
818 strcat(str
, " / d12,d14 / d8,d9,d10 ");
821 strcat(str
, " / d14 / d8,d9,d10,d11,d12");
824 strcat(str
, " / d8,d9,d10,d11,d12,d13,d14 ");
827 strcat(str
, " / d8,d9,d10,d11,d12,d13,d14 ");
830 strcat(str
, " / unknown D register usage ");
838 strcpy(str
, "no unwind information");
840 strcpy(str
, "unsupported compact unwind");
847 const char* UnwindPrinter
<x86_64
>::personalityName(const macho_relocation_info
<x86_64::P
>* reloc
)
849 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
850 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
851 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
852 return &fStrings
[sym
.n_strx()];
856 const char* UnwindPrinter
<x86
>::personalityName(const macho_relocation_info
<x86::P
>* reloc
)
858 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
859 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
860 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
861 return &fStrings
[sym
.n_strx()];
864 #if SUPPORT_ARCH_arm64
866 const char* UnwindPrinter
<arm64
>::personalityName(const macho_relocation_info
<arm64::P
>* reloc
)
868 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
869 //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
870 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
871 return &fStrings
[sym
.n_strx()];
876 const char* UnwindPrinter
<arm
>::personalityName(const macho_relocation_info
<arm::P
>* reloc
)
878 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
879 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
880 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
881 return &fStrings
[sym
.n_strx()];
884 template <typename A
>
885 bool UnwindPrinter
<A
>::hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
)
887 const macho_relocation_info
<P
>* relocs
= (macho_relocation_info
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->reloff());
888 const macho_relocation_info
<P
>* relocsEnd
= &relocs
[fUnwindSection
->nreloc()];
889 for (const macho_relocation_info
<P
>* reloc
= relocs
; reloc
< relocsEnd
; ++reloc
) {
890 if ( reloc
->r_extern() && (reloc
->r_address() == sectionOffset
) ) {
891 *personalityStr
= this->personalityName(reloc
);
893 *addr
= fSymbols
[reloc
->r_symbolnum()].n_value();
901 template <typename A
>
902 void UnwindPrinter
<A
>::printObjectUnwindSection(bool showFunctionNames
)
904 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
905 archName(), fUnwindSection
->size(), fUnwindSection
->size() / sizeof(macho_compact_unwind_entry
<P
>));
907 const macho_compact_unwind_entry
<P
>* const entriesStart
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset());
908 const macho_compact_unwind_entry
<P
>* const entriesEnd
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset() + fUnwindSection
->size());
909 for (const macho_compact_unwind_entry
<P
>* entry
=entriesStart
; entry
< entriesEnd
; ++entry
) {
910 uint64_t entryAddress
= ((char*)entry
- (char*)entriesStart
) + fUnwindSection
->addr();
911 printf("0x%08llX:\n", entryAddress
);
912 const char* functionNameStr
;
914 uint32_t offsetInFunction
;
915 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::codeStartFieldOffset(), &functionNameStr
, &funcAddress
) ) {
916 offsetInFunction
= entry
->codeStart();
919 functionNameStr
= this->functionName(entry
->codeStart(), &offsetInFunction
);
920 funcAddress
= entry
->codeStart();
922 if ( offsetInFunction
== 0 )
923 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress
, functionNameStr
);
925 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress
+offsetInFunction
, functionNameStr
, offsetInFunction
);
927 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress
+offsetInFunction
+entry
->codeLen()), entry
->codeLen());
929 char encodingString
[200];
930 this->decode(entry
->compactUnwindInfo(), ((const uint8_t*)fHeader
), encodingString
);
931 printf(" unwind info: 0x%08X %s\n", entry
->compactUnwindInfo(), encodingString
);
933 const char* personalityNameStr
;
934 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::personalityFieldOffset(), &personalityNameStr
) ) {
935 printf(" personality: %s\n", personalityNameStr
);
938 printf(" personality:\n");
940 if ( entry
->lsda() == 0 ) {
945 const char* lsdaName
= this->functionName(entry
->lsda(), &lsdaOffset
);
946 if ( lsdaOffset
== 0 )
947 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry
->lsda(), lsdaName
);
949 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry
->lsda(), lsdaName
, lsdaOffset
);
956 template <typename A
>
957 void UnwindPrinter
<A
>::printUnwindSection(bool showFunctionNames
)
959 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
960 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
962 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
963 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
964 printf("\tversion=0x%08X\n", sectionHeader
->version());
965 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
966 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
967 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
968 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
969 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
970 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
971 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
972 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
973 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
974 printf("\t\tencoding[%3u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
976 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
977 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
978 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
979 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
981 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
982 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
983 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
984 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
985 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
987 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
988 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
989 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
990 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
991 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
992 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
993 const char* name
= showFunctionNames
? functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
) : "";
994 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), name
);
995 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
996 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
998 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
999 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
1000 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
1001 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
1002 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
1003 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
1004 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
1005 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
1006 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
1007 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
1008 uint32_t funcOffset
= entry
[j
].functionOffset();
1009 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
1010 // verify there is a corresponding entry in lsda table
1012 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
1013 if ( lindex
[k
].functionOffset() == funcOffset
) {
1019 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
1022 char encodingString
[100];
1023 decode(entry
[j
].encoding(), ((const uint8_t*)fHeader
)+funcOffset
, encodingString
);
1024 const char* name
= showFunctionNames
? functionName(funcOffset
+fMachHeaderAddress
) : "";
1025 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
1026 j
, funcOffset
, entry
[j
].encoding(), encodingString
, name
);
1029 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
1030 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
1031 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
1032 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
1033 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
1034 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
1035 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
1036 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
1037 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
1038 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
1039 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
1040 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
1042 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
1043 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
1045 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
1046 char encodingString
[100];
1047 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
1048 decode(encoding
, ((const uint8_t*)fHeader
)+funcOff
, encodingString
);
1049 const char* name
= showFunctionNames
? functionName(funcOff
+fMachHeaderAddress
) : "";
1050 if ( encoding
& UNWIND_HAS_LSDA
) {
1051 // verify there is a corresponding entry in lsda table
1053 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
1054 if ( lindex
[k
].functionOffset() == funcOff
) {
1060 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
1063 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
1064 j
, funcOff
, encodingIndex
, encoding
, encodingString
, name
);
1068 fprintf(stderr
, "\t\tbad page header\n");
1074 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
, bool showFunctionNames
)
1076 struct stat stat_buf
;
1079 int fd
= ::open(path
, O_RDONLY
, 0);
1081 throw "cannot open file";
1082 if ( ::fstat(fd
, &stat_buf
) != 0 )
1083 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1084 uint32_t length
= stat_buf
.st_size
;
1085 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1086 if ( p
== ((uint8_t*)(-1)) )
1087 throw "cannot map file";
1089 const mach_header
* mh
= (mach_header
*)p
;
1090 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1091 const struct fat_header
* fh
= (struct fat_header
*)p
;
1092 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1093 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1094 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1095 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1096 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1097 if ( onlyArchs
.count(cputype
) ) {
1100 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
1101 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1103 throw "in universal file, i386 slice does not contain i386 mach-o";
1105 case CPU_TYPE_X86_64
:
1106 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
1107 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1109 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1111 #if SUPPORT_ARCH_arm64
1112 case CPU_TYPE_ARM64
:
1113 if ( UnwindPrinter
<arm64
>::validFile(p
+ offset
) )
1114 UnwindPrinter
<arm64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1116 throw "in universal file, arm64 slice does not contain arm64 mach-o";
1120 if ( UnwindPrinter
<arm
>::validFile(p
+ offset
) )
1121 UnwindPrinter
<arm
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1123 throw "in universal file, arm slice does not contain arm mach-o";
1126 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1131 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
1132 UnwindPrinter
<x86
>::make(p
, length
, path
, showFunctionNames
);
1134 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
1135 UnwindPrinter
<x86_64
>::make(p
, length
, path
, showFunctionNames
);
1137 #if SUPPORT_ARCH_arm64
1138 else if ( UnwindPrinter
<arm64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM64
) ) {
1139 UnwindPrinter
<arm64
>::make(p
, length
, path
, showFunctionNames
);
1142 else if ( UnwindPrinter
<arm
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM
) ) {
1143 UnwindPrinter
<arm
>::make(p
, length
, path
, showFunctionNames
);
1146 throw "not a known file type";
1149 catch (const char* msg
) {
1150 throwf("%s in %s", msg
, path
);
1155 int main(int argc
, const char* argv
[])
1157 std::set
<cpu_type_t
> onlyArchs
;
1158 std::vector
<const char*> files
;
1159 bool showFunctionNames
= true;
1162 for(int i
=1; i
< argc
; ++i
) {
1163 const char* arg
= argv
[i
];
1164 if ( arg
[0] == '-' ) {
1165 if ( strcmp(arg
, "-arch") == 0 ) {
1166 const char* arch
= argv
[++i
];
1167 if ( strcmp(arch
, "i386") == 0 )
1168 onlyArchs
.insert(CPU_TYPE_I386
);
1169 else if ( strcmp(arch
, "x86_64") == 0 )
1170 onlyArchs
.insert(CPU_TYPE_X86_64
);
1171 #if SUPPORT_ARCH_arm64
1172 else if ( strcmp(arch
, "arm64") == 0 )
1173 onlyArchs
.insert(CPU_TYPE_ARM64
);
1175 else if ( strcmp(arch
, "armv7k") == 0 )
1176 onlyArchs
.insert(CPU_TYPE_ARM
);
1178 throwf("unknown architecture %s", arch
);
1180 else if ( strcmp(arg
, "-no_symbols") == 0 ) {
1181 showFunctionNames
= false;
1184 throwf("unknown option: %s\n", arg
);
1188 files
.push_back(arg
);
1192 // use all architectures if no restrictions specified
1193 if ( onlyArchs
.size() == 0 ) {
1194 onlyArchs
.insert(CPU_TYPE_I386
);
1195 onlyArchs
.insert(CPU_TYPE_X86_64
);
1196 #if SUPPORT_ARCH_arm64
1197 onlyArchs
.insert(CPU_TYPE_ARM64
);
1199 onlyArchs
.insert(CPU_TYPE_ARM
);
1202 // process each file
1203 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
1204 dump(*it
, onlyArchs
, showFunctionNames
);
1208 catch (const char* msg
) {
1209 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);