1473 lines · c
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 info21// used on Darwin systems for exception handling. The output of22// unwinddump will be more authoritative/reliable but this program23// can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries24// 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 space40 uint8_t *compact_unwind_start; // pointer into this program's address space41 42 int addr_size; // 4 or 8 bytes, the size of addresses in this file43 44 uint64_t text_segment_vmaddr; // __TEXT segment vmaddr45 uint64_t text_segment_file_offset;46 47 uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr48 uint64_t text_section_file_offset;49 50 uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame51 // section52 53 uint8_t54 *lsda_array_start; // for the currently-being-processed first-level index55 uint8_t56 *lsda_array_end; // the lsda_array_start for the NEXT first-level index57 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_header69 compressed_second_level_page_header;70 struct unwind_info_regular_second_level_page_header71 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_info91// 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("Unexpected magic number 0x%x in header, exiting.", *magic);104 exit(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 else118 offset += sizeof(struct mach_header);119 120 if (is_64bit)121 baton->addr_size = 8;122 else123 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(&segment_name, &seg.segname, 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("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(&segment_name, &seg.segname, 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("Segment '%s' is encrypted.\n", segment_name);169 }170 }171 172 if (nsects != 0 && strcmp(segment_name, "__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(§_name, offset, 16);181 sect_name[16] = '\0';182 if (strcmp(sect_name, "__unwind_info") == 0) {183 if (is_64bit) {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;189 } else {190 struct section sect;191 memset(§, 0, sizeof(struct section));192 memcpy(§, offset, sizeof(struct section));193 baton->compact_unwind_start =194 baton->mach_header_start + sect.offset;195 }196 }197 if (strcmp(sect_name, "__eh_frame") == 0) {198 if (is_64bit) {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;203 } else {204 struct section sect;205 memset(§, 0, sizeof(struct section));206 memcpy(§, offset, sizeof(struct section));207 baton->eh_section_file_address = sect.addr;208 }209 }210 if (strcmp(sect_name, "__text") == 0) {211 if (is_64bit) {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;217 } else {218 struct section sect;219 memset(§, 0, sizeof(struct section));220 memcpy(§, 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(segment_name, "__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 symbols260 // but to261 // simplify the memory management I'll allocate enough space to store all262 // of them.263 baton->symbols = (struct symbol *)malloc(264 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(baton->symbols, baton->symbols_count, sizeof(struct symbol),329 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(&funcstarts_offset) != 0) {344 count++;345 }346 }347 348 baton->function_start_addresses =349 (uint64_t *)malloc(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(&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 symbol370 // entries371 // 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(&search_key, baton->symbols, baton->symbols_count,381 sizeof(struct symbol), symbol_compare);382 if (sym == NULL)383 unnamed_functions_to_add++;384 }385 386 baton->symbols = (struct symbol *)realloc(387 baton->symbols, 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(&search_key, baton->symbols, baton->symbols_count,399 sizeof(struct symbol), symbol_compare);400 if (sym == NULL) {401 char *name;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++;407 }408 }409 baton->symbols_count += number_symbols_added;410 qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol),411 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("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);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(" rbx=[CFA-%d]", saved_registers_offset * 8);448 break;449 case UNWIND_X86_64_REG_R12:450 printf(" r12=[CFA-%d]", saved_registers_offset * 8);451 break;452 case UNWIND_X86_64_REG_R13:453 printf(" r13=[CFA-%d]", saved_registers_offset * 8);454 break;455 case UNWIND_X86_64_REG_R14:456 printf(" r14=[CFA-%d]", saved_registers_offset * 8);457 break;458 case UNWIND_X86_64_REG_R15:459 printf(" 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 first481 // instruction482 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("large stack ");490 }491 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);495 } else {496 printf("frameless function: stack size %d, register count %d ",497 stack_size, register_count);498 }499 500 if (register_count == 0) {501 printf(" 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 indicate506 // the order they're saved on the stack.507 //508 // This is done with Lehmer code permutation, e.g. see509 // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms510 int permunreg[6];511 512 // This decodes the variable-base number in the 10 bits513 // and gives us the Lehmer code sequence which can then514 // 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 of567 // the registers v. http://en.wikipedia.org/wiki/Lehmer_code568 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(" CFA is rsp+%d ", stack_size);587 } else {588 printf(" CFA is rsp+%d ", stack_size * 8);589 }590 591 uint32_t saved_registers_offset = 1;592 printf(" 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(" rbx=[CFA-%d]", saved_registers_offset * 8);601 saved_registers_offset++;602 break;603 case UNWIND_X86_64_REG_R12:604 printf(" r12=[CFA-%d]", saved_registers_offset * 8);605 saved_registers_offset++;606 break;607 case UNWIND_X86_64_REG_R13:608 printf(" r13=[CFA-%d]", saved_registers_offset * 8);609 saved_registers_offset++;610 break;611 case UNWIND_X86_64_REG_R14:612 printf(" r14=[CFA-%d]", saved_registers_offset * 8);613 saved_registers_offset++;614 break;615 case UNWIND_X86_64_REG_R15:616 printf(" r15=[CFA-%d]", saved_registers_offset * 8);617 saved_registers_offset++;618 break;619 case UNWIND_X86_64_REG_RBP:620 printf(" 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 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64633 ")",634 dwarf_offset, dwarf_offset + baton.eh_section_file_address);635 } break;636 637 case 0: {638 printf(" 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("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);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(" ebx=[CFA-%d]", saved_registers_offset * 4);664 break;665 case UNWIND_X86_REG_ECX:666 printf(" ecx=[CFA-%d]", saved_registers_offset * 4);667 break;668 case UNWIND_X86_REG_EDX:669 printf(" edx=[CFA-%d]", saved_registers_offset * 4);670 break;671 case UNWIND_X86_REG_EDI:672 printf(" edi=[CFA-%d]", saved_registers_offset * 4);673 break;674 case UNWIND_X86_REG_ESI:675 printf(" 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 first697 // instruction698 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("large stack ");706 }707 708 if (mode == UNWIND_X86_MODE_STACK_IND) {709 printf("frameless function: stack size %d, register count %d ",710 stack_size, register_count);711 } else {712 printf("frameless function: stack size %d, register count %d ",713 stack_size * 4, register_count);714 }715 716 if (register_count == 0) {717 printf(" 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 indicate722 // the order they're saved on the stack.723 //724 // This is done with Lehmer code permutation, e.g. see725 // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms726 int permunreg[6];727 728 // This decodes the variable-base number in the 10 bits729 // and gives us the Lehmer code sequence which can then730 // 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 of783 // the registers v. http://en.wikipedia.org/wiki/Lehmer_code784 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(" CFA is esp+%d ", stack_size);803 } else {804 printf(" CFA is esp+%d ", stack_size * 4);805 }806 807 uint32_t saved_registers_offset = 1;808 printf(" 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(" ebx=[CFA-%d]", saved_registers_offset * 4);817 saved_registers_offset++;818 break;819 case UNWIND_X86_REG_ECX:820 printf(" ecx=[CFA-%d]", saved_registers_offset * 4);821 saved_registers_offset++;822 break;823 case UNWIND_X86_REG_EDX:824 printf(" edx=[CFA-%d]", saved_registers_offset * 4);825 saved_registers_offset++;826 break;827 case UNWIND_X86_REG_EDI:828 printf(" edi=[CFA-%d]", saved_registers_offset * 4);829 saved_registers_offset++;830 break;831 case UNWIND_X86_REG_ESI:832 printf(" esi=[CFA-%d]", saved_registers_offset * 4);833 saved_registers_offset++;834 break;835 case UNWIND_X86_REG_EBP:836 printf(" 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 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64849 ")",850 dwarf_offset, dwarf_offset + baton.eh_section_file_address);851 } break;852 853 case 0: {854 printf(" 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("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++;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(" x21=[CFA%d]", cfa_offset);881 cfa_offset -= wordsize;882 printf(" 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(" x23=[CFA%d]", cfa_offset);889 cfa_offset -= wordsize;890 printf(" 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(" x25=[CFA%d]", cfa_offset);897 cfa_offset -= wordsize;898 printf(" 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(" x27=[CFA%d]", cfa_offset);905 cfa_offset -= wordsize;906 printf(" 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(" d8=[CFA%d]", cfa_offset);913 cfa_offset -= wordsize;914 printf(" 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(" d10=[CFA%d]", cfa_offset);921 cfa_offset -= wordsize;922 printf(" 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(" d12=[CFA%d]", cfa_offset);929 cfa_offset -= wordsize;930 printf(" 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(" d14=[CFA%d]", cfa_offset);937 cfa_offset -= wordsize;938 printf(" 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("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 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64954 ")",955 dwarf_offset, dwarf_offset + baton.eh_section_file_address);956 } break;957 958 case 0: {959 printf(" 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("frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust);975 int cfa_offset = -stack_adjust;976 977 cfa_offset -= wordsize;978 printf(" pc=[CFA%d]", cfa_offset);979 cfa_offset -= wordsize;980 printf(" 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(" r6=[CFA%d]", cfa_offset);986 }987 if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) {988 cfa_offset -= wordsize;989 printf(" r5=[CFA%d]", cfa_offset);990 }991 if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) {992 cfa_offset -= wordsize;993 printf(" r4=[CFA%d]", cfa_offset);994 }995 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) {996 cfa_offset -= wordsize;997 printf(" r12=[CFA%d]", cfa_offset);998 }999 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) {1000 cfa_offset -= wordsize;1001 printf(" r11=[CFA%d]", cfa_offset);1002 }1003 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) {1004 cfa_offset -= wordsize;1005 printf(" r10=[CFA%d]", cfa_offset);1006 }1007 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) {1008 cfa_offset -= wordsize;1009 printf(" r9=[CFA%d]", cfa_offset);1010 }1011 if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) {1012 cfa_offset -= wordsize;1013 printf(" 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(" d8=[CFA%d]", cfa_offset);1024 break;1025 case 1:1026 // vpush {d10}1027 // vpush {d8}1028 cfa_offset -= 8;1029 printf(" d10=[CFA%d]", cfa_offset);1030 cfa_offset -= 8;1031 printf(" 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(" d12=[CFA%d]", cfa_offset);1039 cfa_offset -= 8;1040 printf(" d10=[CFA%d]", cfa_offset);1041 cfa_offset -= 8;1042 printf(" 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(" d14=[CFA%d]", cfa_offset);1051 cfa_offset -= 8;1052 printf(" d12=[CFA%d]", cfa_offset);1053 cfa_offset -= 8;1054 printf(" d10=[CFA%d]", cfa_offset);1055 cfa_offset -= 8;1056 printf(" 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(" 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(" 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(" 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(" 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 "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx641092 ")",1093 dwarf_offset, dwarf_offset + baton.eh_section_file_address);1094 } break;1095 1096 case 0: {1097 printf(" 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(" -- 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(&entry_encoding_index_str, ", encoding #%d", entry_encoding_index);1125 } else {1126 asprintf(&entry_encoding_index_str, "");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 " 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("\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 the1159 // 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 the1161 // function bounds.1162 if (offset >= 0) {1163 printf("name: %s", symbol->name);1164 if (offset > 0) {1165 printf(" + %d", offset);1166 }1167 }1168 printf("\n ");1169 }1170 1171 print_encoding(baton, 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(", LSDA entry #%d", lsda_entry_number);1211 } else {1212 printf(", 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 index1219 printf(", personality entry #%d", pers_idx);1220 }1221 1222 printf("\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 the1240 // functionOffset from the containing index table already.1241 // UNWIND_SECOND_LEVEL_COMPRESSED1242 // entries only have the offset from the containing index table1243 // functionOffset.1244 // So strip off the containing index table functionOffset value here so they1245 // can1246 // be treated the same at the lower layers.1247 1248 print_function_encoding(baton, idx, encoding, (uint32_t)-1,1249 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 header1276 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 table1282 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_header1286 .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 " 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(" 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("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 entry1346 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(" LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx641369 "), 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("\n");1379 1380 baton.first_level_index_entry = index_entry;1381 print_second_level_index(baton);1382 }1383 1384 printf("\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, O_RDONLY);1397 if (fd == -1) {1398 printf("Failed to open '%s'\n", file);1399 exit(1);1400 }1401 fstat(fd, &st);1402 uint8_t *file_mem =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);1406 }1407 1408 FILE *f = fopen("a.out", "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);1418 1419 if (baton.compact_unwind_start == NULL) {1420 printf("could not find __TEXT,__unwind_info section\n");1421 exit(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("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);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(" Common Encoding [%d]: 0x%x ", encoding_idx, encoding);1445 print_encoding(baton, NULL, encoding);1446 printf("\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(" 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("\n");1466 1467 baton.unwind_header = header;1468 1469 print_index_sections(baton);1470 1471 return 0;1472}1473