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()) {
168 bool UnwindPrinter
<arm
>::validFile(const uint8_t* fileContent
)
170 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
171 if ( header
->magic() != MH_MAGIC
)
173 if ( header
->cputype() != CPU_TYPE_ARM
)
175 switch (header
->filetype()) {
186 template <typename A
>
187 UnwindPrinter
<A
>::UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, bool showFunctionNames
)
188 : fHeader(NULL
), fLength(fileLength
), fUnwindSection(NULL
),
189 fStrings(NULL
), fStringsEnd(NULL
), fSymbols(NULL
), fSymbolCount(0), fMachHeaderAddress(0)
192 if ( ! validFile(fileContent
) )
193 throw "not a mach-o file that can be checked";
195 fPath
= strdup(path
);
196 fHeader
= (const macho_header
<P
>*)fileContent
;
198 getSymbolTableInfo();
200 if ( findUnwindSection() ) {
201 if ( fHeader
->filetype() == MH_OBJECT
)
202 printObjectUnwindSection(showFunctionNames
);
204 printUnwindSection(showFunctionNames
);
209 template <typename A
>
210 void UnwindPrinter
<A
>::getSymbolTableInfo()
212 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
213 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
214 const uint32_t cmd_count
= fHeader
->ncmds();
215 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
216 const macho_load_command
<P
>* cmd
= cmds
;
217 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
218 uint32_t size
= cmd
->cmdsize();
219 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
220 if ( endOfCmd
> endOfLoadCommands
)
221 throwf("load command #%d extends beyond the end of the load commands", i
);
222 if ( endOfCmd
> endOfFile
)
223 throwf("load command #%d extends beyond the end of the file", i
);
224 if ( cmd
->cmd() == LC_SYMTAB
) {
225 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
226 fSymbolCount
= symtab
->nsyms();
227 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
228 fStrings
= (char*)fHeader
+ symtab
->stroff();
229 fStringsEnd
= fStrings
+ symtab
->strsize();
231 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
235 template <typename A
>
236 const char* UnwindPrinter
<A
>::functionName(pint_t addr
, uint32_t* offset
)
238 const macho_nlist
<P
>* closestSymbol
= NULL
;
239 if ( offset
!= NULL
)
241 for (uint32_t i
=0; i
< fSymbolCount
; ++i
) {
242 uint8_t type
= fSymbols
[i
].n_type();
243 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
244 pint_t value
= fSymbols
[i
].n_value();
245 if ( value
== addr
) {
246 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
249 if ( fSymbols
[i
].n_desc() & N_ARM_THUMB_DEF
)
251 if ( value
== addr
) {
252 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
253 //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);
256 else if ( offset
!= NULL
) {
257 if ( closestSymbol
== NULL
) {
258 if ( fSymbols
[i
].n_value() < addr
)
259 closestSymbol
= &fSymbols
[i
];
262 if ( (fSymbols
[i
].n_value() < addr
) && (fSymbols
[i
].n_value() > closestSymbol
->n_value()) )
263 closestSymbol
= &fSymbols
[i
];
268 if ( closestSymbol
!= NULL
) {
269 *offset
= addr
- closestSymbol
->n_value();
270 return &fStrings
[closestSymbol
->n_strx()];
272 return "--anonymous function--";
277 template <typename A
>
278 bool UnwindPrinter
<A
>::findUnwindSection()
280 const char* unwindSectionName
= "__unwind_info";
281 const char* unwindSegmentName
= "__TEXT";
282 if ( fHeader
->filetype() == MH_OBJECT
) {
283 unwindSectionName
= "__compact_unwind";
284 unwindSegmentName
= "__LD";
286 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
287 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
288 const uint32_t cmd_count
= fHeader
->ncmds();
289 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
290 const macho_load_command
<P
>* cmd
= cmds
;
291 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
292 uint32_t size
= cmd
->cmdsize();
293 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
294 if ( endOfCmd
> endOfLoadCommands
)
295 throwf("load command #%d extends beyond the end of the load commands", i
);
296 if ( endOfCmd
> endOfFile
)
297 throwf("load command #%d extends beyond the end of the file", i
);
298 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
299 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
300 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
301 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
302 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
303 if ( (strncmp(sect
->sectname(), unwindSectionName
, 16) == 0) && (strcmp(sect
->segname(), unwindSegmentName
) == 0) ) {
304 fUnwindSection
= sect
;
305 fMachHeaderAddress
= segCmd
->vmaddr();
306 return fUnwindSection
;
310 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
315 #define EXTRACT_BITS(value, mask) \
316 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
320 void UnwindPrinter
<x86_64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
323 switch ( encoding
& UNWIND_X86_64_MODE_MASK
) {
324 case UNWIND_X86_64_MODE_RBP_FRAME
:
326 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
327 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
328 if ( savedRegistersLocations
== 0 ) {
329 strcpy(str
, "rbp frame, no saved registers");
332 sprintf(str
, "rbp frame, at -%d:", savedRegistersOffset
*8);
333 bool needComma
= false;
334 for (int i
=0; i
< 5; ++i
) {
339 switch (savedRegistersLocations
& 0x7) {
340 case UNWIND_X86_64_REG_NONE
:
343 case UNWIND_X86_64_REG_RBX
:
346 case UNWIND_X86_64_REG_R12
:
349 case UNWIND_X86_64_REG_R13
:
352 case UNWIND_X86_64_REG_R14
:
355 case UNWIND_X86_64_REG_R15
:
361 savedRegistersLocations
= (savedRegistersLocations
>> 3);
362 if ( savedRegistersLocations
== 0 )
368 case UNWIND_X86_64_MODE_STACK_IMMD
:
369 case UNWIND_X86_64_MODE_STACK_IND
:
371 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
372 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
373 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
374 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
375 if ( (encoding
& UNWIND_X86_64_MODE_MASK
) == UNWIND_X86_64_MODE_STACK_IND
) {
376 // stack size is encoded in subl $xxx,%esp instruction
377 uint32_t subl
= x86_64::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
378 sprintf(str
, "stack size=0x%08X, ", subl
+ 8*stackAdjust
);
381 sprintf(str
, "stack size=%d, ", stackSize
*8);
383 if ( regCount
== 0 ) {
384 strcat(str
, "no registers saved");
388 switch ( regCount
) {
390 permunreg
[0] = permutation
/120;
391 permutation
-= (permunreg
[0]*120);
392 permunreg
[1] = permutation
/24;
393 permutation
-= (permunreg
[1]*24);
394 permunreg
[2] = permutation
/6;
395 permutation
-= (permunreg
[2]*6);
396 permunreg
[3] = permutation
/2;
397 permutation
-= (permunreg
[3]*2);
398 permunreg
[4] = permutation
;
402 permunreg
[0] = permutation
/120;
403 permutation
-= (permunreg
[0]*120);
404 permunreg
[1] = permutation
/24;
405 permutation
-= (permunreg
[1]*24);
406 permunreg
[2] = permutation
/6;
407 permutation
-= (permunreg
[2]*6);
408 permunreg
[3] = permutation
/2;
409 permutation
-= (permunreg
[3]*2);
410 permunreg
[4] = permutation
;
413 permunreg
[0] = permutation
/60;
414 permutation
-= (permunreg
[0]*60);
415 permunreg
[1] = permutation
/12;
416 permutation
-= (permunreg
[1]*12);
417 permunreg
[2] = permutation
/3;
418 permutation
-= (permunreg
[2]*3);
419 permunreg
[3] = permutation
;
422 permunreg
[0] = permutation
/20;
423 permutation
-= (permunreg
[0]*20);
424 permunreg
[1] = permutation
/4;
425 permutation
-= (permunreg
[1]*4);
426 permunreg
[2] = permutation
;
429 permunreg
[0] = permutation
/5;
430 permutation
-= (permunreg
[0]*5);
431 permunreg
[1] = permutation
;
434 permunreg
[0] = permutation
;
437 // renumber registers back to standard numbers
439 bool used
[7] = { false, false, false, false, false, false, false };
440 for (int i
=0; i
< regCount
; ++i
) {
442 for (int u
=1; u
< 7; ++u
) {
444 if ( renum
== permunreg
[i
] ) {
453 bool needComma
= false;
454 for (int i
=0; i
< regCount
; ++i
) {
459 switch ( registers
[i
] ) {
460 case UNWIND_X86_64_REG_RBX
:
463 case UNWIND_X86_64_REG_R12
:
466 case UNWIND_X86_64_REG_R13
:
469 case UNWIND_X86_64_REG_R14
:
472 case UNWIND_X86_64_REG_R15
:
475 case UNWIND_X86_64_REG_RBP
:
485 case UNWIND_X86_64_MODE_DWARF
:
486 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
490 strcat(str
, "no unwind information");
494 if ( encoding
& UNWIND_HAS_LSDA
) {
495 strcat(str
, " LSDA");
501 void UnwindPrinter
<x86
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
504 switch ( encoding
& UNWIND_X86_MODE_MASK
) {
505 case UNWIND_X86_MODE_EBP_FRAME
:
507 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
508 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
509 if ( savedRegistersLocations
== 0 ) {
510 strcpy(str
, "ebp frame, no saved registers");
513 sprintf(str
, "ebp frame, at -%d:", savedRegistersOffset
*4);
514 bool needComma
= false;
515 for (int i
=0; i
< 5; ++i
) {
520 switch (savedRegistersLocations
& 0x7) {
521 case UNWIND_X86_REG_NONE
:
524 case UNWIND_X86_REG_EBX
:
527 case UNWIND_X86_REG_ECX
:
530 case UNWIND_X86_REG_EDX
:
533 case UNWIND_X86_REG_EDI
:
536 case UNWIND_X86_REG_ESI
:
542 savedRegistersLocations
= (savedRegistersLocations
>> 3);
543 if ( savedRegistersLocations
== 0 )
549 case UNWIND_X86_MODE_STACK_IMMD
:
550 case UNWIND_X86_MODE_STACK_IND
:
552 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
553 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
554 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
555 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
556 if ( (encoding
& UNWIND_X86_MODE_MASK
) == UNWIND_X86_MODE_STACK_IND
) {
557 // stack size is encoded in subl $xxx,%esp instruction
558 uint32_t subl
= x86::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
559 sprintf(str
, "stack size=0x%08X, ", subl
+4*stackAdjust
);
562 sprintf(str
, "stack size=%d, ", stackSize
*4);
564 if ( regCount
== 0 ) {
565 strcat(str
, "no saved regs");
569 switch ( regCount
) {
571 permunreg
[0] = permutation
/120;
572 permutation
-= (permunreg
[0]*120);
573 permunreg
[1] = permutation
/24;
574 permutation
-= (permunreg
[1]*24);
575 permunreg
[2] = permutation
/6;
576 permutation
-= (permunreg
[2]*6);
577 permunreg
[3] = permutation
/2;
578 permutation
-= (permunreg
[3]*2);
579 permunreg
[4] = permutation
;
583 permunreg
[0] = permutation
/120;
584 permutation
-= (permunreg
[0]*120);
585 permunreg
[1] = permutation
/24;
586 permutation
-= (permunreg
[1]*24);
587 permunreg
[2] = permutation
/6;
588 permutation
-= (permunreg
[2]*6);
589 permunreg
[3] = permutation
/2;
590 permutation
-= (permunreg
[3]*2);
591 permunreg
[4] = permutation
;
594 permunreg
[0] = permutation
/60;
595 permutation
-= (permunreg
[0]*60);
596 permunreg
[1] = permutation
/12;
597 permutation
-= (permunreg
[1]*12);
598 permunreg
[2] = permutation
/3;
599 permutation
-= (permunreg
[2]*3);
600 permunreg
[3] = permutation
;
603 permunreg
[0] = permutation
/20;
604 permutation
-= (permunreg
[0]*20);
605 permunreg
[1] = permutation
/4;
606 permutation
-= (permunreg
[1]*4);
607 permunreg
[2] = permutation
;
610 permunreg
[0] = permutation
/5;
611 permutation
-= (permunreg
[0]*5);
612 permunreg
[1] = permutation
;
615 permunreg
[0] = permutation
;
618 // renumber registers back to standard numbers
620 bool used
[7] = { false, false, false, false, false, false, false };
621 for (int i
=0; i
< regCount
; ++i
) {
623 for (int u
=1; u
< 7; ++u
) {
625 if ( renum
== permunreg
[i
] ) {
634 bool needComma
= false;
635 for (int i
=0; i
< regCount
; ++i
) {
640 switch ( registers
[i
] ) {
641 case UNWIND_X86_REG_EBX
:
644 case UNWIND_X86_REG_ECX
:
647 case UNWIND_X86_REG_EDX
:
650 case UNWIND_X86_REG_EDI
:
653 case UNWIND_X86_REG_ESI
:
656 case UNWIND_X86_REG_EBP
:
666 case UNWIND_X86_MODE_DWARF
:
667 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
);
671 strcat(str
, "no unwind information");
675 if ( encoding
& UNWIND_HAS_LSDA
) {
676 strcat(str
, " LSDA");
681 #if SUPPORT_ARCH_arm64
683 void UnwindPrinter
<arm64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
686 switch ( encoding
& UNWIND_ARM64_MODE_MASK
) {
687 case UNWIND_ARM64_MODE_FRAMELESS
:
688 stackSize
= EXTRACT_BITS(encoding
, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
);
689 if ( stackSize
== 0 )
690 strcpy(str
, "no frame, no saved registers ");
692 sprintf(str
, "stack size=%d: ", 16 * stackSize
);
693 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
694 strcat(str
, "x19/20 ");
695 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
696 strcat(str
, "x21/22 ");
697 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
698 strcat(str
, "x23/24 ");
699 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
700 strcat(str
, "x25/26 ");
701 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
702 strcat(str
, "x27/28 ");
703 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
704 strcat(str
, "d8/9 ");
705 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
706 strcat(str
, "d10/11 ");
707 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
708 strcat(str
, "d12/13 ");
709 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
710 strcat(str
, "d14/15 ");
713 case UNWIND_ARM64_MODE_DWARF
:
714 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
716 case UNWIND_ARM64_MODE_FRAME
:
717 strcpy(str
, "std frame: ");
718 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
719 strcat(str
, "x19/20 ");
720 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
721 strcat(str
, "x21/22 ");
722 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
723 strcat(str
, "x23/24 ");
724 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
725 strcat(str
, "x25/26 ");
726 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
727 strcat(str
, "x27/28 ");
728 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
729 strcat(str
, "d8/9 ");
730 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
731 strcat(str
, "d10/11 ");
732 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
733 strcat(str
, "d12/13 ");
734 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
735 strcat(str
, "d14/15 ");
737 case UNWIND_ARM64_MODE_FRAME_OLD
:
738 strcpy(str
, "old frame: ");
739 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD
)
740 strcat(str
, "x21/22 ");
741 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD
)
742 strcat(str
, "x23/24 ");
743 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD
)
744 strcat(str
, "x25/26 ");
745 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD
)
746 strcat(str
, "x27/28 ");
747 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD
)
748 strcat(str
, "d8/9 ");
749 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD
)
750 strcat(str
, "d10/11 ");
751 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD
)
752 strcat(str
, "d12/13 ");
753 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD
)
754 strcat(str
, "d14/15 ");
762 void UnwindPrinter
<arm
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
765 switch ( encoding
& UNWIND_ARM_MODE_MASK
) {
766 case UNWIND_ARM_MODE_DWARF
:
767 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_ARM_DWARF_SECTION_OFFSET
);
769 case UNWIND_ARM_MODE_FRAME
:
770 case UNWIND_ARM_MODE_FRAME_D
:
771 switch ( encoding
& UNWIND_ARM_FRAME_STACK_ADJUST_MASK
) {
773 strcpy(str
, "std frame: ");
776 strcat(str
, "std frame(sp adj 4): ");
779 strcat(str
, "std frame(sp adj 8): ");
782 strcat(str
, "std frame(sp adj 12): ");
785 if ( encoding
& UNWIND_ARM_FRAME_FIRST_PUSH_R4
)
787 if ( encoding
& UNWIND_ARM_FRAME_FIRST_PUSH_R5
)
789 if ( encoding
& UNWIND_ARM_FRAME_FIRST_PUSH_R6
)
792 if ( encoding
& 0x000000F8)
794 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R8
)
796 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R9
)
798 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R10
)
800 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R11
)
802 if ( encoding
& UNWIND_ARM_FRAME_SECOND_PUSH_R12
)
805 if ( (encoding
& UNWIND_ARM_MODE_MASK
) == UNWIND_ARM_MODE_FRAME_D
) {
806 switch ( encoding
& UNWIND_ARM_FRAME_D_REG_COUNT_MASK
) {
808 strcat(str
, " / d8 ");
811 strcat(str
, " / d8,d10 ");
814 strcat(str
, " / d8,d10,d12 ");
817 strcat(str
, " / d8,d10,d12,d14 ");
820 strcat(str
, " / d12,d14 / d8,d9,d10 ");
823 strcat(str
, " / d14 / d8,d9,d10,d11,d12");
826 strcat(str
, " / d8,d9,d10,d11,d12,d13,d14 ");
829 strcat(str
, " / d8,d9,d10,d11,d12,d13,d14 ");
832 strcat(str
, " / unknown D register usage ");
840 strcpy(str
, "no unwind information");
842 strcpy(str
, "unsupported compact unwind");
849 const char* UnwindPrinter
<x86_64
>::personalityName(const macho_relocation_info
<x86_64::P
>* reloc
)
851 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
852 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
853 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
854 return &fStrings
[sym
.n_strx()];
858 const char* UnwindPrinter
<x86
>::personalityName(const macho_relocation_info
<x86::P
>* reloc
)
860 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
861 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
862 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
863 return &fStrings
[sym
.n_strx()];
866 #if SUPPORT_ARCH_arm64
868 const char* UnwindPrinter
<arm64
>::personalityName(const macho_relocation_info
<arm64::P
>* reloc
)
870 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
871 //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
872 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
873 return &fStrings
[sym
.n_strx()];
879 const char* UnwindPrinter
<arm
>::personalityName(const macho_relocation_info
<arm::P
>* reloc
)
881 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
882 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
883 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
884 return &fStrings
[sym
.n_strx()];
887 template <typename A
>
888 bool UnwindPrinter
<A
>::hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
)
890 const macho_relocation_info
<P
>* relocs
= (macho_relocation_info
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->reloff());
891 const macho_relocation_info
<P
>* relocsEnd
= &relocs
[fUnwindSection
->nreloc()];
892 for (const macho_relocation_info
<P
>* reloc
= relocs
; reloc
< relocsEnd
; ++reloc
) {
893 if ( reloc
->r_extern() && (reloc
->r_address() == sectionOffset
) ) {
894 *personalityStr
= this->personalityName(reloc
);
896 *addr
= fSymbols
[reloc
->r_symbolnum()].n_value();
904 template <typename A
>
905 void UnwindPrinter
<A
>::printObjectUnwindSection(bool showFunctionNames
)
907 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
908 archName(), fUnwindSection
->size(), fUnwindSection
->size() / sizeof(macho_compact_unwind_entry
<P
>));
910 const macho_compact_unwind_entry
<P
>* const entriesStart
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset());
911 const macho_compact_unwind_entry
<P
>* const entriesEnd
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset() + fUnwindSection
->size());
912 for (const macho_compact_unwind_entry
<P
>* entry
=entriesStart
; entry
< entriesEnd
; ++entry
) {
913 uint64_t entryAddress
= ((char*)entry
- (char*)entriesStart
) + fUnwindSection
->addr();
914 printf("0x%08llX:\n", entryAddress
);
915 const char* functionNameStr
;
917 uint32_t offsetInFunction
;
918 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::codeStartFieldOffset(), &functionNameStr
, &funcAddress
) ) {
919 offsetInFunction
= entry
->codeStart();
922 functionNameStr
= this->functionName(entry
->codeStart(), &offsetInFunction
);
923 funcAddress
= entry
->codeStart();
925 if ( offsetInFunction
== 0 )
926 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress
, functionNameStr
);
928 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress
+offsetInFunction
, functionNameStr
, offsetInFunction
);
930 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress
+offsetInFunction
+entry
->codeLen()), entry
->codeLen());
932 char encodingString
[200];
933 this->decode(entry
->compactUnwindInfo(), ((const uint8_t*)fHeader
), encodingString
);
934 printf(" unwind info: 0x%08X %s\n", entry
->compactUnwindInfo(), encodingString
);
936 const char* personalityNameStr
;
937 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::personalityFieldOffset(), &personalityNameStr
) ) {
938 printf(" personality: %s\n", personalityNameStr
);
941 printf(" personality:\n");
943 if ( entry
->lsda() == 0 ) {
948 const char* lsdaName
= this->functionName(entry
->lsda(), &lsdaOffset
);
949 if ( lsdaOffset
== 0 )
950 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry
->lsda(), lsdaName
);
952 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry
->lsda(), lsdaName
, lsdaOffset
);
959 template <typename A
>
960 void UnwindPrinter
<A
>::printUnwindSection(bool showFunctionNames
)
962 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
963 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
965 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
966 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
967 printf("\tversion=0x%08X\n", sectionHeader
->version());
968 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
969 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
970 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
971 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
972 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
973 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
974 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
975 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
976 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
977 printf("\t\tencoding[%3u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
979 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
980 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
981 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
982 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
984 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
985 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
986 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
987 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
988 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
990 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
991 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
992 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
993 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
994 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
995 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
996 const char* name
= showFunctionNames
? functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
) : "";
997 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), name
);
998 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
999 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
1001 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
1002 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
1003 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
1004 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
1005 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
1006 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
1007 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
1008 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
1009 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
1010 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
1011 uint32_t funcOffset
= entry
[j
].functionOffset();
1012 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
1013 // verify there is a corresponding entry in lsda table
1015 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
1016 if ( lindex
[k
].functionOffset() == funcOffset
) {
1022 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
1025 char encodingString
[100];
1026 decode(entry
[j
].encoding(), ((const uint8_t*)fHeader
)+funcOffset
, encodingString
);
1027 const char* name
= showFunctionNames
? functionName(funcOffset
+fMachHeaderAddress
) : "";
1028 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
1029 j
, funcOffset
, entry
[j
].encoding(), encodingString
, name
);
1032 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
1033 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
1034 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
1035 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
1036 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
1037 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
1038 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
1039 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
1040 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
1041 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
1042 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
1043 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
1045 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
1046 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
1048 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
1049 char encodingString
[100];
1050 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
1051 decode(encoding
, ((const uint8_t*)fHeader
)+funcOff
, encodingString
);
1052 const char* name
= showFunctionNames
? functionName(funcOff
+fMachHeaderAddress
) : "";
1053 if ( encoding
& UNWIND_HAS_LSDA
) {
1054 // verify there is a corresponding entry in lsda table
1056 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
1057 if ( lindex
[k
].functionOffset() == funcOff
) {
1063 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
1066 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
1067 j
, funcOff
, encodingIndex
, encoding
, encodingString
, name
);
1071 fprintf(stderr
, "\t\tbad page header\n");
1077 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
, bool showFunctionNames
)
1079 struct stat stat_buf
;
1082 int fd
= ::open(path
, O_RDONLY
, 0);
1084 throw "cannot open file";
1085 if ( ::fstat(fd
, &stat_buf
) != 0 )
1086 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1087 uint32_t length
= stat_buf
.st_size
;
1088 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1089 if ( p
== ((uint8_t*)(-1)) )
1090 throw "cannot map file";
1092 const mach_header
* mh
= (mach_header
*)p
;
1093 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1094 const struct fat_header
* fh
= (struct fat_header
*)p
;
1095 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1096 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1097 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1098 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1099 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1100 if ( onlyArchs
.count(cputype
) ) {
1103 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
1104 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1106 throw "in universal file, i386 slice does not contain i386 mach-o";
1108 case CPU_TYPE_X86_64
:
1109 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
1110 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1112 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1114 #if SUPPORT_ARCH_arm64
1115 case CPU_TYPE_ARM64
:
1116 if ( UnwindPrinter
<arm64
>::validFile(p
+ offset
) )
1117 UnwindPrinter
<arm64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1119 throw "in universal file, arm64 slice does not contain arm64 mach-o";
1123 if ( UnwindPrinter
<arm
>::validFile(p
+ offset
) )
1124 UnwindPrinter
<arm
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1126 throw "in universal file, arm slice does not contain arm mach-o";
1129 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1134 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
1135 UnwindPrinter
<x86
>::make(p
, length
, path
, showFunctionNames
);
1137 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
1138 UnwindPrinter
<x86_64
>::make(p
, length
, path
, showFunctionNames
);
1140 #if SUPPORT_ARCH_arm64
1141 else if ( UnwindPrinter
<arm64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM64
) ) {
1142 UnwindPrinter
<arm64
>::make(p
, length
, path
, showFunctionNames
);
1145 else if ( UnwindPrinter
<arm
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM
) ) {
1146 UnwindPrinter
<arm
>::make(p
, length
, path
, showFunctionNames
);
1149 throw "not a known file type";
1152 catch (const char* msg
) {
1153 throwf("%s in %s", msg
, path
);
1158 int main(int argc
, const char* argv
[])
1160 std::set
<cpu_type_t
> onlyArchs
;
1161 std::vector
<const char*> files
;
1162 bool showFunctionNames
= true;
1165 for(int i
=1; i
< argc
; ++i
) {
1166 const char* arg
= argv
[i
];
1167 if ( arg
[0] == '-' ) {
1168 if ( strcmp(arg
, "-arch") == 0 ) {
1169 const char* arch
= argv
[++i
];
1170 if ( strcmp(arch
, "i386") == 0 )
1171 onlyArchs
.insert(CPU_TYPE_I386
);
1172 else if ( strcmp(arch
, "x86_64") == 0 )
1173 onlyArchs
.insert(CPU_TYPE_X86_64
);
1174 #if SUPPORT_ARCH_arm64
1175 else if ( strcmp(arch
, "arm64") == 0 )
1176 onlyArchs
.insert(CPU_TYPE_ARM64
);
1178 else if ( strcmp(arch
, "armv7k") == 0 )
1179 onlyArchs
.insert(CPU_TYPE_ARM
);
1181 throwf("unknown architecture %s", arch
);
1183 else if ( strcmp(arg
, "-no_symbols") == 0 ) {
1184 showFunctionNames
= false;
1187 throwf("unknown option: %s\n", arg
);
1191 files
.push_back(arg
);
1195 // use all architectures if no restrictions specified
1196 if ( onlyArchs
.size() == 0 ) {
1197 onlyArchs
.insert(CPU_TYPE_I386
);
1198 onlyArchs
.insert(CPU_TYPE_X86_64
);
1199 #if SUPPORT_ARCH_arm64
1200 onlyArchs
.insert(CPU_TYPE_ARM64
);
1202 onlyArchs
.insert(CPU_TYPE_ARM
);
1205 // process each file
1206 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
1207 dump(*it
, onlyArchs
, showFunctionNames
);
1211 catch (const char* msg
) {
1212 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);