3 #include <mach-o/compact_unwind_encoding.h>
4 #include <mach-o/loader.h>
5 #include <mach-o/nlist.h>
6 #include <mach/machine.h>
12 #include <sys/errno.h>
15 #include <sys/types.h>
17 #define EXTRACT_BITS(value, mask) \
18 ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
20 // A quick sketch of a program which can parse the compact unwind info
21 // used on Darwin systems for exception handling. The output of
22 // unwinddump will be more authoritative/reliable but this program
23 // can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries
27 uint64_t file_address
;
31 int symbol_compare(const void *a
, const void *b
) {
32 return (int)((struct symbol
*)a
)->file_address
-
33 ((struct symbol
*)b
)->file_address
;
39 uint8_t *mach_header_start
; // pointer into this program's address space
40 uint8_t *compact_unwind_start
; // pointer into this program's address space
42 int addr_size
; // 4 or 8 bytes, the size of addresses in this file
44 uint64_t text_segment_vmaddr
; // __TEXT segment vmaddr
45 uint64_t text_segment_file_offset
;
47 uint64_t text_section_vmaddr
; // __TEXT,__text section vmaddr
48 uint64_t text_section_file_offset
;
50 uint64_t eh_section_file_address
; // the file address of the __TEXT,__eh_frame
54 *lsda_array_start
; // for the currently-being-processed first-level index
56 *lsda_array_end
; // the lsda_array_start for the NEXT first-level index
58 struct symbol
*symbols
;
61 uint64_t *function_start_addresses
;
62 int function_start_addresses_count
;
64 int current_index_table_number
;
66 struct unwind_info_section_header unwind_header
;
67 struct unwind_info_section_header_index_entry first_level_index_entry
;
68 struct unwind_info_compressed_second_level_page_header
69 compressed_second_level_page_header
;
70 struct unwind_info_regular_second_level_page_header
71 regular_second_level_page_header
;
74 uint64_t read_leb128(uint8_t **offset
) {
78 uint8_t byte
= **offset
;
79 *offset
= *offset
+ 1;
80 result
|= (byte
& 0x7f) << shift
;
81 if ((byte
& 0x80) == 0)
89 // step through the load commands in a thin mach-o binary,
90 // find the cputype and the start of the __TEXT,__unwind_info
91 // section, return a pointer to that section or NULL if not found.
93 static void scan_macho_load_commands(struct baton
*baton
) {
94 struct symtab_command symtab_cmd
;
95 uint64_t linkedit_segment_vmaddr
;
96 uint64_t linkedit_segment_file_offset
;
98 baton
->compact_unwind_start
= 0;
100 uint32_t *magic
= (uint32_t *)baton
->mach_header_start
;
102 if (*magic
!= MH_MAGIC
&& *magic
!= MH_MAGIC_64
) {
103 printf("Unexpected magic number 0x%x in header, exiting.", *magic
);
107 bool is_64bit
= false;
108 if (*magic
== MH_MAGIC_64
)
111 uint8_t *offset
= baton
->mach_header_start
;
113 struct mach_header mh
;
114 memcpy(&mh
, offset
, sizeof(struct mach_header
));
116 offset
+= sizeof(struct mach_header_64
);
118 offset
+= sizeof(struct mach_header
);
121 baton
->addr_size
= 8;
123 baton
->addr_size
= 4;
125 baton
->cputype
= mh
.cputype
;
127 uint8_t *start_of_load_commands
= offset
;
129 uint32_t cur_cmd
= 0;
130 while (cur_cmd
< mh
.ncmds
&&
131 (offset
- start_of_load_commands
) < mh
.sizeofcmds
) {
132 struct load_command lc
;
133 uint32_t *lc_cmd
= (uint32_t *)offset
;
134 uint32_t *lc_cmdsize
= (uint32_t *)offset
+ 1;
135 uint8_t *start_of_this_load_cmd
= offset
;
137 if (*lc_cmd
== LC_SEGMENT
|| *lc_cmd
== LC_SEGMENT_64
) {
138 char segment_name
[17];
139 segment_name
[0] = '\0';
141 uint64_t segment_offset
= 0;
142 uint64_t segment_vmaddr
= 0;
144 if (*lc_cmd
== LC_SEGMENT_64
) {
145 struct segment_command_64 seg
;
146 memcpy(&seg
, offset
, sizeof(struct segment_command_64
));
147 memcpy(&segment_name
, &seg
.segname
, 16);
148 segment_name
[16] = '\0';
150 segment_offset
= seg
.fileoff
;
151 segment_vmaddr
= seg
.vmaddr
;
152 offset
+= sizeof(struct segment_command_64
);
153 if ((seg
.flags
& SG_PROTECTED_VERSION_1
) == SG_PROTECTED_VERSION_1
) {
154 printf("Segment '%s' is encrypted.\n", segment_name
);
158 if (*lc_cmd
== LC_SEGMENT
) {
159 struct segment_command seg
;
160 memcpy(&seg
, offset
, sizeof(struct segment_command
));
161 memcpy(&segment_name
, &seg
.segname
, 16);
162 segment_name
[16] = '\0';
164 segment_offset
= seg
.fileoff
;
165 segment_vmaddr
= seg
.vmaddr
;
166 offset
+= sizeof(struct segment_command
);
167 if ((seg
.flags
& SG_PROTECTED_VERSION_1
) == SG_PROTECTED_VERSION_1
) {
168 printf("Segment '%s' is encrypted.\n", segment_name
);
172 if (nsects
!= 0 && strcmp(segment_name
, "__TEXT") == 0) {
173 baton
->text_segment_vmaddr
= segment_vmaddr
;
174 baton
->text_segment_file_offset
= segment_offset
;
176 uint32_t current_sect
= 0;
177 while (current_sect
< nsects
&&
178 (offset
- start_of_this_load_cmd
) < *lc_cmdsize
) {
180 memcpy(§_name
, offset
, 16);
181 sect_name
[16] = '\0';
182 if (strcmp(sect_name
, "__unwind_info") == 0) {
184 struct section_64 sect
;
185 memset(§
, 0, sizeof(struct section_64
));
186 memcpy(§
, offset
, sizeof(struct section_64
));
187 baton
->compact_unwind_start
=
188 baton
->mach_header_start
+ sect
.offset
;
191 memset(§
, 0, sizeof(struct section
));
192 memcpy(§
, offset
, sizeof(struct section
));
193 baton
->compact_unwind_start
=
194 baton
->mach_header_start
+ sect
.offset
;
197 if (strcmp(sect_name
, "__eh_frame") == 0) {
199 struct section_64 sect
;
200 memset(§
, 0, sizeof(struct section_64
));
201 memcpy(§
, offset
, sizeof(struct section_64
));
202 baton
->eh_section_file_address
= sect
.addr
;
205 memset(§
, 0, sizeof(struct section
));
206 memcpy(§
, offset
, sizeof(struct section
));
207 baton
->eh_section_file_address
= sect
.addr
;
210 if (strcmp(sect_name
, "__text") == 0) {
212 struct section_64 sect
;
213 memset(§
, 0, sizeof(struct section_64
));
214 memcpy(§
, offset
, sizeof(struct section_64
));
215 baton
->text_section_vmaddr
= sect
.addr
;
216 baton
->text_section_file_offset
= sect
.offset
;
219 memset(§
, 0, sizeof(struct section
));
220 memcpy(§
, offset
, sizeof(struct section
));
221 baton
->text_section_vmaddr
= sect
.addr
;
225 offset
+= sizeof(struct section_64
);
227 offset
+= sizeof(struct section
);
232 if (strcmp(segment_name
, "__LINKEDIT") == 0) {
233 linkedit_segment_vmaddr
= segment_vmaddr
;
234 linkedit_segment_file_offset
= segment_offset
;
238 if (*lc_cmd
== LC_SYMTAB
) {
239 memcpy(&symtab_cmd
, offset
, sizeof(struct symtab_command
));
242 if (*lc_cmd
== LC_DYSYMTAB
) {
243 struct dysymtab_command dysymtab_cmd
;
244 memcpy(&dysymtab_cmd
, offset
, sizeof(struct dysymtab_command
));
251 (char *)(baton
->mach_header_start
+ symtab_cmd
.stroff
);
252 uint8_t *local_syms
= baton
->mach_header_start
+ symtab_cmd
.symoff
+
253 (dysymtab_cmd
.ilocalsym
* nlist_size
);
254 int local_syms_count
= dysymtab_cmd
.nlocalsym
;
255 uint8_t *exported_syms
= baton
->mach_header_start
+ symtab_cmd
.symoff
+
256 (dysymtab_cmd
.iextdefsym
* nlist_size
);
257 int exported_syms_count
= dysymtab_cmd
.nextdefsym
;
259 // We're only going to create records for a small number of these symbols
261 // simplify the memory management I'll allocate enough space to store all
263 baton
->symbols
= (struct symbol
*)malloc(
264 sizeof(struct symbol
) * (local_syms_count
+ exported_syms_count
));
265 baton
->symbols_count
= 0;
267 for (int i
= 0; i
< local_syms_count
; i
++) {
268 struct nlist_64 nlist
;
269 memset(&nlist
, 0, sizeof(struct nlist_64
));
271 memcpy(&nlist
, local_syms
+ (i
* nlist_size
),
272 sizeof(struct nlist_64
));
274 struct nlist nlist_32
;
275 memset(&nlist_32
, 0, sizeof(struct nlist
));
276 memcpy(&nlist_32
, local_syms
+ (i
* nlist_size
),
277 sizeof(struct nlist
));
278 nlist
.n_un
.n_strx
= nlist_32
.n_un
.n_strx
;
279 nlist
.n_type
= nlist_32
.n_type
;
280 nlist
.n_sect
= nlist_32
.n_sect
;
281 nlist
.n_desc
= nlist_32
.n_desc
;
282 nlist
.n_value
= nlist_32
.n_value
;
284 if ((nlist
.n_type
& N_STAB
) == 0 &&
285 ((nlist
.n_type
& N_EXT
) == 1 ||
286 ((nlist
.n_type
& N_TYPE
) == N_TYPE
&& nlist
.n_sect
!= NO_SECT
)) &&
287 nlist
.n_value
!= 0 && nlist
.n_value
!= baton
->text_segment_vmaddr
) {
288 baton
->symbols
[baton
->symbols_count
].file_address
= nlist
.n_value
;
289 if (baton
->cputype
== CPU_TYPE_ARM
)
290 baton
->symbols
[baton
->symbols_count
].file_address
=
291 baton
->symbols
[baton
->symbols_count
].file_address
& ~1;
292 baton
->symbols
[baton
->symbols_count
].name
=
293 string_table
+ nlist
.n_un
.n_strx
;
294 baton
->symbols_count
++;
298 for (int i
= 0; i
< exported_syms_count
; i
++) {
299 struct nlist_64 nlist
;
300 memset(&nlist
, 0, sizeof(struct nlist_64
));
302 memcpy(&nlist
, exported_syms
+ (i
* nlist_size
),
303 sizeof(struct nlist_64
));
305 struct nlist nlist_32
;
306 memcpy(&nlist_32
, exported_syms
+ (i
* nlist_size
),
307 sizeof(struct nlist
));
308 nlist
.n_un
.n_strx
= nlist_32
.n_un
.n_strx
;
309 nlist
.n_type
= nlist_32
.n_type
;
310 nlist
.n_sect
= nlist_32
.n_sect
;
311 nlist
.n_desc
= nlist_32
.n_desc
;
312 nlist
.n_value
= nlist_32
.n_value
;
314 if ((nlist
.n_type
& N_STAB
) == 0 &&
315 ((nlist
.n_type
& N_EXT
) == 1 ||
316 ((nlist
.n_type
& N_TYPE
) == N_TYPE
&& nlist
.n_sect
!= NO_SECT
)) &&
317 nlist
.n_value
!= 0 && nlist
.n_value
!= baton
->text_segment_vmaddr
) {
318 baton
->symbols
[baton
->symbols_count
].file_address
= nlist
.n_value
;
319 if (baton
->cputype
== CPU_TYPE_ARM
)
320 baton
->symbols
[baton
->symbols_count
].file_address
=
321 baton
->symbols
[baton
->symbols_count
].file_address
& ~1;
322 baton
->symbols
[baton
->symbols_count
].name
=
323 string_table
+ nlist
.n_un
.n_strx
;
324 baton
->symbols_count
++;
328 qsort(baton
->symbols
, baton
->symbols_count
, sizeof(struct symbol
),
332 if (*lc_cmd
== LC_FUNCTION_STARTS
) {
333 struct linkedit_data_command function_starts_cmd
;
334 memcpy(&function_starts_cmd
, offset
,
335 sizeof(struct linkedit_data_command
));
337 uint8_t *funcstarts_offset
=
338 baton
->mach_header_start
+ function_starts_cmd
.dataoff
;
339 uint8_t *function_end
= funcstarts_offset
+ function_starts_cmd
.datasize
;
342 while (funcstarts_offset
< function_end
) {
343 if (read_leb128(&funcstarts_offset
) != 0) {
348 baton
->function_start_addresses
=
349 (uint64_t *)malloc(sizeof(uint64_t) * count
);
350 baton
->function_start_addresses_count
= count
;
353 baton
->mach_header_start
+ function_starts_cmd
.dataoff
;
354 uint64_t current_pc
= baton
->text_segment_vmaddr
;
356 while (funcstarts_offset
< function_end
) {
357 uint64_t func_start
= read_leb128(&funcstarts_offset
);
358 if (func_start
!= 0) {
359 current_pc
+= func_start
;
360 baton
->function_start_addresses
[i
++] = current_pc
;
365 offset
= start_of_this_load_cmd
+ *lc_cmdsize
;
369 // Augment the symbol table with the function starts table -- adding symbol
371 // for functions that were stripped.
373 int unnamed_functions_to_add
= 0;
374 for (int i
= 0; i
< baton
->function_start_addresses_count
; i
++) {
375 struct symbol search_key
;
376 search_key
.file_address
= baton
->function_start_addresses
[i
];
377 if (baton
->cputype
== CPU_TYPE_ARM
)
378 search_key
.file_address
= search_key
.file_address
& ~1;
380 bsearch(&search_key
, baton
->symbols
, baton
->symbols_count
,
381 sizeof(struct symbol
), symbol_compare
);
383 unnamed_functions_to_add
++;
386 baton
->symbols
= (struct symbol
*)realloc(
387 baton
->symbols
, sizeof(struct symbol
) *
388 (baton
->symbols_count
+ unnamed_functions_to_add
));
390 int current_unnamed_symbol
= 1;
391 int number_symbols_added
= 0;
392 for (int i
= 0; i
< baton
->function_start_addresses_count
; i
++) {
393 struct symbol search_key
;
394 search_key
.file_address
= baton
->function_start_addresses
[i
];
395 if (baton
->cputype
== CPU_TYPE_ARM
)
396 search_key
.file_address
= search_key
.file_address
& ~1;
398 bsearch(&search_key
, baton
->symbols
, baton
->symbols_count
,
399 sizeof(struct symbol
), symbol_compare
);
402 asprintf(&name
, "unnamed function #%d", current_unnamed_symbol
++);
403 baton
->symbols
[baton
->symbols_count
+ number_symbols_added
].file_address
=
404 baton
->function_start_addresses
[i
];
405 baton
->symbols
[baton
->symbols_count
+ number_symbols_added
].name
= name
;
406 number_symbols_added
++;
409 baton
->symbols_count
+= number_symbols_added
;
410 qsort(baton
->symbols
, baton
->symbols_count
, sizeof(struct symbol
),
413 // printf ("function start addresses\n");
414 // for (int i = 0; i < baton->function_start_addresses_count; i++)
416 // printf ("0x%012llx\n", baton->function_start_addresses[i]);
419 // printf ("symbol table names & addresses\n");
420 // for (int i = 0; i < baton->symbols_count; i++)
422 // printf ("0x%012llx %s\n", baton->symbols[i].file_address,
423 // baton->symbols[i].name);
427 void print_encoding_x86_64(struct baton baton
, uint8_t *function_start
,
429 int mode
= encoding
& UNWIND_X86_64_MODE_MASK
;
431 case UNWIND_X86_64_MODE_RBP_FRAME
: {
432 printf("frame func: CFA is rbp+%d ", 16);
433 printf(" rip=[CFA-8] rbp=[CFA-16]");
434 uint32_t saved_registers_offset
=
435 EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_OFFSET
);
437 uint32_t saved_registers_locations
=
438 EXTRACT_BITS(encoding
, UNWIND_X86_64_RBP_FRAME_REGISTERS
);
440 saved_registers_offset
+= 2;
442 for (int i
= 0; i
< 5; i
++) {
443 switch (saved_registers_locations
& 0x7) {
444 case UNWIND_X86_64_REG_NONE
:
446 case UNWIND_X86_64_REG_RBX
:
447 printf(" rbx=[CFA-%d]", saved_registers_offset
* 8);
449 case UNWIND_X86_64_REG_R12
:
450 printf(" r12=[CFA-%d]", saved_registers_offset
* 8);
452 case UNWIND_X86_64_REG_R13
:
453 printf(" r13=[CFA-%d]", saved_registers_offset
* 8);
455 case UNWIND_X86_64_REG_R14
:
456 printf(" r14=[CFA-%d]", saved_registers_offset
* 8);
458 case UNWIND_X86_64_REG_R15
:
459 printf(" r15=[CFA-%d]", saved_registers_offset
* 8);
462 saved_registers_offset
--;
463 saved_registers_locations
>>= 3;
467 case UNWIND_X86_64_MODE_STACK_IND
:
468 case UNWIND_X86_64_MODE_STACK_IMMD
: {
469 uint32_t stack_size
=
470 EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
471 uint32_t register_count
=
472 EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT
);
473 uint32_t permutation
=
474 EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION
);
476 if (mode
== UNWIND_X86_64_MODE_STACK_IND
&& function_start
) {
477 uint32_t stack_adjust
=
478 EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_ADJUST
);
480 // offset into the function instructions; 0 == beginning of first
482 uint32_t offset_to_subl_insn
=
483 EXTRACT_BITS(encoding
, UNWIND_X86_64_FRAMELESS_STACK_SIZE
);
485 stack_size
= *((uint32_t *)(function_start
+ offset_to_subl_insn
));
487 stack_size
+= stack_adjust
* 8;
489 printf("large stack ");
492 if (mode
== UNWIND_X86_64_MODE_STACK_IND
) {
493 printf("frameless function: stack size %d, register count %d ",
494 stack_size
* 8, register_count
);
496 printf("frameless function: stack size %d, register count %d ",
497 stack_size
, register_count
);
500 if (register_count
== 0) {
501 printf(" no registers saved");
504 // We need to include (up to) 6 registers in 10 bits.
505 // That would be 18 bits if we just used 3 bits per reg to indicate
506 // the order they're saved on the stack.
508 // This is done with Lehmer code permutation, e.g. see
509 // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
512 // This decodes the variable-base number in the 10 bits
513 // and gives us the Lehmer code sequence which can then
516 switch (register_count
) {
518 permunreg
[0] = permutation
/ 120; // 120 == 5!
519 permutation
-= (permunreg
[0] * 120);
520 permunreg
[1] = permutation
/ 24; // 24 == 4!
521 permutation
-= (permunreg
[1] * 24);
522 permunreg
[2] = permutation
/ 6; // 6 == 3!
523 permutation
-= (permunreg
[2] * 6);
524 permunreg
[3] = permutation
/ 2; // 2 == 2!
525 permutation
-= (permunreg
[3] * 2);
526 permunreg
[4] = permutation
; // 1 == 1!
530 permunreg
[0] = permutation
/ 120;
531 permutation
-= (permunreg
[0] * 120);
532 permunreg
[1] = permutation
/ 24;
533 permutation
-= (permunreg
[1] * 24);
534 permunreg
[2] = permutation
/ 6;
535 permutation
-= (permunreg
[2] * 6);
536 permunreg
[3] = permutation
/ 2;
537 permutation
-= (permunreg
[3] * 2);
538 permunreg
[4] = permutation
;
541 permunreg
[0] = permutation
/ 60;
542 permutation
-= (permunreg
[0] * 60);
543 permunreg
[1] = permutation
/ 12;
544 permutation
-= (permunreg
[1] * 12);
545 permunreg
[2] = permutation
/ 3;
546 permutation
-= (permunreg
[2] * 3);
547 permunreg
[3] = permutation
;
550 permunreg
[0] = permutation
/ 20;
551 permutation
-= (permunreg
[0] * 20);
552 permunreg
[1] = permutation
/ 4;
553 permutation
-= (permunreg
[1] * 4);
554 permunreg
[2] = permutation
;
557 permunreg
[0] = permutation
/ 5;
558 permutation
-= (permunreg
[0] * 5);
559 permunreg
[1] = permutation
;
562 permunreg
[0] = permutation
;
566 // Decode the Lehmer code for this permutation of
567 // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
570 bool used
[7] = {false, false, false, false, false, false, false};
571 for (int i
= 0; i
< register_count
; i
++) {
573 for (int j
= 1; j
< 7; j
++) {
574 if (used
[j
] == false) {
575 if (renum
== permunreg
[i
]) {
585 if (mode
== UNWIND_X86_64_MODE_STACK_IND
) {
586 printf(" CFA is rsp+%d ", stack_size
);
588 printf(" CFA is rsp+%d ", stack_size
* 8);
591 uint32_t saved_registers_offset
= 1;
592 printf(" rip=[CFA-%d]", saved_registers_offset
* 8);
593 saved_registers_offset
++;
595 for (int i
= (sizeof(registers
) / sizeof(int)) - 1; i
>= 0; i
--) {
596 switch (registers
[i
]) {
597 case UNWIND_X86_64_REG_NONE
:
599 case UNWIND_X86_64_REG_RBX
:
600 printf(" rbx=[CFA-%d]", saved_registers_offset
* 8);
601 saved_registers_offset
++;
603 case UNWIND_X86_64_REG_R12
:
604 printf(" r12=[CFA-%d]", saved_registers_offset
* 8);
605 saved_registers_offset
++;
607 case UNWIND_X86_64_REG_R13
:
608 printf(" r13=[CFA-%d]", saved_registers_offset
* 8);
609 saved_registers_offset
++;
611 case UNWIND_X86_64_REG_R14
:
612 printf(" r14=[CFA-%d]", saved_registers_offset
* 8);
613 saved_registers_offset
++;
615 case UNWIND_X86_64_REG_R15
:
616 printf(" r15=[CFA-%d]", saved_registers_offset
* 8);
617 saved_registers_offset
++;
619 case UNWIND_X86_64_REG_RBP
:
620 printf(" rbp=[CFA-%d]", saved_registers_offset
* 8);
621 saved_registers_offset
++;
629 case UNWIND_X86_64_MODE_DWARF
: {
630 uint32_t dwarf_offset
= encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
;
632 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
634 dwarf_offset
, dwarf_offset
+ baton
.eh_section_file_address
);
638 printf(" no unwind information");
643 void print_encoding_i386(struct baton baton
, uint8_t *function_start
,
645 int mode
= encoding
& UNWIND_X86_MODE_MASK
;
647 case UNWIND_X86_MODE_EBP_FRAME
: {
648 printf("frame func: CFA is ebp+%d ", 8);
649 printf(" eip=[CFA-4] ebp=[CFA-8]");
650 uint32_t saved_registers_offset
=
651 EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_OFFSET
);
653 uint32_t saved_registers_locations
=
654 EXTRACT_BITS(encoding
, UNWIND_X86_EBP_FRAME_REGISTERS
);
656 saved_registers_offset
+= 2;
658 for (int i
= 0; i
< 5; i
++) {
659 switch (saved_registers_locations
& 0x7) {
660 case UNWIND_X86_REG_NONE
:
662 case UNWIND_X86_REG_EBX
:
663 printf(" ebx=[CFA-%d]", saved_registers_offset
* 4);
665 case UNWIND_X86_REG_ECX
:
666 printf(" ecx=[CFA-%d]", saved_registers_offset
* 4);
668 case UNWIND_X86_REG_EDX
:
669 printf(" edx=[CFA-%d]", saved_registers_offset
* 4);
671 case UNWIND_X86_REG_EDI
:
672 printf(" edi=[CFA-%d]", saved_registers_offset
* 4);
674 case UNWIND_X86_REG_ESI
:
675 printf(" esi=[CFA-%d]", saved_registers_offset
* 4);
678 saved_registers_offset
--;
679 saved_registers_locations
>>= 3;
683 case UNWIND_X86_MODE_STACK_IND
:
684 case UNWIND_X86_MODE_STACK_IMMD
: {
685 uint32_t stack_size
=
686 EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
687 uint32_t register_count
=
688 EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_COUNT
);
689 uint32_t permutation
=
690 EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION
);
692 if (mode
== UNWIND_X86_MODE_STACK_IND
&& function_start
) {
693 uint32_t stack_adjust
=
694 EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_ADJUST
);
696 // offset into the function instructions; 0 == beginning of first
698 uint32_t offset_to_subl_insn
=
699 EXTRACT_BITS(encoding
, UNWIND_X86_FRAMELESS_STACK_SIZE
);
701 stack_size
= *((uint32_t *)(function_start
+ offset_to_subl_insn
));
703 stack_size
+= stack_adjust
* 4;
705 printf("large stack ");
708 if (mode
== UNWIND_X86_MODE_STACK_IND
) {
709 printf("frameless function: stack size %d, register count %d ",
710 stack_size
, register_count
);
712 printf("frameless function: stack size %d, register count %d ",
713 stack_size
* 4, register_count
);
716 if (register_count
== 0) {
717 printf(" no registers saved");
720 // We need to include (up to) 6 registers in 10 bits.
721 // That would be 18 bits if we just used 3 bits per reg to indicate
722 // the order they're saved on the stack.
724 // This is done with Lehmer code permutation, e.g. see
725 // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
728 // This decodes the variable-base number in the 10 bits
729 // and gives us the Lehmer code sequence which can then
732 switch (register_count
) {
734 permunreg
[0] = permutation
/ 120; // 120 == 5!
735 permutation
-= (permunreg
[0] * 120);
736 permunreg
[1] = permutation
/ 24; // 24 == 4!
737 permutation
-= (permunreg
[1] * 24);
738 permunreg
[2] = permutation
/ 6; // 6 == 3!
739 permutation
-= (permunreg
[2] * 6);
740 permunreg
[3] = permutation
/ 2; // 2 == 2!
741 permutation
-= (permunreg
[3] * 2);
742 permunreg
[4] = permutation
; // 1 == 1!
746 permunreg
[0] = permutation
/ 120;
747 permutation
-= (permunreg
[0] * 120);
748 permunreg
[1] = permutation
/ 24;
749 permutation
-= (permunreg
[1] * 24);
750 permunreg
[2] = permutation
/ 6;
751 permutation
-= (permunreg
[2] * 6);
752 permunreg
[3] = permutation
/ 2;
753 permutation
-= (permunreg
[3] * 2);
754 permunreg
[4] = permutation
;
757 permunreg
[0] = permutation
/ 60;
758 permutation
-= (permunreg
[0] * 60);
759 permunreg
[1] = permutation
/ 12;
760 permutation
-= (permunreg
[1] * 12);
761 permunreg
[2] = permutation
/ 3;
762 permutation
-= (permunreg
[2] * 3);
763 permunreg
[3] = permutation
;
766 permunreg
[0] = permutation
/ 20;
767 permutation
-= (permunreg
[0] * 20);
768 permunreg
[1] = permutation
/ 4;
769 permutation
-= (permunreg
[1] * 4);
770 permunreg
[2] = permutation
;
773 permunreg
[0] = permutation
/ 5;
774 permutation
-= (permunreg
[0] * 5);
775 permunreg
[1] = permutation
;
778 permunreg
[0] = permutation
;
782 // Decode the Lehmer code for this permutation of
783 // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
786 bool used
[7] = {false, false, false, false, false, false, false};
787 for (int i
= 0; i
< register_count
; i
++) {
789 for (int j
= 1; j
< 7; j
++) {
790 if (used
[j
] == false) {
791 if (renum
== permunreg
[i
]) {
801 if (mode
== UNWIND_X86_MODE_STACK_IND
) {
802 printf(" CFA is esp+%d ", stack_size
);
804 printf(" CFA is esp+%d ", stack_size
* 4);
807 uint32_t saved_registers_offset
= 1;
808 printf(" eip=[CFA-%d]", saved_registers_offset
* 4);
809 saved_registers_offset
++;
811 for (int i
= (sizeof(registers
) / sizeof(int)) - 1; i
>= 0; i
--) {
812 switch (registers
[i
]) {
813 case UNWIND_X86_REG_NONE
:
815 case UNWIND_X86_REG_EBX
:
816 printf(" ebx=[CFA-%d]", saved_registers_offset
* 4);
817 saved_registers_offset
++;
819 case UNWIND_X86_REG_ECX
:
820 printf(" ecx=[CFA-%d]", saved_registers_offset
* 4);
821 saved_registers_offset
++;
823 case UNWIND_X86_REG_EDX
:
824 printf(" edx=[CFA-%d]", saved_registers_offset
* 4);
825 saved_registers_offset
++;
827 case UNWIND_X86_REG_EDI
:
828 printf(" edi=[CFA-%d]", saved_registers_offset
* 4);
829 saved_registers_offset
++;
831 case UNWIND_X86_REG_ESI
:
832 printf(" esi=[CFA-%d]", saved_registers_offset
* 4);
833 saved_registers_offset
++;
835 case UNWIND_X86_REG_EBP
:
836 printf(" ebp=[CFA-%d]", saved_registers_offset
* 4);
837 saved_registers_offset
++;
845 case UNWIND_X86_MODE_DWARF
: {
846 uint32_t dwarf_offset
= encoding
& UNWIND_X86_DWARF_SECTION_OFFSET
;
848 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
850 dwarf_offset
, dwarf_offset
+ baton
.eh_section_file_address
);
854 printf(" no unwind information");
859 void print_encoding_arm64(struct baton baton
, uint8_t *function_start
,
861 const int wordsize
= 8;
862 int mode
= encoding
& UNWIND_ARM64_MODE_MASK
;
864 case UNWIND_ARM64_MODE_FRAME
: {
865 printf("frame func: CFA is fp+%d ", 16);
866 printf(" pc=[CFA-8] fp=[CFA-16]");
867 int reg_pairs_saved_count
= 1;
868 uint32_t saved_register_bits
= encoding
& 0xfff;
869 if (saved_register_bits
& UNWIND_ARM64_FRAME_X19_X20_PAIR
) {
870 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
871 cfa_offset
-= wordsize
;
872 printf(" x19=[CFA%d]", cfa_offset
);
873 cfa_offset
-= wordsize
;
874 printf(" x20=[CFA%d]", cfa_offset
);
875 reg_pairs_saved_count
++;
877 if (saved_register_bits
& UNWIND_ARM64_FRAME_X21_X22_PAIR
) {
878 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
879 cfa_offset
-= wordsize
;
880 printf(" x21=[CFA%d]", cfa_offset
);
881 cfa_offset
-= wordsize
;
882 printf(" x22=[CFA%d]", cfa_offset
);
883 reg_pairs_saved_count
++;
885 if (saved_register_bits
& UNWIND_ARM64_FRAME_X23_X24_PAIR
) {
886 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
887 cfa_offset
-= wordsize
;
888 printf(" x23=[CFA%d]", cfa_offset
);
889 cfa_offset
-= wordsize
;
890 printf(" x24=[CFA%d]", cfa_offset
);
891 reg_pairs_saved_count
++;
893 if (saved_register_bits
& UNWIND_ARM64_FRAME_X25_X26_PAIR
) {
894 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
895 cfa_offset
-= wordsize
;
896 printf(" x25=[CFA%d]", cfa_offset
);
897 cfa_offset
-= wordsize
;
898 printf(" x26=[CFA%d]", cfa_offset
);
899 reg_pairs_saved_count
++;
901 if (saved_register_bits
& UNWIND_ARM64_FRAME_X27_X28_PAIR
) {
902 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
903 cfa_offset
-= wordsize
;
904 printf(" x27=[CFA%d]", cfa_offset
);
905 cfa_offset
-= wordsize
;
906 printf(" x28=[CFA%d]", cfa_offset
);
907 reg_pairs_saved_count
++;
909 if (saved_register_bits
& UNWIND_ARM64_FRAME_D8_D9_PAIR
) {
910 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
911 cfa_offset
-= wordsize
;
912 printf(" d8=[CFA%d]", cfa_offset
);
913 cfa_offset
-= wordsize
;
914 printf(" d9=[CFA%d]", cfa_offset
);
915 reg_pairs_saved_count
++;
917 if (saved_register_bits
& UNWIND_ARM64_FRAME_D10_D11_PAIR
) {
918 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
919 cfa_offset
-= wordsize
;
920 printf(" d10=[CFA%d]", cfa_offset
);
921 cfa_offset
-= wordsize
;
922 printf(" d11=[CFA%d]", cfa_offset
);
923 reg_pairs_saved_count
++;
925 if (saved_register_bits
& UNWIND_ARM64_FRAME_D12_D13_PAIR
) {
926 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
927 cfa_offset
-= wordsize
;
928 printf(" d12=[CFA%d]", cfa_offset
);
929 cfa_offset
-= wordsize
;
930 printf(" d13=[CFA%d]", cfa_offset
);
931 reg_pairs_saved_count
++;
933 if (saved_register_bits
& UNWIND_ARM64_FRAME_D14_D15_PAIR
) {
934 int cfa_offset
= reg_pairs_saved_count
* -2 * wordsize
;
935 cfa_offset
-= wordsize
;
936 printf(" d14=[CFA%d]", cfa_offset
);
937 cfa_offset
-= wordsize
;
938 printf(" d15=[CFA%d]", cfa_offset
);
939 reg_pairs_saved_count
++;
944 case UNWIND_ARM64_MODE_FRAMELESS
: {
945 uint32_t stack_size
= encoding
& UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
;
946 printf("frameless function: stack size %d ", stack_size
* 16);
950 case UNWIND_ARM64_MODE_DWARF
: {
951 uint32_t dwarf_offset
= encoding
& UNWIND_ARM64_DWARF_SECTION_OFFSET
;
953 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
955 dwarf_offset
, dwarf_offset
+ baton
.eh_section_file_address
);
959 printf(" no unwind information");
964 void print_encoding_armv7(struct baton baton
, uint8_t *function_start
,
966 const int wordsize
= 4;
967 int mode
= encoding
& UNWIND_ARM_MODE_MASK
;
969 case UNWIND_ARM_MODE_FRAME_D
:
970 case UNWIND_ARM_MODE_FRAME
: {
972 EXTRACT_BITS(encoding
, UNWIND_ARM_FRAME_STACK_ADJUST_MASK
) * wordsize
;
974 printf("frame func: CFA is fp+%d ", (2 * wordsize
) + stack_adjust
);
975 int cfa_offset
= -stack_adjust
;
977 cfa_offset
-= wordsize
;
978 printf(" pc=[CFA%d]", cfa_offset
);
979 cfa_offset
-= wordsize
;
980 printf(" fp=[CFA%d]", cfa_offset
);
982 uint32_t saved_register_bits
= encoding
& 0xff;
983 if (saved_register_bits
& UNWIND_ARM_FRAME_FIRST_PUSH_R6
) {
984 cfa_offset
-= wordsize
;
985 printf(" r6=[CFA%d]", cfa_offset
);
987 if (saved_register_bits
& UNWIND_ARM_FRAME_FIRST_PUSH_R5
) {
988 cfa_offset
-= wordsize
;
989 printf(" r5=[CFA%d]", cfa_offset
);
991 if (saved_register_bits
& UNWIND_ARM_FRAME_FIRST_PUSH_R4
) {
992 cfa_offset
-= wordsize
;
993 printf(" r4=[CFA%d]", cfa_offset
);
995 if (saved_register_bits
& UNWIND_ARM_FRAME_SECOND_PUSH_R12
) {
996 cfa_offset
-= wordsize
;
997 printf(" r12=[CFA%d]", cfa_offset
);
999 if (saved_register_bits
& UNWIND_ARM_FRAME_SECOND_PUSH_R11
) {
1000 cfa_offset
-= wordsize
;
1001 printf(" r11=[CFA%d]", cfa_offset
);
1003 if (saved_register_bits
& UNWIND_ARM_FRAME_SECOND_PUSH_R10
) {
1004 cfa_offset
-= wordsize
;
1005 printf(" r10=[CFA%d]", cfa_offset
);
1007 if (saved_register_bits
& UNWIND_ARM_FRAME_SECOND_PUSH_R9
) {
1008 cfa_offset
-= wordsize
;
1009 printf(" r9=[CFA%d]", cfa_offset
);
1011 if (saved_register_bits
& UNWIND_ARM_FRAME_SECOND_PUSH_R8
) {
1012 cfa_offset
-= wordsize
;
1013 printf(" r8=[CFA%d]", cfa_offset
);
1016 if (mode
== UNWIND_ARM_MODE_FRAME_D
) {
1017 uint32_t d_reg_bits
=
1018 EXTRACT_BITS(encoding
, UNWIND_ARM_FRAME_D_REG_COUNT_MASK
);
1019 switch (d_reg_bits
) {
1023 printf(" d8=[CFA%d]", cfa_offset
);
1029 printf(" d10=[CFA%d]", cfa_offset
);
1031 printf(" d8=[CFA%d]", cfa_offset
);
1038 printf(" d12=[CFA%d]", cfa_offset
);
1040 printf(" d10=[CFA%d]", cfa_offset
);
1042 printf(" d8=[CFA%d]", cfa_offset
);
1050 printf(" d14=[CFA%d]", cfa_offset
);
1052 printf(" d12=[CFA%d]", cfa_offset
);
1054 printf(" d10=[CFA%d]", cfa_offset
);
1056 printf(" d8=[CFA%d]", cfa_offset
);
1061 // sp = (sp - 24) & (-16);
1062 // vst {d8, d9, d10}
1063 printf(" d14, d12, d10, d9, d8");
1067 // sp = (sp - 40) & (-16);
1068 // vst {d8, d9, d10, d11}
1070 printf(" d14, d11, d10, d9, d8, d12");
1073 // sp = (sp - 56) & (-16);
1074 // vst {d8, d9, d10, d11}
1075 // vst {d12, d13, d14}
1076 printf(" d11, d10, d9, d8, d14, d13, d12");
1079 // sp = (sp - 64) & (-16);
1080 // vst {d8, d9, d10, d11}
1081 // vst {d12, d13, d14, d15}
1082 printf(" d11, d10, d9, d8, d15, d14, d13, d12");
1088 case UNWIND_ARM_MODE_DWARF
: {
1089 uint32_t dwarf_offset
= encoding
& UNWIND_ARM_DWARF_SECTION_OFFSET
;
1091 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
1093 dwarf_offset
, dwarf_offset
+ baton
.eh_section_file_address
);
1097 printf(" no unwind information");
1102 void print_encoding(struct baton baton
, uint8_t *function_start
,
1103 uint32_t encoding
) {
1105 if (baton
.cputype
== CPU_TYPE_X86_64
) {
1106 print_encoding_x86_64(baton
, function_start
, encoding
);
1107 } else if (baton
.cputype
== CPU_TYPE_I386
) {
1108 print_encoding_i386(baton
, function_start
, encoding
);
1109 } else if (baton
.cputype
== CPU_TYPE_ARM64
|| baton
.cputype
== CPU_TYPE_ARM64_32
) {
1110 print_encoding_arm64(baton
, function_start
, encoding
);
1111 } else if (baton
.cputype
== CPU_TYPE_ARM
) {
1112 print_encoding_armv7(baton
, function_start
, encoding
);
1114 printf(" -- unsupported encoding arch -- ");
1118 void print_function_encoding(struct baton baton
, uint32_t idx
,
1119 uint32_t encoding
, uint32_t entry_encoding_index
,
1120 uint32_t entry_func_offset
) {
1122 char *entry_encoding_index_str
= "";
1123 if (entry_encoding_index
!= (uint32_t)-1) {
1124 asprintf(&entry_encoding_index_str
, ", encoding #%d", entry_encoding_index
);
1126 asprintf(&entry_encoding_index_str
, "");
1129 uint64_t file_address
= baton
.first_level_index_entry
.functionOffset
+
1130 entry_func_offset
+ baton
.text_segment_vmaddr
;
1132 if (baton
.cputype
== CPU_TYPE_ARM
)
1133 file_address
= file_address
& ~1;
1136 " func [%d] offset %d (file addr 0x%" PRIx64
")%s, encoding is 0x%x",
1137 idx
, entry_func_offset
, file_address
, entry_encoding_index_str
, encoding
);
1139 struct symbol
*symbol
= NULL
;
1140 for (int i
= 0; i
< baton
.symbols_count
; i
++) {
1141 if (i
== baton
.symbols_count
- 1 &&
1142 baton
.symbols
[i
].file_address
<= file_address
) {
1143 symbol
= &(baton
.symbols
[i
]);
1146 if (baton
.symbols
[i
].file_address
<= file_address
&&
1147 baton
.symbols
[i
+ 1].file_address
> file_address
) {
1148 symbol
= &(baton
.symbols
[i
]);
1156 int offset
= file_address
- symbol
->file_address
;
1158 // FIXME this is a poor heuristic - if we're greater than 16 bytes past the
1159 // start of the function, this is the unwind info for a stripped function.
1160 // In reality the compact unwind entry may not line up exactly with the
1163 printf("name: %s", symbol
->name
);
1165 printf(" + %d", offset
);
1171 print_encoding(baton
, baton
.mach_header_start
+
1172 baton
.first_level_index_entry
.functionOffset
+
1173 baton
.text_section_file_offset
+ entry_func_offset
,
1176 bool has_lsda
= encoding
& UNWIND_HAS_LSDA
;
1179 uint32_t func_offset
=
1180 entry_func_offset
+ baton
.first_level_index_entry
.functionOffset
;
1182 int lsda_entry_number
= -1;
1185 uint32_t high
= (baton
.lsda_array_end
- baton
.lsda_array_start
) /
1186 sizeof(struct unwind_info_section_header_lsda_index_entry
);
1188 while (low
< high
) {
1189 uint32_t mid
= (low
+ high
) / 2;
1191 uint8_t *mid_lsda_entry_addr
=
1192 (baton
.lsda_array_start
+
1193 (mid
* sizeof(struct unwind_info_section_header_lsda_index_entry
)));
1194 struct unwind_info_section_header_lsda_index_entry mid_lsda_entry
;
1195 memcpy(&mid_lsda_entry
, mid_lsda_entry_addr
,
1196 sizeof(struct unwind_info_section_header_lsda_index_entry
));
1197 if (mid_lsda_entry
.functionOffset
== func_offset
) {
1199 (mid_lsda_entry_addr
- baton
.lsda_array_start
) /
1200 sizeof(struct unwind_info_section_header_lsda_index_entry
);
1202 } else if (mid_lsda_entry
.functionOffset
< func_offset
) {
1209 if (lsda_entry_number
!= -1) {
1210 printf(", LSDA entry #%d", lsda_entry_number
);
1212 printf(", LSDA entry not found");
1216 uint32_t pers_idx
= EXTRACT_BITS(encoding
, UNWIND_PERSONALITY_MASK
);
1217 if (pers_idx
!= 0) {
1218 pers_idx
--; // Change 1-based to 0-based index
1219 printf(", personality entry #%d", pers_idx
);
1225 void print_second_level_index_regular(struct baton baton
) {
1226 uint8_t *page_entries
=
1227 baton
.compact_unwind_start
+
1228 baton
.first_level_index_entry
.secondLevelPagesSectionOffset
+
1229 baton
.regular_second_level_page_header
.entryPageOffset
;
1230 uint32_t entries_count
= baton
.regular_second_level_page_header
.entryCount
;
1232 uint8_t *offset
= page_entries
;
1235 while (idx
< entries_count
) {
1236 uint32_t func_offset
= *((uint32_t *)(offset
));
1237 uint32_t encoding
= *((uint32_t *)(offset
+ 4));
1239 // UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the
1240 // functionOffset from the containing index table already.
1241 // UNWIND_SECOND_LEVEL_COMPRESSED
1242 // entries only have the offset from the containing index table
1244 // So strip off the containing index table functionOffset value here so they
1246 // be treated the same at the lower layers.
1248 print_function_encoding(baton
, idx
, encoding
, (uint32_t)-1,
1250 baton
.first_level_index_entry
.functionOffset
);
1256 void print_second_level_index_compressed(struct baton baton
) {
1257 uint8_t *this_index
=
1258 baton
.compact_unwind_start
+
1259 baton
.first_level_index_entry
.secondLevelPagesSectionOffset
;
1260 uint8_t *start_of_entries
=
1261 this_index
+ baton
.compressed_second_level_page_header
.entryPageOffset
;
1262 uint8_t *offset
= start_of_entries
;
1263 for (uint16_t idx
= 0;
1264 idx
< baton
.compressed_second_level_page_header
.entryCount
; idx
++) {
1265 uint32_t entry
= *((uint32_t *)offset
);
1269 uint32_t entry_encoding_index
=
1270 UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry
);
1271 uint32_t entry_func_offset
=
1272 UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry
);
1274 if (entry_encoding_index
< baton
.unwind_header
.commonEncodingsArrayCount
) {
1275 // encoding is in common table in section header
1277 *((uint32_t *)(baton
.compact_unwind_start
+
1278 baton
.unwind_header
.commonEncodingsArraySectionOffset
+
1279 (entry_encoding_index
* sizeof(uint32_t))));
1281 // encoding is in page specific table
1282 uint32_t page_encoding_index
=
1283 entry_encoding_index
- baton
.unwind_header
.commonEncodingsArrayCount
;
1284 encoding
= *((uint32_t *)(this_index
+
1285 baton
.compressed_second_level_page_header
1286 .encodingsPageOffset
+
1287 (page_encoding_index
* sizeof(uint32_t))));
1290 print_function_encoding(baton
, idx
, encoding
, entry_encoding_index
,
1295 void print_second_level_index(struct baton baton
) {
1296 uint8_t *index_start
=
1297 baton
.compact_unwind_start
+
1298 baton
.first_level_index_entry
.secondLevelPagesSectionOffset
;
1300 if ((*(uint32_t *)index_start
) == UNWIND_SECOND_LEVEL_REGULAR
) {
1301 struct unwind_info_regular_second_level_page_header header
;
1302 memcpy(&header
, index_start
,
1303 sizeof(struct unwind_info_regular_second_level_page_header
));
1305 " UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n",
1306 baton
.current_index_table_number
, header
.entryPageOffset
,
1308 baton
.regular_second_level_page_header
= header
;
1309 print_second_level_index_regular(baton
);
1312 if ((*(uint32_t *)index_start
) == UNWIND_SECOND_LEVEL_COMPRESSED
) {
1313 struct unwind_info_compressed_second_level_page_header header
;
1314 memcpy(&header
, index_start
,
1315 sizeof(struct unwind_info_compressed_second_level_page_header
));
1316 printf(" UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, "
1317 "entryCount %d, encodingsPageOffset %d, encodingsCount %d\n",
1318 baton
.current_index_table_number
, header
.entryPageOffset
,
1319 header
.entryCount
, header
.encodingsPageOffset
,
1320 header
.encodingsCount
);
1321 baton
.compressed_second_level_page_header
= header
;
1322 print_second_level_index_compressed(baton
);
1326 void print_index_sections(struct baton baton
) {
1327 uint8_t *index_section_offset
=
1328 baton
.compact_unwind_start
+ baton
.unwind_header
.indexSectionOffset
;
1329 uint32_t index_count
= baton
.unwind_header
.indexCount
;
1331 uint32_t cur_idx
= 0;
1333 uint8_t *offset
= index_section_offset
;
1334 while (cur_idx
< index_count
) {
1335 baton
.current_index_table_number
= cur_idx
;
1336 struct unwind_info_section_header_index_entry index_entry
;
1337 memcpy(&index_entry
, offset
,
1338 sizeof(struct unwind_info_section_header_index_entry
));
1339 printf("index section #%d: functionOffset %d, "
1340 "secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n",
1341 cur_idx
, index_entry
.functionOffset
,
1342 index_entry
.secondLevelPagesSectionOffset
,
1343 index_entry
.lsdaIndexArraySectionOffset
);
1345 // secondLevelPagesSectionOffset == 0 means this is a sentinel entry
1346 if (index_entry
.secondLevelPagesSectionOffset
!= 0) {
1347 struct unwind_info_section_header_index_entry next_index_entry
;
1348 memcpy(&next_index_entry
,
1349 offset
+ sizeof(struct unwind_info_section_header_index_entry
),
1350 sizeof(struct unwind_info_section_header_index_entry
));
1352 baton
.lsda_array_start
=
1353 baton
.compact_unwind_start
+ index_entry
.lsdaIndexArraySectionOffset
;
1354 baton
.lsda_array_end
= baton
.compact_unwind_start
+
1355 next_index_entry
.lsdaIndexArraySectionOffset
;
1357 uint8_t *lsda_entry_offset
= baton
.lsda_array_start
;
1358 uint32_t lsda_count
= 0;
1359 while (lsda_entry_offset
< baton
.lsda_array_end
) {
1360 struct unwind_info_section_header_lsda_index_entry lsda_entry
;
1361 memcpy(&lsda_entry
, lsda_entry_offset
,
1362 sizeof(struct unwind_info_section_header_lsda_index_entry
));
1363 uint64_t function_file_address
=
1364 baton
.first_level_index_entry
.functionOffset
+
1365 lsda_entry
.functionOffset
+ baton
.text_segment_vmaddr
;
1366 uint64_t lsda_file_address
=
1367 lsda_entry
.lsdaOffset
+ baton
.text_segment_vmaddr
;
1368 printf(" LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx64
1369 "), lsdaOffset %d (file address 0x%" PRIx64
")\n",
1370 lsda_count
, lsda_entry
.functionOffset
,
1371 lsda_entry
.functionOffset
- index_entry
.functionOffset
,
1372 function_file_address
, lsda_entry
.lsdaOffset
, lsda_file_address
);
1374 lsda_entry_offset
+=
1375 sizeof(struct unwind_info_section_header_lsda_index_entry
);
1380 baton
.first_level_index_entry
= index_entry
;
1381 print_second_level_index(baton
);
1387 offset
+= sizeof(struct unwind_info_section_header_index_entry
);
1391 int main(int argc
, char **argv
) {
1393 char *file
= argv
[0];
1396 int fd
= open(file
, O_RDONLY
);
1398 printf("Failed to open '%s'\n", file
);
1403 (uint8_t *)mmap(0, st
.st_size
, PROT_READ
, MAP_PRIVATE
| MAP_FILE
, fd
, 0);
1404 if (file_mem
== MAP_FAILED
) {
1405 printf("Failed to mmap() '%s'\n", file
);
1408 FILE *f
= fopen("a.out", "r");
1411 baton
.mach_header_start
= file_mem
;
1412 baton
.symbols
= NULL
;
1413 baton
.symbols_count
= 0;
1414 baton
.function_start_addresses
= NULL
;
1415 baton
.function_start_addresses_count
= 0;
1417 scan_macho_load_commands(&baton
);
1419 if (baton
.compact_unwind_start
== NULL
) {
1420 printf("could not find __TEXT,__unwind_info section\n");
1424 struct unwind_info_section_header header
;
1425 memcpy(&header
, baton
.compact_unwind_start
,
1426 sizeof(struct unwind_info_section_header
));
1427 printf("Header:\n");
1428 printf(" version %u\n", header
.version
);
1429 printf(" commonEncodingsArraySectionOffset is %d\n",
1430 header
.commonEncodingsArraySectionOffset
);
1431 printf(" commonEncodingsArrayCount is %d\n",
1432 header
.commonEncodingsArrayCount
);
1433 printf(" personalityArraySectionOffset is %d\n",
1434 header
.personalityArraySectionOffset
);
1435 printf(" personalityArrayCount is %d\n", header
.personalityArrayCount
);
1436 printf(" indexSectionOffset is %d\n", header
.indexSectionOffset
);
1437 printf(" indexCount is %d\n", header
.indexCount
);
1439 uint8_t *common_encodings
=
1440 baton
.compact_unwind_start
+ header
.commonEncodingsArraySectionOffset
;
1441 uint32_t encoding_idx
= 0;
1442 while (encoding_idx
< header
.commonEncodingsArrayCount
) {
1443 uint32_t encoding
= *((uint32_t *)common_encodings
);
1444 printf(" Common Encoding [%d]: 0x%x ", encoding_idx
, encoding
);
1445 print_encoding(baton
, NULL
, encoding
);
1447 common_encodings
+= sizeof(uint32_t);
1452 baton
.compact_unwind_start
+ header
.personalityArraySectionOffset
;
1453 uint32_t pers_idx
= 0;
1454 while (pers_idx
< header
.personalityArrayCount
) {
1455 int32_t pers_delta
= *((int32_t *)(baton
.compact_unwind_start
+
1456 header
.personalityArraySectionOffset
+
1457 (pers_idx
* sizeof(uint32_t))));
1458 printf(" Personality [%d]: personality function ptr @ offset %d (file "
1459 "address 0x%" PRIx64
")\n",
1460 pers_idx
, pers_delta
, baton
.text_segment_vmaddr
+ pers_delta
);
1462 pers_arr
+= sizeof(uint32_t);
1467 baton
.unwind_header
= header
;
1469 print_index_sections(baton
);