1#include <fcntl.h>
2#include <inttypes.h>
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>
7#include <stdbool.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/errno.h>
13#include <sys/mman.h>
14#include <sys/stat.h>
15#include <sys/types.h>
16
17#define EXTRACT_BITS(value, mask) \
18 ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
19
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
24// correctly.
25
26struct symbol {
27 uint64_t file_address;
28 const char *name;
29};
30
31int symbol_compare(const void *a, const void *b) {
32 return (int)((struct symbol *)a)->file_address -
33 ((struct symbol *)b)->file_address;
34}
35
36struct baton {
37 cpu_type_t cputype;
38
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
41
42 int addr_size; // 4 or 8 bytes, the size of addresses in this file
43
44 uint64_t text_segment_vmaddr; // __TEXT segment vmaddr
45 uint64_t text_segment_file_offset;
46
47 uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr
48 uint64_t text_section_file_offset;
49
50 uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame
51 // section
52
53 uint8_t
54 *lsda_array_start; // for the currently-being-processed first-level index
55 uint8_t
56 *lsda_array_end; // the lsda_array_start for the NEXT first-level index
57
58 struct symbol *symbols;
59 int symbols_count;
60
61 uint64_t *function_start_addresses;
62 int function_start_addresses_count;
63
64 int current_index_table_number;
65
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;
72};
73
74uint64_t read_leb128(uint8_t **offset) {
75 uint64_t result = 0;
76 int shift = 0;
77 while (1) {
78 uint8_t byte = **offset;
79 *offset = *offset + 1;
80 result |= (byte & 0x7f) << shift;
81 if ((byte & 0x80) == 0)
82 break;
83 shift += 7;
84 }
85
86 return result;
87}
88
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.
92
93static 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;
97
98 baton->compact_unwind_start = 0;
99
100 uint32_t *magic = (uint32_t *)baton->mach_header_start;
101
102 if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) {
103 printf(format: "Unexpected magic number 0x%x in header, exiting.", *magic);
104 exit(status: 1);
105 }
106
107 bool is_64bit = false;
108 if (*magic == MH_MAGIC_64)
109 is_64bit = true;
110
111 uint8_t *offset = baton->mach_header_start;
112
113 struct mach_header mh;
114 memcpy(&mh, offset, sizeof(struct mach_header));
115 if (is_64bit)
116 offset += sizeof(struct mach_header_64);
117 else
118 offset += sizeof(struct mach_header);
119
120 if (is_64bit)
121 baton->addr_size = 8;
122 else
123 baton->addr_size = 4;
124
125 baton->cputype = mh.cputype;
126
127 uint8_t *start_of_load_commands = offset;
128
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;
136
137 if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) {
138 char segment_name[17];
139 segment_name[0] = '\0';
140 uint32_t nsects = 0;
141 uint64_t segment_offset = 0;
142 uint64_t segment_vmaddr = 0;
143
144 if (*lc_cmd == LC_SEGMENT_64) {
145 struct segment_command_64 seg;
146 memcpy(&seg, offset, sizeof(struct segment_command_64));
147 memcpy(dest: &segment_name, src: &seg.segname, n: 16);
148 segment_name[16] = '\0';
149 nsects = seg.nsects;
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(format: "Segment '%s' is encrypted.\n", segment_name);
155 }
156 }
157
158 if (*lc_cmd == LC_SEGMENT) {
159 struct segment_command seg;
160 memcpy(&seg, offset, sizeof(struct segment_command));
161 memcpy(dest: &segment_name, src: &seg.segname, n: 16);
162 segment_name[16] = '\0';
163 nsects = seg.nsects;
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(format: "Segment '%s' is encrypted.\n", segment_name);
169 }
170 }
171
172 if (nsects != 0 && strcmp(s1: segment_name, s2: "__TEXT") == 0) {
173 baton->text_segment_vmaddr = segment_vmaddr;
174 baton->text_segment_file_offset = segment_offset;
175
176 uint32_t current_sect = 0;
177 while (current_sect < nsects &&
178 (offset - start_of_this_load_cmd) < *lc_cmdsize) {
179 char sect_name[17];
180 memcpy(dest: &sect_name, src: offset, n: 16);
181 sect_name[16] = '\0';
182 if (strcmp(s1: sect_name, s2: "__unwind_info") == 0) {
183 if (is_64bit) {
184 struct section_64 sect;
185 memset(&sect, 0, sizeof(struct section_64));
186 memcpy(&sect, offset, sizeof(struct section_64));
187 baton->compact_unwind_start =
188 baton->mach_header_start + sect.offset;
189 } else {
190 struct section sect;
191 memset(&sect, 0, sizeof(struct section));
192 memcpy(&sect, offset, sizeof(struct section));
193 baton->compact_unwind_start =
194 baton->mach_header_start + sect.offset;
195 }
196 }
197 if (strcmp(s1: sect_name, s2: "__eh_frame") == 0) {
198 if (is_64bit) {
199 struct section_64 sect;
200 memset(&sect, 0, sizeof(struct section_64));
201 memcpy(&sect, offset, sizeof(struct section_64));
202 baton->eh_section_file_address = sect.addr;
203 } else {
204 struct section sect;
205 memset(&sect, 0, sizeof(struct section));
206 memcpy(&sect, offset, sizeof(struct section));
207 baton->eh_section_file_address = sect.addr;
208 }
209 }
210 if (strcmp(s1: sect_name, s2: "__text") == 0) {
211 if (is_64bit) {
212 struct section_64 sect;
213 memset(&sect, 0, sizeof(struct section_64));
214 memcpy(&sect, offset, sizeof(struct section_64));
215 baton->text_section_vmaddr = sect.addr;
216 baton->text_section_file_offset = sect.offset;
217 } else {
218 struct section sect;
219 memset(&sect, 0, sizeof(struct section));
220 memcpy(&sect, offset, sizeof(struct section));
221 baton->text_section_vmaddr = sect.addr;
222 }
223 }
224 if (is_64bit) {
225 offset += sizeof(struct section_64);
226 } else {
227 offset += sizeof(struct section);
228 }
229 }
230 }
231
232 if (strcmp(s1: segment_name, s2: "__LINKEDIT") == 0) {
233 linkedit_segment_vmaddr = segment_vmaddr;
234 linkedit_segment_file_offset = segment_offset;
235 }
236 }
237
238 if (*lc_cmd == LC_SYMTAB) {
239 memcpy(&symtab_cmd, offset, sizeof(struct symtab_command));
240 }
241
242 if (*lc_cmd == LC_DYSYMTAB) {
243 struct dysymtab_command dysymtab_cmd;
244 memcpy(&dysymtab_cmd, offset, sizeof(struct dysymtab_command));
245
246 int nlist_size = 12;
247 if (is_64bit)
248 nlist_size = 16;
249
250 char *string_table =
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;
258
259 // We're only going to create records for a small number of these symbols
260 // but to
261 // simplify the memory management I'll allocate enough space to store all
262 // of them.
263 baton->symbols = (struct symbol *)malloc(
264 size: sizeof(struct symbol) * (local_syms_count + exported_syms_count));
265 baton->symbols_count = 0;
266
267 for (int i = 0; i < local_syms_count; i++) {
268 struct nlist_64 nlist;
269 memset(&nlist, 0, sizeof(struct nlist_64));
270 if (is_64bit) {
271 memcpy(&nlist, local_syms + (i * nlist_size),
272 sizeof(struct nlist_64));
273 } else {
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;
283 }
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++;
295 }
296 }
297
298 for (int i = 0; i < exported_syms_count; i++) {
299 struct nlist_64 nlist;
300 memset(&nlist, 0, sizeof(struct nlist_64));
301 if (is_64bit) {
302 memcpy(&nlist, exported_syms + (i * nlist_size),
303 sizeof(struct nlist_64));
304 } else {
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;
313 }
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++;
325 }
326 }
327
328 qsort(base: baton->symbols, nmemb: baton->symbols_count, size: sizeof(struct symbol),
329 compar: symbol_compare);
330 }
331
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));
336
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;
340 int count = 0;
341
342 while (funcstarts_offset < function_end) {
343 if (read_leb128(offset: &funcstarts_offset) != 0) {
344 count++;
345 }
346 }
347
348 baton->function_start_addresses =
349 (uint64_t *)malloc(size: sizeof(uint64_t) * count);
350 baton->function_start_addresses_count = count;
351
352 funcstarts_offset =
353 baton->mach_header_start + function_starts_cmd.dataoff;
354 uint64_t current_pc = baton->text_segment_vmaddr;
355 int i = 0;
356 while (funcstarts_offset < function_end) {
357 uint64_t func_start = read_leb128(offset: &funcstarts_offset);
358 if (func_start != 0) {
359 current_pc += func_start;
360 baton->function_start_addresses[i++] = current_pc;
361 }
362 }
363 }
364
365 offset = start_of_this_load_cmd + *lc_cmdsize;
366 cur_cmd++;
367 }
368
369 // Augment the symbol table with the function starts table -- adding symbol
370 // entries
371 // for functions that were stripped.
372
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;
379 struct symbol *sym =
380 bsearch(key: &search_key, base: baton->symbols, nmemb: baton->symbols_count,
381 size: sizeof(struct symbol), compar: symbol_compare);
382 if (sym == NULL)
383 unnamed_functions_to_add++;
384 }
385
386 baton->symbols = (struct symbol *)realloc(
387 ptr: baton->symbols, size: sizeof(struct symbol) *
388 (baton->symbols_count + unnamed_functions_to_add));
389
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;
397 struct symbol *sym =
398 bsearch(key: &search_key, base: baton->symbols, nmemb: baton->symbols_count,
399 size: sizeof(struct symbol), compar: symbol_compare);
400 if (sym == NULL) {
401 char *name;
402 asprintf(ptr: &name, fmt: "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++;
407 }
408 }
409 baton->symbols_count += number_symbols_added;
410 qsort(base: baton->symbols, nmemb: baton->symbols_count, size: sizeof(struct symbol),
411 compar: symbol_compare);
412
413 // printf ("function start addresses\n");
414 // for (int i = 0; i < baton->function_start_addresses_count; i++)
415 // {
416 // printf ("0x%012llx\n", baton->function_start_addresses[i]);
417 // }
418
419 // printf ("symbol table names & addresses\n");
420 // for (int i = 0; i < baton->symbols_count; i++)
421 // {
422 // printf ("0x%012llx %s\n", baton->symbols[i].file_address,
423 // baton->symbols[i].name);
424 // }
425}
426
427void print_encoding_x86_64(struct baton baton, uint8_t *function_start,
428 uint32_t encoding) {
429 int mode = encoding & UNWIND_X86_64_MODE_MASK;
430 switch (mode) {
431 case UNWIND_X86_64_MODE_RBP_FRAME: {
432 printf(format: "frame func: CFA is rbp+%d ", 16);
433 printf(format: " rip=[CFA-8] rbp=[CFA-16]");
434 uint32_t saved_registers_offset =
435 EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
436
437 uint32_t saved_registers_locations =
438 EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
439
440 saved_registers_offset += 2;
441
442 for (int i = 0; i < 5; i++) {
443 switch (saved_registers_locations & 0x7) {
444 case UNWIND_X86_64_REG_NONE:
445 break;
446 case UNWIND_X86_64_REG_RBX:
447 printf(format: " rbx=[CFA-%d]", saved_registers_offset * 8);
448 break;
449 case UNWIND_X86_64_REG_R12:
450 printf(format: " r12=[CFA-%d]", saved_registers_offset * 8);
451 break;
452 case UNWIND_X86_64_REG_R13:
453 printf(format: " r13=[CFA-%d]", saved_registers_offset * 8);
454 break;
455 case UNWIND_X86_64_REG_R14:
456 printf(format: " r14=[CFA-%d]", saved_registers_offset * 8);
457 break;
458 case UNWIND_X86_64_REG_R15:
459 printf(format: " r15=[CFA-%d]", saved_registers_offset * 8);
460 break;
461 }
462 saved_registers_offset--;
463 saved_registers_locations >>= 3;
464 }
465 } break;
466
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);
475
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);
479
480 // offset into the function instructions; 0 == beginning of first
481 // instruction
482 uint32_t offset_to_subl_insn =
483 EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
484
485 stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));
486
487 stack_size += stack_adjust * 8;
488
489 printf(format: "large stack ");
490 }
491
492 if (mode == UNWIND_X86_64_MODE_STACK_IND) {
493 printf(format: "frameless function: stack size %d, register count %d ",
494 stack_size * 8, register_count);
495 } else {
496 printf(format: "frameless function: stack size %d, register count %d ",
497 stack_size, register_count);
498 }
499
500 if (register_count == 0) {
501 printf(format: " no registers saved");
502 } else {
503
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.
507 //
508 // This is done with Lehmer code permutation, e.g. see
509 // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
510 int permunreg[6];
511
512 // This decodes the variable-base number in the 10 bits
513 // and gives us the Lehmer code sequence which can then
514 // be decoded.
515
516 switch (register_count) {
517 case 6:
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!
527 permunreg[5] = 0;
528 break;
529 case 5:
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;
539 break;
540 case 4:
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;
548 break;
549 case 3:
550 permunreg[0] = permutation / 20;
551 permutation -= (permunreg[0] * 20);
552 permunreg[1] = permutation / 4;
553 permutation -= (permunreg[1] * 4);
554 permunreg[2] = permutation;
555 break;
556 case 2:
557 permunreg[0] = permutation / 5;
558 permutation -= (permunreg[0] * 5);
559 permunreg[1] = permutation;
560 break;
561 case 1:
562 permunreg[0] = permutation;
563 break;
564 }
565
566 // Decode the Lehmer code for this permutation of
567 // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
568
569 int registers[6];
570 bool used[7] = {false, false, false, false, false, false, false};
571 for (int i = 0; i < register_count; i++) {
572 int renum = 0;
573 for (int j = 1; j < 7; j++) {
574 if (used[j] == false) {
575 if (renum == permunreg[i]) {
576 registers[i] = j;
577 used[j] = true;
578 break;
579 }
580 renum++;
581 }
582 }
583 }
584
585 if (mode == UNWIND_X86_64_MODE_STACK_IND) {
586 printf(format: " CFA is rsp+%d ", stack_size);
587 } else {
588 printf(format: " CFA is rsp+%d ", stack_size * 8);
589 }
590
591 uint32_t saved_registers_offset = 1;
592 printf(format: " rip=[CFA-%d]", saved_registers_offset * 8);
593 saved_registers_offset++;
594
595 for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
596 switch (registers[i]) {
597 case UNWIND_X86_64_REG_NONE:
598 break;
599 case UNWIND_X86_64_REG_RBX:
600 printf(format: " rbx=[CFA-%d]", saved_registers_offset * 8);
601 saved_registers_offset++;
602 break;
603 case UNWIND_X86_64_REG_R12:
604 printf(format: " r12=[CFA-%d]", saved_registers_offset * 8);
605 saved_registers_offset++;
606 break;
607 case UNWIND_X86_64_REG_R13:
608 printf(format: " r13=[CFA-%d]", saved_registers_offset * 8);
609 saved_registers_offset++;
610 break;
611 case UNWIND_X86_64_REG_R14:
612 printf(format: " r14=[CFA-%d]", saved_registers_offset * 8);
613 saved_registers_offset++;
614 break;
615 case UNWIND_X86_64_REG_R15:
616 printf(format: " r15=[CFA-%d]", saved_registers_offset * 8);
617 saved_registers_offset++;
618 break;
619 case UNWIND_X86_64_REG_RBP:
620 printf(format: " rbp=[CFA-%d]", saved_registers_offset * 8);
621 saved_registers_offset++;
622 break;
623 }
624 }
625 }
626
627 } break;
628
629 case UNWIND_X86_64_MODE_DWARF: {
630 uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;
631 printf(
632 format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
633 ")",
634 dwarf_offset, dwarf_offset + baton.eh_section_file_address);
635 } break;
636
637 case 0: {
638 printf(format: " no unwind information");
639 } break;
640 }
641}
642
643void print_encoding_i386(struct baton baton, uint8_t *function_start,
644 uint32_t encoding) {
645 int mode = encoding & UNWIND_X86_MODE_MASK;
646 switch (mode) {
647 case UNWIND_X86_MODE_EBP_FRAME: {
648 printf(format: "frame func: CFA is ebp+%d ", 8);
649 printf(format: " eip=[CFA-4] ebp=[CFA-8]");
650 uint32_t saved_registers_offset =
651 EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);
652
653 uint32_t saved_registers_locations =
654 EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
655
656 saved_registers_offset += 2;
657
658 for (int i = 0; i < 5; i++) {
659 switch (saved_registers_locations & 0x7) {
660 case UNWIND_X86_REG_NONE:
661 break;
662 case UNWIND_X86_REG_EBX:
663 printf(format: " ebx=[CFA-%d]", saved_registers_offset * 4);
664 break;
665 case UNWIND_X86_REG_ECX:
666 printf(format: " ecx=[CFA-%d]", saved_registers_offset * 4);
667 break;
668 case UNWIND_X86_REG_EDX:
669 printf(format: " edx=[CFA-%d]", saved_registers_offset * 4);
670 break;
671 case UNWIND_X86_REG_EDI:
672 printf(format: " edi=[CFA-%d]", saved_registers_offset * 4);
673 break;
674 case UNWIND_X86_REG_ESI:
675 printf(format: " esi=[CFA-%d]", saved_registers_offset * 4);
676 break;
677 }
678 saved_registers_offset--;
679 saved_registers_locations >>= 3;
680 }
681 } break;
682
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);
691
692 if (mode == UNWIND_X86_MODE_STACK_IND && function_start) {
693 uint32_t stack_adjust =
694 EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
695
696 // offset into the function instructions; 0 == beginning of first
697 // instruction
698 uint32_t offset_to_subl_insn =
699 EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
700
701 stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));
702
703 stack_size += stack_adjust * 4;
704
705 printf(format: "large stack ");
706 }
707
708 if (mode == UNWIND_X86_MODE_STACK_IND) {
709 printf(format: "frameless function: stack size %d, register count %d ",
710 stack_size, register_count);
711 } else {
712 printf(format: "frameless function: stack size %d, register count %d ",
713 stack_size * 4, register_count);
714 }
715
716 if (register_count == 0) {
717 printf(format: " no registers saved");
718 } else {
719
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.
723 //
724 // This is done with Lehmer code permutation, e.g. see
725 // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
726 int permunreg[6];
727
728 // This decodes the variable-base number in the 10 bits
729 // and gives us the Lehmer code sequence which can then
730 // be decoded.
731
732 switch (register_count) {
733 case 6:
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!
743 permunreg[5] = 0;
744 break;
745 case 5:
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;
755 break;
756 case 4:
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;
764 break;
765 case 3:
766 permunreg[0] = permutation / 20;
767 permutation -= (permunreg[0] * 20);
768 permunreg[1] = permutation / 4;
769 permutation -= (permunreg[1] * 4);
770 permunreg[2] = permutation;
771 break;
772 case 2:
773 permunreg[0] = permutation / 5;
774 permutation -= (permunreg[0] * 5);
775 permunreg[1] = permutation;
776 break;
777 case 1:
778 permunreg[0] = permutation;
779 break;
780 }
781
782 // Decode the Lehmer code for this permutation of
783 // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
784
785 int registers[6];
786 bool used[7] = {false, false, false, false, false, false, false};
787 for (int i = 0; i < register_count; i++) {
788 int renum = 0;
789 for (int j = 1; j < 7; j++) {
790 if (used[j] == false) {
791 if (renum == permunreg[i]) {
792 registers[i] = j;
793 used[j] = true;
794 break;
795 }
796 renum++;
797 }
798 }
799 }
800
801 if (mode == UNWIND_X86_MODE_STACK_IND) {
802 printf(format: " CFA is esp+%d ", stack_size);
803 } else {
804 printf(format: " CFA is esp+%d ", stack_size * 4);
805 }
806
807 uint32_t saved_registers_offset = 1;
808 printf(format: " eip=[CFA-%d]", saved_registers_offset * 4);
809 saved_registers_offset++;
810
811 for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
812 switch (registers[i]) {
813 case UNWIND_X86_REG_NONE:
814 break;
815 case UNWIND_X86_REG_EBX:
816 printf(format: " ebx=[CFA-%d]", saved_registers_offset * 4);
817 saved_registers_offset++;
818 break;
819 case UNWIND_X86_REG_ECX:
820 printf(format: " ecx=[CFA-%d]", saved_registers_offset * 4);
821 saved_registers_offset++;
822 break;
823 case UNWIND_X86_REG_EDX:
824 printf(format: " edx=[CFA-%d]", saved_registers_offset * 4);
825 saved_registers_offset++;
826 break;
827 case UNWIND_X86_REG_EDI:
828 printf(format: " edi=[CFA-%d]", saved_registers_offset * 4);
829 saved_registers_offset++;
830 break;
831 case UNWIND_X86_REG_ESI:
832 printf(format: " esi=[CFA-%d]", saved_registers_offset * 4);
833 saved_registers_offset++;
834 break;
835 case UNWIND_X86_REG_EBP:
836 printf(format: " ebp=[CFA-%d]", saved_registers_offset * 4);
837 saved_registers_offset++;
838 break;
839 }
840 }
841 }
842
843 } break;
844
845 case UNWIND_X86_MODE_DWARF: {
846 uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;
847 printf(
848 format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
849 ")",
850 dwarf_offset, dwarf_offset + baton.eh_section_file_address);
851 } break;
852
853 case 0: {
854 printf(format: " no unwind information");
855 } break;
856 }
857}
858
859void print_encoding_arm64(struct baton baton, uint8_t *function_start,
860 uint32_t encoding) {
861 const int wordsize = 8;
862 int mode = encoding & UNWIND_ARM64_MODE_MASK;
863 switch (mode) {
864 case UNWIND_ARM64_MODE_FRAME: {
865 printf(format: "frame func: CFA is fp+%d ", 16);
866 printf(format: " 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(format: " x19=[CFA%d]", cfa_offset);
873 cfa_offset -= wordsize;
874 printf(format: " x20=[CFA%d]", cfa_offset);
875 reg_pairs_saved_count++;
876 }
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(format: " x21=[CFA%d]", cfa_offset);
881 cfa_offset -= wordsize;
882 printf(format: " x22=[CFA%d]", cfa_offset);
883 reg_pairs_saved_count++;
884 }
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(format: " x23=[CFA%d]", cfa_offset);
889 cfa_offset -= wordsize;
890 printf(format: " x24=[CFA%d]", cfa_offset);
891 reg_pairs_saved_count++;
892 }
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(format: " x25=[CFA%d]", cfa_offset);
897 cfa_offset -= wordsize;
898 printf(format: " x26=[CFA%d]", cfa_offset);
899 reg_pairs_saved_count++;
900 }
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(format: " x27=[CFA%d]", cfa_offset);
905 cfa_offset -= wordsize;
906 printf(format: " x28=[CFA%d]", cfa_offset);
907 reg_pairs_saved_count++;
908 }
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(format: " d8=[CFA%d]", cfa_offset);
913 cfa_offset -= wordsize;
914 printf(format: " d9=[CFA%d]", cfa_offset);
915 reg_pairs_saved_count++;
916 }
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(format: " d10=[CFA%d]", cfa_offset);
921 cfa_offset -= wordsize;
922 printf(format: " d11=[CFA%d]", cfa_offset);
923 reg_pairs_saved_count++;
924 }
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(format: " d12=[CFA%d]", cfa_offset);
929 cfa_offset -= wordsize;
930 printf(format: " d13=[CFA%d]", cfa_offset);
931 reg_pairs_saved_count++;
932 }
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(format: " d14=[CFA%d]", cfa_offset);
937 cfa_offset -= wordsize;
938 printf(format: " d15=[CFA%d]", cfa_offset);
939 reg_pairs_saved_count++;
940 }
941
942 } break;
943
944 case UNWIND_ARM64_MODE_FRAMELESS: {
945 uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK;
946 printf(format: "frameless function: stack size %d ", stack_size * 16);
947
948 } break;
949
950 case UNWIND_ARM64_MODE_DWARF: {
951 uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET;
952 printf(
953 format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
954 ")",
955 dwarf_offset, dwarf_offset + baton.eh_section_file_address);
956 } break;
957
958 case 0: {
959 printf(format: " no unwind information");
960 } break;
961 }
962}
963
964void print_encoding_armv7(struct baton baton, uint8_t *function_start,
965 uint32_t encoding) {
966 const int wordsize = 4;
967 int mode = encoding & UNWIND_ARM_MODE_MASK;
968 switch (mode) {
969 case UNWIND_ARM_MODE_FRAME_D:
970 case UNWIND_ARM_MODE_FRAME: {
971 int stack_adjust =
972 EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize;
973
974 printf(format: "frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust);
975 int cfa_offset = -stack_adjust;
976
977 cfa_offset -= wordsize;
978 printf(format: " pc=[CFA%d]", cfa_offset);
979 cfa_offset -= wordsize;
980 printf(format: " fp=[CFA%d]", cfa_offset);
981
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(format: " r6=[CFA%d]", cfa_offset);
986 }
987 if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) {
988 cfa_offset -= wordsize;
989 printf(format: " r5=[CFA%d]", cfa_offset);
990 }
991 if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) {
992 cfa_offset -= wordsize;
993 printf(format: " r4=[CFA%d]", cfa_offset);
994 }
995 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) {
996 cfa_offset -= wordsize;
997 printf(format: " r12=[CFA%d]", cfa_offset);
998 }
999 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) {
1000 cfa_offset -= wordsize;
1001 printf(format: " r11=[CFA%d]", cfa_offset);
1002 }
1003 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) {
1004 cfa_offset -= wordsize;
1005 printf(format: " r10=[CFA%d]", cfa_offset);
1006 }
1007 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) {
1008 cfa_offset -= wordsize;
1009 printf(format: " r9=[CFA%d]", cfa_offset);
1010 }
1011 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) {
1012 cfa_offset -= wordsize;
1013 printf(format: " r8=[CFA%d]", cfa_offset);
1014 }
1015
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) {
1020 case 0:
1021 // vpush {d8}
1022 cfa_offset -= 8;
1023 printf(format: " d8=[CFA%d]", cfa_offset);
1024 break;
1025 case 1:
1026 // vpush {d10}
1027 // vpush {d8}
1028 cfa_offset -= 8;
1029 printf(format: " d10=[CFA%d]", cfa_offset);
1030 cfa_offset -= 8;
1031 printf(format: " d8=[CFA%d]", cfa_offset);
1032 break;
1033 case 2:
1034 // vpush {d12}
1035 // vpush {d10}
1036 // vpush {d8}
1037 cfa_offset -= 8;
1038 printf(format: " d12=[CFA%d]", cfa_offset);
1039 cfa_offset -= 8;
1040 printf(format: " d10=[CFA%d]", cfa_offset);
1041 cfa_offset -= 8;
1042 printf(format: " d8=[CFA%d]", cfa_offset);
1043 break;
1044 case 3:
1045 // vpush {d14}
1046 // vpush {d12}
1047 // vpush {d10}
1048 // vpush {d8}
1049 cfa_offset -= 8;
1050 printf(format: " d14=[CFA%d]", cfa_offset);
1051 cfa_offset -= 8;
1052 printf(format: " d12=[CFA%d]", cfa_offset);
1053 cfa_offset -= 8;
1054 printf(format: " d10=[CFA%d]", cfa_offset);
1055 cfa_offset -= 8;
1056 printf(format: " d8=[CFA%d]", cfa_offset);
1057 break;
1058 case 4:
1059 // vpush {d14}
1060 // vpush {d12}
1061 // sp = (sp - 24) & (-16);
1062 // vst {d8, d9, d10}
1063 printf(format: " d14, d12, d10, d9, d8");
1064 break;
1065 case 5:
1066 // vpush {d14}
1067 // sp = (sp - 40) & (-16);
1068 // vst {d8, d9, d10, d11}
1069 // vst {d12}
1070 printf(format: " d14, d11, d10, d9, d8, d12");
1071 break;
1072 case 6:
1073 // sp = (sp - 56) & (-16);
1074 // vst {d8, d9, d10, d11}
1075 // vst {d12, d13, d14}
1076 printf(format: " d11, d10, d9, d8, d14, d13, d12");
1077 break;
1078 case 7:
1079 // sp = (sp - 64) & (-16);
1080 // vst {d8, d9, d10, d11}
1081 // vst {d12, d13, d14, d15}
1082 printf(format: " d11, d10, d9, d8, d15, d14, d13, d12");
1083 break;
1084 }
1085 }
1086 } break;
1087
1088 case UNWIND_ARM_MODE_DWARF: {
1089 uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET;
1090 printf(
1091 format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
1092 ")",
1093 dwarf_offset, dwarf_offset + baton.eh_section_file_address);
1094 } break;
1095
1096 case 0: {
1097 printf(format: " no unwind information");
1098 } break;
1099 }
1100}
1101
1102void print_encoding(struct baton baton, uint8_t *function_start,
1103 uint32_t encoding) {
1104
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);
1113 } else {
1114 printf(format: " -- unsupported encoding arch -- ");
1115 }
1116}
1117
1118void print_function_encoding(struct baton baton, uint32_t idx,
1119 uint32_t encoding, uint32_t entry_encoding_index,
1120 uint32_t entry_func_offset) {
1121
1122 char *entry_encoding_index_str = "";
1123 if (entry_encoding_index != (uint32_t)-1) {
1124 asprintf(ptr: &entry_encoding_index_str, fmt: ", encoding #%d", entry_encoding_index);
1125 } else {
1126 asprintf(ptr: &entry_encoding_index_str, fmt: "");
1127 }
1128
1129 uint64_t file_address = baton.first_level_index_entry.functionOffset +
1130 entry_func_offset + baton.text_segment_vmaddr;
1131
1132 if (baton.cputype == CPU_TYPE_ARM)
1133 file_address = file_address & ~1;
1134
1135 printf(
1136 format: " 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);
1138
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]);
1144 break;
1145 } else {
1146 if (baton.symbols[i].file_address <= file_address &&
1147 baton.symbols[i + 1].file_address > file_address) {
1148 symbol = &(baton.symbols[i]);
1149 break;
1150 }
1151 }
1152 }
1153
1154 printf(format: "\n ");
1155 if (symbol) {
1156 int offset = file_address - symbol->file_address;
1157
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
1161 // function bounds.
1162 if (offset >= 0) {
1163 printf(format: "name: %s", symbol->name);
1164 if (offset > 0) {
1165 printf(format: " + %d", offset);
1166 }
1167 }
1168 printf(format: "\n ");
1169 }
1170
1171 print_encoding(baton, function_start: baton.mach_header_start +
1172 baton.first_level_index_entry.functionOffset +
1173 baton.text_section_file_offset + entry_func_offset,
1174 encoding);
1175
1176 bool has_lsda = encoding & UNWIND_HAS_LSDA;
1177
1178 if (has_lsda) {
1179 uint32_t func_offset =
1180 entry_func_offset + baton.first_level_index_entry.functionOffset;
1181
1182 int lsda_entry_number = -1;
1183
1184 uint32_t low = 0;
1185 uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) /
1186 sizeof(struct unwind_info_section_header_lsda_index_entry);
1187
1188 while (low < high) {
1189 uint32_t mid = (low + high) / 2;
1190
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) {
1198 lsda_entry_number =
1199 (mid_lsda_entry_addr - baton.lsda_array_start) /
1200 sizeof(struct unwind_info_section_header_lsda_index_entry);
1201 break;
1202 } else if (mid_lsda_entry.functionOffset < func_offset) {
1203 low = mid + 1;
1204 } else {
1205 high = mid;
1206 }
1207 }
1208
1209 if (lsda_entry_number != -1) {
1210 printf(format: ", LSDA entry #%d", lsda_entry_number);
1211 } else {
1212 printf(format: ", LSDA entry not found");
1213 }
1214 }
1215
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(format: ", personality entry #%d", pers_idx);
1220 }
1221
1222 printf(format: "\n");
1223}
1224
1225void 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;
1231
1232 uint8_t *offset = page_entries;
1233
1234 uint32_t idx = 0;
1235 while (idx < entries_count) {
1236 uint32_t func_offset = *((uint32_t *)(offset));
1237 uint32_t encoding = *((uint32_t *)(offset + 4));
1238
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
1243 // functionOffset.
1244 // So strip off the containing index table functionOffset value here so they
1245 // can
1246 // be treated the same at the lower layers.
1247
1248 print_function_encoding(baton, idx, encoding, entry_encoding_index: (uint32_t)-1,
1249 entry_func_offset: func_offset -
1250 baton.first_level_index_entry.functionOffset);
1251 idx++;
1252 offset += 8;
1253 }
1254}
1255
1256void 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);
1266 offset += 4;
1267 uint32_t encoding;
1268
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);
1273
1274 if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) {
1275 // encoding is in common table in section header
1276 encoding =
1277 *((uint32_t *)(baton.compact_unwind_start +
1278 baton.unwind_header.commonEncodingsArraySectionOffset +
1279 (entry_encoding_index * sizeof(uint32_t))));
1280 } else {
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))));
1288 }
1289
1290 print_function_encoding(baton, idx, encoding, entry_encoding_index,
1291 entry_func_offset);
1292 }
1293}
1294
1295void print_second_level_index(struct baton baton) {
1296 uint8_t *index_start =
1297 baton.compact_unwind_start +
1298 baton.first_level_index_entry.secondLevelPagesSectionOffset;
1299
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));
1304 printf(
1305 format: " UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n",
1306 baton.current_index_table_number, header.entryPageOffset,
1307 header.entryCount);
1308 baton.regular_second_level_page_header = header;
1309 print_second_level_index_regular(baton);
1310 }
1311
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(format: " 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);
1323 }
1324}
1325
1326void 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;
1330
1331 uint32_t cur_idx = 0;
1332
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(format: "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);
1344
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));
1351
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;
1356
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(format: " 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);
1373 lsda_count++;
1374 lsda_entry_offset +=
1375 sizeof(struct unwind_info_section_header_lsda_index_entry);
1376 }
1377
1378 printf(format: "\n");
1379
1380 baton.first_level_index_entry = index_entry;
1381 print_second_level_index(baton);
1382 }
1383
1384 printf(format: "\n");
1385
1386 cur_idx++;
1387 offset += sizeof(struct unwind_info_section_header_index_entry);
1388 }
1389}
1390
1391int main(int argc, char **argv) {
1392 struct stat st;
1393 char *file = argv[0];
1394 if (argc > 1)
1395 file = argv[1];
1396 int fd = open(file: file, O_RDONLY);
1397 if (fd == -1) {
1398 printf(format: "Failed to open '%s'\n", file);
1399 exit(status: 1);
1400 }
1401 fstat(fd: fd, buf: &st);
1402 uint8_t *file_mem =
1403 (uint8_t *)mmap(addr: 0, len: st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd: fd, offset: 0);
1404 if (file_mem == MAP_FAILED) {
1405 printf(format: "Failed to mmap() '%s'\n", file);
1406 }
1407
1408 FILE *f = fopen(filename: "a.out", modes: "r");
1409
1410 struct baton baton;
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;
1416
1417 scan_macho_load_commands(baton: &baton);
1418
1419 if (baton.compact_unwind_start == NULL) {
1420 printf(format: "could not find __TEXT,__unwind_info section\n");
1421 exit(status: 1);
1422 }
1423
1424 struct unwind_info_section_header header;
1425 memcpy(&header, baton.compact_unwind_start,
1426 sizeof(struct unwind_info_section_header));
1427 printf(format: "Header:\n");
1428 printf(format: " version %u\n", header.version);
1429 printf(format: " commonEncodingsArraySectionOffset is %d\n",
1430 header.commonEncodingsArraySectionOffset);
1431 printf(format: " commonEncodingsArrayCount is %d\n",
1432 header.commonEncodingsArrayCount);
1433 printf(format: " personalityArraySectionOffset is %d\n",
1434 header.personalityArraySectionOffset);
1435 printf(format: " personalityArrayCount is %d\n", header.personalityArrayCount);
1436 printf(format: " indexSectionOffset is %d\n", header.indexSectionOffset);
1437 printf(format: " indexCount is %d\n", header.indexCount);
1438
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(format: " Common Encoding [%d]: 0x%x ", encoding_idx, encoding);
1445 print_encoding(baton, NULL, encoding);
1446 printf(format: "\n");
1447 common_encodings += sizeof(uint32_t);
1448 encoding_idx++;
1449 }
1450
1451 uint8_t *pers_arr =
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(format: " Personality [%d]: personality function ptr @ offset %d (file "
1459 "address 0x%" PRIx64 ")\n",
1460 pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta);
1461 pers_idx++;
1462 pers_arr += sizeof(uint32_t);
1463 }
1464
1465 printf(format: "\n");
1466
1467 baton.unwind_header = header;
1468
1469 print_index_sections(baton);
1470
1471 return 0;
1472}
1473

source code of lldb/tools/compact-unwind/compact-unwind-dumper.c