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 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 pint_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 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
292 if ( endOfCmd
> endOfLoadCommands
)
293 throwf("load command #%d extends beyond the end of the load commands", i
);
294 if ( endOfCmd
> endOfFile
)
295 throwf("load command #%d extends beyond the end of the file", i
);
296 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
297 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
298 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
299 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
300 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
301 if ( (strncmp(sect
->sectname(), unwindSectionName
, 16) == 0) && (strcmp(sect
->segname(), unwindSegmentName
) == 0) ) {
302 fUnwindSection
= sect
;
303 fMachHeaderAddress
= segCmd
->vmaddr();
304 return fUnwindSection
;
308 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
313 #define EXTRACT_BITS(value, mask) \
314 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
318 void UnwindPrinter
<x86_64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
321 switch ( encoding
& UNWIND_X86_64_MODE_MASK
) {
322 case UNWIND_X86_64_MODE_RBP_FRAME
:
324 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
325 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
326 if ( savedRegistersLocations
== 0 ) {
327 strcpy(str
, "rbp frame, no saved registers");
330 sprintf(str
, "rbp frame, at -%d:", savedRegistersOffset
*8);
331 bool needComma
= false;
332 for (int i
=0; i
< 5; ++i
) {
337 switch (savedRegistersLocations
& 0x7) {
338 case UNWIND_X86_64_REG_NONE
:
341 case UNWIND_X86_64_REG_RBX
:
344 case UNWIND_X86_64_REG_R12
:
347 case UNWIND_X86_64_REG_R13
:
350 case UNWIND_X86_64_REG_R14
:
353 case UNWIND_X86_64_REG_R15
:
359 savedRegistersLocations
= (savedRegistersLocations
>> 3);
360 if ( savedRegistersLocations
== 0 )
366 case UNWIND_X86_64_MODE_STACK_IMMD
:
367 case UNWIND_X86_64_MODE_STACK_IND
:
369 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
370 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
371 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
372 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
373 if ( (encoding
& UNWIND_X86_64_MODE_MASK
) == UNWIND_X86_64_MODE_STACK_IND
) {
374 // stack size is encoded in subl $xxx,%esp instruction
375 uint32_t subl
= x86_64::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
376 sprintf(str
, "stack size=0x%08X, ", subl
+ 8*stackAdjust
);
379 sprintf(str
, "stack size=%d, ", stackSize
*8);
381 if ( regCount
== 0 ) {
382 strcat(str
, "no registers saved");
386 switch ( regCount
) {
388 permunreg
[0] = permutation
/120;
389 permutation
-= (permunreg
[0]*120);
390 permunreg
[1] = permutation
/24;
391 permutation
-= (permunreg
[1]*24);
392 permunreg
[2] = permutation
/6;
393 permutation
-= (permunreg
[2]*6);
394 permunreg
[3] = permutation
/2;
395 permutation
-= (permunreg
[3]*2);
396 permunreg
[4] = permutation
;
400 permunreg
[0] = permutation
/120;
401 permutation
-= (permunreg
[0]*120);
402 permunreg
[1] = permutation
/24;
403 permutation
-= (permunreg
[1]*24);
404 permunreg
[2] = permutation
/6;
405 permutation
-= (permunreg
[2]*6);
406 permunreg
[3] = permutation
/2;
407 permutation
-= (permunreg
[3]*2);
408 permunreg
[4] = permutation
;
411 permunreg
[0] = permutation
/60;
412 permutation
-= (permunreg
[0]*60);
413 permunreg
[1] = permutation
/12;
414 permutation
-= (permunreg
[1]*12);
415 permunreg
[2] = permutation
/3;
416 permutation
-= (permunreg
[2]*3);
417 permunreg
[3] = permutation
;
420 permunreg
[0] = permutation
/20;
421 permutation
-= (permunreg
[0]*20);
422 permunreg
[1] = permutation
/4;
423 permutation
-= (permunreg
[1]*4);
424 permunreg
[2] = permutation
;
427 permunreg
[0] = permutation
/5;
428 permutation
-= (permunreg
[0]*5);
429 permunreg
[1] = permutation
;
432 permunreg
[0] = permutation
;
435 // renumber registers back to standard numbers
437 bool used
[7] = { false, false, false, false, false, false, false };
438 for (int i
=0; i
< regCount
; ++i
) {
440 for (int u
=1; u
< 7; ++u
) {
442 if ( renum
== permunreg
[i
] ) {
451 bool needComma
= false;
452 for (int i
=0; i
< regCount
; ++i
) {
457 switch ( registers
[i
] ) {
458 case UNWIND_X86_64_REG_RBX
:
461 case UNWIND_X86_64_REG_R12
:
464 case UNWIND_X86_64_REG_R13
:
467 case UNWIND_X86_64_REG_R14
:
470 case UNWIND_X86_64_REG_R15
:
473 case UNWIND_X86_64_REG_RBP
:
483 case UNWIND_X86_64_MODE_DWARF
:
484 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
488 strcat(str
, "no unwind information");
492 if ( encoding
& UNWIND_HAS_LSDA
) {
493 strcat(str
, " LSDA");
499 void UnwindPrinter
<x86
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
502 switch ( encoding
& UNWIND_X86_MODE_MASK
) {
503 case UNWIND_X86_MODE_EBP_FRAME
:
505 uint32_t savedRegistersOffset
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
506 uint32_t savedRegistersLocations
= EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
507 if ( savedRegistersLocations
== 0 ) {
508 strcpy(str
, "ebp frame, no saved registers");
511 sprintf(str
, "ebp frame, at -%d:", savedRegistersOffset
*4);
512 bool needComma
= false;
513 for (int i
=0; i
< 5; ++i
) {
518 switch (savedRegistersLocations
& 0x7) {
519 case UNWIND_X86_REG_NONE
:
522 case UNWIND_X86_REG_EBX
:
525 case UNWIND_X86_REG_ECX
:
528 case UNWIND_X86_REG_EDX
:
531 case UNWIND_X86_REG_EDI
:
534 case UNWIND_X86_REG_ESI
:
540 savedRegistersLocations
= (savedRegistersLocations
>> 3);
541 if ( savedRegistersLocations
== 0 )
547 case UNWIND_X86_MODE_STACK_IMMD
:
548 case UNWIND_X86_MODE_STACK_IND
:
550 uint32_t stackSize
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
551 uint32_t stackAdjust
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
552 uint32_t regCount
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
553 uint32_t permutation
= EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
554 if ( (encoding
& UNWIND_X86_MODE_MASK
) == UNWIND_X86_MODE_STACK_IND
) {
555 // stack size is encoded in subl $xxx,%esp instruction
556 uint32_t subl
= x86::P::E::get32(*((uint32_t*)(funcStart
+stackSize
)));
557 sprintf(str
, "stack size=0x%08X, ", subl
+4*stackAdjust
);
560 sprintf(str
, "stack size=%d, ", stackSize
*4);
562 if ( regCount
== 0 ) {
563 strcat(str
, "no saved regs");
567 switch ( regCount
) {
569 permunreg
[0] = permutation
/120;
570 permutation
-= (permunreg
[0]*120);
571 permunreg
[1] = permutation
/24;
572 permutation
-= (permunreg
[1]*24);
573 permunreg
[2] = permutation
/6;
574 permutation
-= (permunreg
[2]*6);
575 permunreg
[3] = permutation
/2;
576 permutation
-= (permunreg
[3]*2);
577 permunreg
[4] = permutation
;
581 permunreg
[0] = permutation
/120;
582 permutation
-= (permunreg
[0]*120);
583 permunreg
[1] = permutation
/24;
584 permutation
-= (permunreg
[1]*24);
585 permunreg
[2] = permutation
/6;
586 permutation
-= (permunreg
[2]*6);
587 permunreg
[3] = permutation
/2;
588 permutation
-= (permunreg
[3]*2);
589 permunreg
[4] = permutation
;
592 permunreg
[0] = permutation
/60;
593 permutation
-= (permunreg
[0]*60);
594 permunreg
[1] = permutation
/12;
595 permutation
-= (permunreg
[1]*12);
596 permunreg
[2] = permutation
/3;
597 permutation
-= (permunreg
[2]*3);
598 permunreg
[3] = permutation
;
601 permunreg
[0] = permutation
/20;
602 permutation
-= (permunreg
[0]*20);
603 permunreg
[1] = permutation
/4;
604 permutation
-= (permunreg
[1]*4);
605 permunreg
[2] = permutation
;
608 permunreg
[0] = permutation
/5;
609 permutation
-= (permunreg
[0]*5);
610 permunreg
[1] = permutation
;
613 permunreg
[0] = permutation
;
616 // renumber registers back to standard numbers
618 bool used
[7] = { false, false, false, false, false, false, false };
619 for (int i
=0; i
< regCount
; ++i
) {
621 for (int u
=1; u
< 7; ++u
) {
623 if ( renum
== permunreg
[i
] ) {
632 bool needComma
= false;
633 for (int i
=0; i
< regCount
; ++i
) {
638 switch ( registers
[i
] ) {
639 case UNWIND_X86_REG_EBX
:
642 case UNWIND_X86_REG_ECX
:
645 case UNWIND_X86_REG_EDX
:
648 case UNWIND_X86_REG_EDI
:
651 case UNWIND_X86_REG_ESI
:
654 case UNWIND_X86_REG_EBP
:
664 case UNWIND_X86_MODE_DWARF
:
665 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
);
669 strcat(str
, "no unwind information");
673 if ( encoding
& UNWIND_HAS_LSDA
) {
674 strcat(str
, " LSDA");
679 #if SUPPORT_ARCH_arm64
681 void UnwindPrinter
<arm64
>::decode(uint32_t encoding
, const uint8_t* funcStart
, char* str
)
684 switch ( encoding
& UNWIND_ARM64_MODE_MASK
) {
685 case UNWIND_ARM64_MODE_FRAMELESS
:
686 stackSize
= EXTRACT_BITS(encoding
, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
);
687 if ( stackSize
== 0 )
688 strcpy(str
, "no frame, no saved registers ");
690 sprintf(str
, "stack size=%d: ", 16 * stackSize
);
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 ");
711 case UNWIND_ARM64_MODE_DWARF
:
712 sprintf(str
, "dwarf offset 0x%08X, ", encoding
& UNWIND_X86_64_DWARF_SECTION_OFFSET
);
714 case UNWIND_ARM64_MODE_FRAME
:
715 strcpy(str
, "std frame: ");
716 if ( encoding
& UNWIND_ARM64_FRAME_X19_X20_PAIR
)
717 strcat(str
, "x19/20 ");
718 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR
)
719 strcat(str
, "x21/22 ");
720 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR
)
721 strcat(str
, "x23/24 ");
722 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR
)
723 strcat(str
, "x25/26 ");
724 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR
)
725 strcat(str
, "x27/28 ");
726 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR
)
727 strcat(str
, "d8/9 ");
728 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR
)
729 strcat(str
, "d10/11 ");
730 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR
)
731 strcat(str
, "d12/13 ");
732 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR
)
733 strcat(str
, "d14/15 ");
735 case UNWIND_ARM64_MODE_FRAME_OLD
:
736 strcpy(str
, "old frame: ");
737 if ( encoding
& UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD
)
738 strcat(str
, "x21/22 ");
739 if ( encoding
& UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD
)
740 strcat(str
, "x23/24 ");
741 if ( encoding
& UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD
)
742 strcat(str
, "x25/26 ");
743 if ( encoding
& UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD
)
744 strcat(str
, "x27/28 ");
745 if ( encoding
& UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD
)
746 strcat(str
, "d8/9 ");
747 if ( encoding
& UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD
)
748 strcat(str
, "d10/11 ");
749 if ( encoding
& UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD
)
750 strcat(str
, "d12/13 ");
751 if ( encoding
& UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD
)
752 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()];
877 const char* UnwindPrinter
<arm
>::personalityName(const macho_relocation_info
<arm::P
>* reloc
)
879 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
880 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
881 const macho_nlist
<P
>& sym
= fSymbols
[reloc
->r_symbolnum()];
882 return &fStrings
[sym
.n_strx()];
885 template <typename A
>
886 bool UnwindPrinter
<A
>::hasExernReloc(uint64_t sectionOffset
, const char** personalityStr
, pint_t
* addr
)
888 const macho_relocation_info
<P
>* relocs
= (macho_relocation_info
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->reloff());
889 const macho_relocation_info
<P
>* relocsEnd
= &relocs
[fUnwindSection
->nreloc()];
890 for (const macho_relocation_info
<P
>* reloc
= relocs
; reloc
< relocsEnd
; ++reloc
) {
891 if ( reloc
->r_extern() && (reloc
->r_address() == sectionOffset
) ) {
892 *personalityStr
= this->personalityName(reloc
);
894 *addr
= fSymbols
[reloc
->r_symbolnum()].n_value();
902 template <typename A
>
903 void UnwindPrinter
<A
>::printObjectUnwindSection(bool showFunctionNames
)
905 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
906 archName(), fUnwindSection
->size(), fUnwindSection
->size() / sizeof(macho_compact_unwind_entry
<P
>));
908 const macho_compact_unwind_entry
<P
>* const entriesStart
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset());
909 const macho_compact_unwind_entry
<P
>* const entriesEnd
= (macho_compact_unwind_entry
<P
>*)((uint8_t*)fHeader
+ fUnwindSection
->offset() + fUnwindSection
->size());
910 for (const macho_compact_unwind_entry
<P
>* entry
=entriesStart
; entry
< entriesEnd
; ++entry
) {
911 uint64_t entryAddress
= ((char*)entry
- (char*)entriesStart
) + fUnwindSection
->addr();
912 printf("0x%08llX:\n", entryAddress
);
913 const char* functionNameStr
;
915 uint32_t offsetInFunction
;
916 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::codeStartFieldOffset(), &functionNameStr
, &funcAddress
) ) {
917 offsetInFunction
= entry
->codeStart();
920 functionNameStr
= this->functionName(entry
->codeStart(), &offsetInFunction
);
921 funcAddress
= entry
->codeStart();
923 if ( offsetInFunction
== 0 )
924 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress
, functionNameStr
);
926 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress
+offsetInFunction
, functionNameStr
, offsetInFunction
);
928 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress
+offsetInFunction
+entry
->codeLen()), entry
->codeLen());
930 char encodingString
[200];
931 this->decode(entry
->compactUnwindInfo(), ((const uint8_t*)fHeader
), encodingString
);
932 printf(" unwind info: 0x%08X %s\n", entry
->compactUnwindInfo(), encodingString
);
934 const char* personalityNameStr
;
935 if ( hasExernReloc(((char*)entry
-(char*)entriesStart
)+macho_compact_unwind_entry
<P
>::personalityFieldOffset(), &personalityNameStr
) ) {
936 printf(" personality: %s\n", personalityNameStr
);
939 printf(" personality:\n");
941 if ( entry
->lsda() == 0 ) {
946 const char* lsdaName
= this->functionName(entry
->lsda(), &lsdaOffset
);
947 if ( lsdaOffset
== 0 )
948 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry
->lsda(), lsdaName
);
950 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry
->lsda(), lsdaName
, lsdaOffset
);
957 template <typename A
>
958 void UnwindPrinter
<A
>::printUnwindSection(bool showFunctionNames
)
960 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
961 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
963 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
964 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
965 printf("\tversion=0x%08X\n", sectionHeader
->version());
966 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
967 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
968 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
969 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
970 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
971 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
972 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
973 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
974 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
975 printf("\t\tencoding[%3u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
977 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
978 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
979 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
980 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
982 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
983 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
984 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
985 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
986 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
988 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
989 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
990 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
991 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
992 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
993 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
994 const char* name
= showFunctionNames
? functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
) : "";
995 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), name
);
996 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
997 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
999 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
1000 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
1001 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
1002 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
1003 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
1004 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
1005 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
1006 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
1007 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
1008 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
1009 uint32_t funcOffset
= entry
[j
].functionOffset();
1010 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
1011 // verify there is a corresponding entry in lsda table
1013 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
1014 if ( lindex
[k
].functionOffset() == funcOffset
) {
1020 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
1023 char encodingString
[100];
1024 decode(entry
[j
].encoding(), ((const uint8_t*)fHeader
)+funcOffset
, encodingString
);
1025 const char* name
= showFunctionNames
? functionName(funcOffset
+fMachHeaderAddress
) : "";
1026 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
1027 j
, funcOffset
, entry
[j
].encoding(), encodingString
, name
);
1030 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
1031 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
1032 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
1033 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
1034 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
1035 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
1036 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
1037 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
1038 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
1039 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
1040 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
1041 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
1043 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
1044 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
1046 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
1047 char encodingString
[100];
1048 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
1049 decode(encoding
, ((const uint8_t*)fHeader
)+funcOff
, encodingString
);
1050 const char* name
= showFunctionNames
? functionName(funcOff
+fMachHeaderAddress
) : "";
1051 if ( encoding
& UNWIND_HAS_LSDA
) {
1052 // verify there is a corresponding entry in lsda table
1054 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
1055 if ( lindex
[k
].functionOffset() == funcOff
) {
1061 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
1064 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
1065 j
, funcOff
, encodingIndex
, encoding
, encodingString
, name
);
1069 fprintf(stderr
, "\t\tbad page header\n");
1075 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
, bool showFunctionNames
)
1077 struct stat stat_buf
;
1080 int fd
= ::open(path
, O_RDONLY
, 0);
1082 throw "cannot open file";
1083 if ( ::fstat(fd
, &stat_buf
) != 0 )
1084 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1085 uint32_t length
= stat_buf
.st_size
;
1086 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1087 if ( p
== ((uint8_t*)(-1)) )
1088 throw "cannot map file";
1090 const mach_header
* mh
= (mach_header
*)p
;
1091 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1092 const struct fat_header
* fh
= (struct fat_header
*)p
;
1093 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1094 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1095 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1096 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1097 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1098 if ( onlyArchs
.count(cputype
) ) {
1101 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
1102 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1104 throw "in universal file, i386 slice does not contain i386 mach-o";
1106 case CPU_TYPE_X86_64
:
1107 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
1108 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1110 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1112 #if SUPPORT_ARCH_arm64
1113 case CPU_TYPE_ARM64
:
1114 if ( UnwindPrinter
<arm64
>::validFile(p
+ offset
) )
1115 UnwindPrinter
<arm64
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1117 throw "in universal file, arm64 slice does not contain arm64 mach-o";
1121 if ( UnwindPrinter
<arm
>::validFile(p
+ offset
) )
1122 UnwindPrinter
<arm
>::make(p
+ offset
, size
, path
, showFunctionNames
);
1124 throw "in universal file, arm slice does not contain arm mach-o";
1127 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1132 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
1133 UnwindPrinter
<x86
>::make(p
, length
, path
, showFunctionNames
);
1135 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
1136 UnwindPrinter
<x86_64
>::make(p
, length
, path
, showFunctionNames
);
1138 #if SUPPORT_ARCH_arm64
1139 else if ( UnwindPrinter
<arm64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM64
) ) {
1140 UnwindPrinter
<arm64
>::make(p
, length
, path
, showFunctionNames
);
1143 else if ( UnwindPrinter
<arm
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM
) ) {
1144 UnwindPrinter
<arm
>::make(p
, length
, path
, showFunctionNames
);
1147 throw "not a known file type";
1150 catch (const char* msg
) {
1151 throwf("%s in %s", msg
, path
);
1156 int main(int argc
, const char* argv
[])
1158 std::set
<cpu_type_t
> onlyArchs
;
1159 std::vector
<const char*> files
;
1160 bool showFunctionNames
= true;
1163 for(int i
=1; i
< argc
; ++i
) {
1164 const char* arg
= argv
[i
];
1165 if ( arg
[0] == '-' ) {
1166 if ( strcmp(arg
, "-arch") == 0 ) {
1167 const char* arch
= argv
[++i
];
1168 if ( strcmp(arch
, "i386") == 0 )
1169 onlyArchs
.insert(CPU_TYPE_I386
);
1170 else if ( strcmp(arch
, "x86_64") == 0 )
1171 onlyArchs
.insert(CPU_TYPE_X86_64
);
1172 #if SUPPORT_ARCH_arm64
1173 else if ( strcmp(arch
, "arm64") == 0 )
1174 onlyArchs
.insert(CPU_TYPE_ARM64
);
1176 else if ( strcmp(arch
, "armv7k") == 0 )
1177 onlyArchs
.insert(CPU_TYPE_ARM
);
1179 throwf("unknown architecture %s", arch
);
1181 else if ( strcmp(arg
, "-no_symbols") == 0 ) {
1182 showFunctionNames
= false;
1185 throwf("unknown option: %s\n", arg
);
1189 files
.push_back(arg
);
1193 // use all architectures if no restrictions specified
1194 if ( onlyArchs
.size() == 0 ) {
1195 onlyArchs
.insert(CPU_TYPE_I386
);
1196 onlyArchs
.insert(CPU_TYPE_X86_64
);
1197 #if SUPPORT_ARCH_arm64
1198 onlyArchs
.insert(CPU_TYPE_ARM64
);
1200 onlyArchs
.insert(CPU_TYPE_ARM
);
1203 // process each file
1204 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
1205 dump(*it
, onlyArchs
, showFunctionNames
);
1209 catch (const char* msg
) {
1210 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);