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 26 struct symbol { 27 uint64_t file_address; 28 const char *name; 29 }; 30 31 int symbol_compare(const void *a, const void *b) { 32 return (int)((struct symbol *)a)->file_address - 33 ((struct symbol *)b)->file_address; 34 } 35 36 struct 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 74 uint64_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 93 static void scan_macho_load_commands(struct baton *baton) { 94 struct symtab_command symtab_cmd; 95 uint64_t linkedit_segment_vmaddr; 96 uint64_t linkedit_segment_file_offset; 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 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(&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 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 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 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(&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 427 void 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 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("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 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(" 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%" PRIx64 633 ")", 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 643 void 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 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("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 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(" 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%" PRIx64 849 ")", 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 859 void 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%" PRIx64 954 ")", 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 964 void 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%" PRIx64 1092 ")", 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 1102 void 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 1118 void print_function_encoding(struct baton baton, uint32_t idx, 1119 uint32_t encoding, uint32_t entry_encoding_index, 1120 uint32_t entry_func_offset) { 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 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("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 index 1219 printf(", personality entry #%d", pers_idx); 1220 } 1221 1222 printf("\n"); 1223 } 1224 1225 void print_second_level_index_regular(struct baton baton) { 1226 uint8_t *page_entries = 1227 baton.compact_unwind_start + 1228 baton.first_level_index_entry.secondLevelPagesSectionOffset + 1229 baton.regular_second_level_page_header.entryPageOffset; 1230 uint32_t entries_count = baton.regular_second_level_page_header.entryCount; 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, (uint32_t)-1, 1249 func_offset - 1250 baton.first_level_index_entry.functionOffset); 1251 idx++; 1252 offset += 8; 1253 } 1254 } 1255 1256 void print_second_level_index_compressed(struct baton baton) { 1257 uint8_t *this_index = 1258 baton.compact_unwind_start + 1259 baton.first_level_index_entry.secondLevelPagesSectionOffset; 1260 uint8_t *start_of_entries = 1261 this_index + baton.compressed_second_level_page_header.entryPageOffset; 1262 uint8_t *offset = start_of_entries; 1263 for (uint16_t idx = 0; 1264 idx < baton.compressed_second_level_page_header.entryCount; idx++) { 1265 uint32_t entry = *((uint32_t *)offset); 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 1295 void print_second_level_index(struct baton baton) { 1296 uint8_t *index_start = 1297 baton.compact_unwind_start + 1298 baton.first_level_index_entry.secondLevelPagesSectionOffset; 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 1326 void print_index_sections(struct baton baton) { 1327 uint8_t *index_section_offset = 1328 baton.compact_unwind_start + baton.unwind_header.indexSectionOffset; 1329 uint32_t index_count = baton.unwind_header.indexCount; 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 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(" 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("\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 1391 int 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