Path: blob/main/contrib/llvm-project/lldb/tools/compact-unwind/compact-unwind-dumper.c
34879 views
#include <fcntl.h>1#include <inttypes.h>2#include <mach-o/compact_unwind_encoding.h>3#include <mach-o/loader.h>4#include <mach-o/nlist.h>5#include <mach/machine.h>6#include <stdbool.h>7#include <stdint.h>8#include <stdio.h>9#include <stdlib.h>10#include <string.h>11#include <sys/errno.h>12#include <sys/mman.h>13#include <sys/stat.h>14#include <sys/types.h>1516#define EXTRACT_BITS(value, mask) \17((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))1819// A quick sketch of a program which can parse the compact unwind info20// used on Darwin systems for exception handling. The output of21// unwinddump will be more authoritative/reliable but this program22// can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries23// correctly.2425struct symbol {26uint64_t file_address;27const char *name;28};2930int symbol_compare(const void *a, const void *b) {31return (int)((struct symbol *)a)->file_address -32((struct symbol *)b)->file_address;33}3435struct baton {36cpu_type_t cputype;3738uint8_t *mach_header_start; // pointer into this program's address space39uint8_t *compact_unwind_start; // pointer into this program's address space4041int addr_size; // 4 or 8 bytes, the size of addresses in this file4243uint64_t text_segment_vmaddr; // __TEXT segment vmaddr44uint64_t text_segment_file_offset;4546uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr47uint64_t text_section_file_offset;4849uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame50// section5152uint8_t53*lsda_array_start; // for the currently-being-processed first-level index54uint8_t55*lsda_array_end; // the lsda_array_start for the NEXT first-level index5657struct symbol *symbols;58int symbols_count;5960uint64_t *function_start_addresses;61int function_start_addresses_count;6263int current_index_table_number;6465struct unwind_info_section_header unwind_header;66struct unwind_info_section_header_index_entry first_level_index_entry;67struct unwind_info_compressed_second_level_page_header68compressed_second_level_page_header;69struct unwind_info_regular_second_level_page_header70regular_second_level_page_header;71};7273uint64_t read_leb128(uint8_t **offset) {74uint64_t result = 0;75int shift = 0;76while (1) {77uint8_t byte = **offset;78*offset = *offset + 1;79result |= (byte & 0x7f) << shift;80if ((byte & 0x80) == 0)81break;82shift += 7;83}8485return result;86}8788// step through the load commands in a thin mach-o binary,89// find the cputype and the start of the __TEXT,__unwind_info90// section, return a pointer to that section or NULL if not found.9192static void scan_macho_load_commands(struct baton *baton) {93struct symtab_command symtab_cmd;94uint64_t linkedit_segment_vmaddr;95uint64_t linkedit_segment_file_offset;9697baton->compact_unwind_start = 0;9899uint32_t *magic = (uint32_t *)baton->mach_header_start;100101if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) {102printf("Unexpected magic number 0x%x in header, exiting.", *magic);103exit(1);104}105106bool is_64bit = false;107if (*magic == MH_MAGIC_64)108is_64bit = true;109110uint8_t *offset = baton->mach_header_start;111112struct mach_header mh;113memcpy(&mh, offset, sizeof(struct mach_header));114if (is_64bit)115offset += sizeof(struct mach_header_64);116else117offset += sizeof(struct mach_header);118119if (is_64bit)120baton->addr_size = 8;121else122baton->addr_size = 4;123124baton->cputype = mh.cputype;125126uint8_t *start_of_load_commands = offset;127128uint32_t cur_cmd = 0;129while (cur_cmd < mh.ncmds &&130(offset - start_of_load_commands) < mh.sizeofcmds) {131struct load_command lc;132uint32_t *lc_cmd = (uint32_t *)offset;133uint32_t *lc_cmdsize = (uint32_t *)offset + 1;134uint8_t *start_of_this_load_cmd = offset;135136if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) {137char segment_name[17];138segment_name[0] = '\0';139uint32_t nsects = 0;140uint64_t segment_offset = 0;141uint64_t segment_vmaddr = 0;142143if (*lc_cmd == LC_SEGMENT_64) {144struct segment_command_64 seg;145memcpy(&seg, offset, sizeof(struct segment_command_64));146memcpy(&segment_name, &seg.segname, 16);147segment_name[16] = '\0';148nsects = seg.nsects;149segment_offset = seg.fileoff;150segment_vmaddr = seg.vmaddr;151offset += sizeof(struct segment_command_64);152if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) {153printf("Segment '%s' is encrypted.\n", segment_name);154}155}156157if (*lc_cmd == LC_SEGMENT) {158struct segment_command seg;159memcpy(&seg, offset, sizeof(struct segment_command));160memcpy(&segment_name, &seg.segname, 16);161segment_name[16] = '\0';162nsects = seg.nsects;163segment_offset = seg.fileoff;164segment_vmaddr = seg.vmaddr;165offset += sizeof(struct segment_command);166if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) {167printf("Segment '%s' is encrypted.\n", segment_name);168}169}170171if (nsects != 0 && strcmp(segment_name, "__TEXT") == 0) {172baton->text_segment_vmaddr = segment_vmaddr;173baton->text_segment_file_offset = segment_offset;174175uint32_t current_sect = 0;176while (current_sect < nsects &&177(offset - start_of_this_load_cmd) < *lc_cmdsize) {178char sect_name[17];179memcpy(§_name, offset, 16);180sect_name[16] = '\0';181if (strcmp(sect_name, "__unwind_info") == 0) {182if (is_64bit) {183struct section_64 sect;184memset(§, 0, sizeof(struct section_64));185memcpy(§, offset, sizeof(struct section_64));186baton->compact_unwind_start =187baton->mach_header_start + sect.offset;188} else {189struct section sect;190memset(§, 0, sizeof(struct section));191memcpy(§, offset, sizeof(struct section));192baton->compact_unwind_start =193baton->mach_header_start + sect.offset;194}195}196if (strcmp(sect_name, "__eh_frame") == 0) {197if (is_64bit) {198struct section_64 sect;199memset(§, 0, sizeof(struct section_64));200memcpy(§, offset, sizeof(struct section_64));201baton->eh_section_file_address = sect.addr;202} else {203struct section sect;204memset(§, 0, sizeof(struct section));205memcpy(§, offset, sizeof(struct section));206baton->eh_section_file_address = sect.addr;207}208}209if (strcmp(sect_name, "__text") == 0) {210if (is_64bit) {211struct section_64 sect;212memset(§, 0, sizeof(struct section_64));213memcpy(§, offset, sizeof(struct section_64));214baton->text_section_vmaddr = sect.addr;215baton->text_section_file_offset = sect.offset;216} else {217struct section sect;218memset(§, 0, sizeof(struct section));219memcpy(§, offset, sizeof(struct section));220baton->text_section_vmaddr = sect.addr;221}222}223if (is_64bit) {224offset += sizeof(struct section_64);225} else {226offset += sizeof(struct section);227}228}229}230231if (strcmp(segment_name, "__LINKEDIT") == 0) {232linkedit_segment_vmaddr = segment_vmaddr;233linkedit_segment_file_offset = segment_offset;234}235}236237if (*lc_cmd == LC_SYMTAB) {238memcpy(&symtab_cmd, offset, sizeof(struct symtab_command));239}240241if (*lc_cmd == LC_DYSYMTAB) {242struct dysymtab_command dysymtab_cmd;243memcpy(&dysymtab_cmd, offset, sizeof(struct dysymtab_command));244245int nlist_size = 12;246if (is_64bit)247nlist_size = 16;248249char *string_table =250(char *)(baton->mach_header_start + symtab_cmd.stroff);251uint8_t *local_syms = baton->mach_header_start + symtab_cmd.symoff +252(dysymtab_cmd.ilocalsym * nlist_size);253int local_syms_count = dysymtab_cmd.nlocalsym;254uint8_t *exported_syms = baton->mach_header_start + symtab_cmd.symoff +255(dysymtab_cmd.iextdefsym * nlist_size);256int exported_syms_count = dysymtab_cmd.nextdefsym;257258// We're only going to create records for a small number of these symbols259// but to260// simplify the memory management I'll allocate enough space to store all261// of them.262baton->symbols = (struct symbol *)malloc(263sizeof(struct symbol) * (local_syms_count + exported_syms_count));264baton->symbols_count = 0;265266for (int i = 0; i < local_syms_count; i++) {267struct nlist_64 nlist;268memset(&nlist, 0, sizeof(struct nlist_64));269if (is_64bit) {270memcpy(&nlist, local_syms + (i * nlist_size),271sizeof(struct nlist_64));272} else {273struct nlist nlist_32;274memset(&nlist_32, 0, sizeof(struct nlist));275memcpy(&nlist_32, local_syms + (i * nlist_size),276sizeof(struct nlist));277nlist.n_un.n_strx = nlist_32.n_un.n_strx;278nlist.n_type = nlist_32.n_type;279nlist.n_sect = nlist_32.n_sect;280nlist.n_desc = nlist_32.n_desc;281nlist.n_value = nlist_32.n_value;282}283if ((nlist.n_type & N_STAB) == 0 &&284((nlist.n_type & N_EXT) == 1 ||285((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) &&286nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) {287baton->symbols[baton->symbols_count].file_address = nlist.n_value;288if (baton->cputype == CPU_TYPE_ARM)289baton->symbols[baton->symbols_count].file_address =290baton->symbols[baton->symbols_count].file_address & ~1;291baton->symbols[baton->symbols_count].name =292string_table + nlist.n_un.n_strx;293baton->symbols_count++;294}295}296297for (int i = 0; i < exported_syms_count; i++) {298struct nlist_64 nlist;299memset(&nlist, 0, sizeof(struct nlist_64));300if (is_64bit) {301memcpy(&nlist, exported_syms + (i * nlist_size),302sizeof(struct nlist_64));303} else {304struct nlist nlist_32;305memcpy(&nlist_32, exported_syms + (i * nlist_size),306sizeof(struct nlist));307nlist.n_un.n_strx = nlist_32.n_un.n_strx;308nlist.n_type = nlist_32.n_type;309nlist.n_sect = nlist_32.n_sect;310nlist.n_desc = nlist_32.n_desc;311nlist.n_value = nlist_32.n_value;312}313if ((nlist.n_type & N_STAB) == 0 &&314((nlist.n_type & N_EXT) == 1 ||315((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) &&316nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) {317baton->symbols[baton->symbols_count].file_address = nlist.n_value;318if (baton->cputype == CPU_TYPE_ARM)319baton->symbols[baton->symbols_count].file_address =320baton->symbols[baton->symbols_count].file_address & ~1;321baton->symbols[baton->symbols_count].name =322string_table + nlist.n_un.n_strx;323baton->symbols_count++;324}325}326327qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol),328symbol_compare);329}330331if (*lc_cmd == LC_FUNCTION_STARTS) {332struct linkedit_data_command function_starts_cmd;333memcpy(&function_starts_cmd, offset,334sizeof(struct linkedit_data_command));335336uint8_t *funcstarts_offset =337baton->mach_header_start + function_starts_cmd.dataoff;338uint8_t *function_end = funcstarts_offset + function_starts_cmd.datasize;339int count = 0;340341while (funcstarts_offset < function_end) {342if (read_leb128(&funcstarts_offset) != 0) {343count++;344}345}346347baton->function_start_addresses =348(uint64_t *)malloc(sizeof(uint64_t) * count);349baton->function_start_addresses_count = count;350351funcstarts_offset =352baton->mach_header_start + function_starts_cmd.dataoff;353uint64_t current_pc = baton->text_segment_vmaddr;354int i = 0;355while (funcstarts_offset < function_end) {356uint64_t func_start = read_leb128(&funcstarts_offset);357if (func_start != 0) {358current_pc += func_start;359baton->function_start_addresses[i++] = current_pc;360}361}362}363364offset = start_of_this_load_cmd + *lc_cmdsize;365cur_cmd++;366}367368// Augment the symbol table with the function starts table -- adding symbol369// entries370// for functions that were stripped.371372int unnamed_functions_to_add = 0;373for (int i = 0; i < baton->function_start_addresses_count; i++) {374struct symbol search_key;375search_key.file_address = baton->function_start_addresses[i];376if (baton->cputype == CPU_TYPE_ARM)377search_key.file_address = search_key.file_address & ~1;378struct symbol *sym =379bsearch(&search_key, baton->symbols, baton->symbols_count,380sizeof(struct symbol), symbol_compare);381if (sym == NULL)382unnamed_functions_to_add++;383}384385baton->symbols = (struct symbol *)realloc(386baton->symbols, sizeof(struct symbol) *387(baton->symbols_count + unnamed_functions_to_add));388389int current_unnamed_symbol = 1;390int number_symbols_added = 0;391for (int i = 0; i < baton->function_start_addresses_count; i++) {392struct symbol search_key;393search_key.file_address = baton->function_start_addresses[i];394if (baton->cputype == CPU_TYPE_ARM)395search_key.file_address = search_key.file_address & ~1;396struct symbol *sym =397bsearch(&search_key, baton->symbols, baton->symbols_count,398sizeof(struct symbol), symbol_compare);399if (sym == NULL) {400char *name;401asprintf(&name, "unnamed function #%d", current_unnamed_symbol++);402baton->symbols[baton->symbols_count + number_symbols_added].file_address =403baton->function_start_addresses[i];404baton->symbols[baton->symbols_count + number_symbols_added].name = name;405number_symbols_added++;406}407}408baton->symbols_count += number_symbols_added;409qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol),410symbol_compare);411412// printf ("function start addresses\n");413// for (int i = 0; i < baton->function_start_addresses_count; i++)414// {415// printf ("0x%012llx\n", baton->function_start_addresses[i]);416// }417418// printf ("symbol table names & addresses\n");419// for (int i = 0; i < baton->symbols_count; i++)420// {421// printf ("0x%012llx %s\n", baton->symbols[i].file_address,422// baton->symbols[i].name);423// }424}425426void print_encoding_x86_64(struct baton baton, uint8_t *function_start,427uint32_t encoding) {428int mode = encoding & UNWIND_X86_64_MODE_MASK;429switch (mode) {430case UNWIND_X86_64_MODE_RBP_FRAME: {431printf("frame func: CFA is rbp+%d ", 16);432printf(" rip=[CFA-8] rbp=[CFA-16]");433uint32_t saved_registers_offset =434EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);435436uint32_t saved_registers_locations =437EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);438439saved_registers_offset += 2;440441for (int i = 0; i < 5; i++) {442switch (saved_registers_locations & 0x7) {443case UNWIND_X86_64_REG_NONE:444break;445case UNWIND_X86_64_REG_RBX:446printf(" rbx=[CFA-%d]", saved_registers_offset * 8);447break;448case UNWIND_X86_64_REG_R12:449printf(" r12=[CFA-%d]", saved_registers_offset * 8);450break;451case UNWIND_X86_64_REG_R13:452printf(" r13=[CFA-%d]", saved_registers_offset * 8);453break;454case UNWIND_X86_64_REG_R14:455printf(" r14=[CFA-%d]", saved_registers_offset * 8);456break;457case UNWIND_X86_64_REG_R15:458printf(" r15=[CFA-%d]", saved_registers_offset * 8);459break;460}461saved_registers_offset--;462saved_registers_locations >>= 3;463}464} break;465466case UNWIND_X86_64_MODE_STACK_IND:467case UNWIND_X86_64_MODE_STACK_IMMD: {468uint32_t stack_size =469EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);470uint32_t register_count =471EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);472uint32_t permutation =473EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);474475if (mode == UNWIND_X86_64_MODE_STACK_IND && function_start) {476uint32_t stack_adjust =477EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);478479// offset into the function instructions; 0 == beginning of first480// instruction481uint32_t offset_to_subl_insn =482EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);483484stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));485486stack_size += stack_adjust * 8;487488printf("large stack ");489}490491if (mode == UNWIND_X86_64_MODE_STACK_IND) {492printf("frameless function: stack size %d, register count %d ",493stack_size * 8, register_count);494} else {495printf("frameless function: stack size %d, register count %d ",496stack_size, register_count);497}498499if (register_count == 0) {500printf(" no registers saved");501} else {502503// We need to include (up to) 6 registers in 10 bits.504// That would be 18 bits if we just used 3 bits per reg to indicate505// the order they're saved on the stack.506//507// This is done with Lehmer code permutation, e.g. see508// http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms509int permunreg[6];510511// This decodes the variable-base number in the 10 bits512// and gives us the Lehmer code sequence which can then513// be decoded.514515switch (register_count) {516case 6:517permunreg[0] = permutation / 120; // 120 == 5!518permutation -= (permunreg[0] * 120);519permunreg[1] = permutation / 24; // 24 == 4!520permutation -= (permunreg[1] * 24);521permunreg[2] = permutation / 6; // 6 == 3!522permutation -= (permunreg[2] * 6);523permunreg[3] = permutation / 2; // 2 == 2!524permutation -= (permunreg[3] * 2);525permunreg[4] = permutation; // 1 == 1!526permunreg[5] = 0;527break;528case 5:529permunreg[0] = permutation / 120;530permutation -= (permunreg[0] * 120);531permunreg[1] = permutation / 24;532permutation -= (permunreg[1] * 24);533permunreg[2] = permutation / 6;534permutation -= (permunreg[2] * 6);535permunreg[3] = permutation / 2;536permutation -= (permunreg[3] * 2);537permunreg[4] = permutation;538break;539case 4:540permunreg[0] = permutation / 60;541permutation -= (permunreg[0] * 60);542permunreg[1] = permutation / 12;543permutation -= (permunreg[1] * 12);544permunreg[2] = permutation / 3;545permutation -= (permunreg[2] * 3);546permunreg[3] = permutation;547break;548case 3:549permunreg[0] = permutation / 20;550permutation -= (permunreg[0] * 20);551permunreg[1] = permutation / 4;552permutation -= (permunreg[1] * 4);553permunreg[2] = permutation;554break;555case 2:556permunreg[0] = permutation / 5;557permutation -= (permunreg[0] * 5);558permunreg[1] = permutation;559break;560case 1:561permunreg[0] = permutation;562break;563}564565// Decode the Lehmer code for this permutation of566// the registers v. http://en.wikipedia.org/wiki/Lehmer_code567568int registers[6];569bool used[7] = {false, false, false, false, false, false, false};570for (int i = 0; i < register_count; i++) {571int renum = 0;572for (int j = 1; j < 7; j++) {573if (used[j] == false) {574if (renum == permunreg[i]) {575registers[i] = j;576used[j] = true;577break;578}579renum++;580}581}582}583584if (mode == UNWIND_X86_64_MODE_STACK_IND) {585printf(" CFA is rsp+%d ", stack_size);586} else {587printf(" CFA is rsp+%d ", stack_size * 8);588}589590uint32_t saved_registers_offset = 1;591printf(" rip=[CFA-%d]", saved_registers_offset * 8);592saved_registers_offset++;593594for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {595switch (registers[i]) {596case UNWIND_X86_64_REG_NONE:597break;598case UNWIND_X86_64_REG_RBX:599printf(" rbx=[CFA-%d]", saved_registers_offset * 8);600saved_registers_offset++;601break;602case UNWIND_X86_64_REG_R12:603printf(" r12=[CFA-%d]", saved_registers_offset * 8);604saved_registers_offset++;605break;606case UNWIND_X86_64_REG_R13:607printf(" r13=[CFA-%d]", saved_registers_offset * 8);608saved_registers_offset++;609break;610case UNWIND_X86_64_REG_R14:611printf(" r14=[CFA-%d]", saved_registers_offset * 8);612saved_registers_offset++;613break;614case UNWIND_X86_64_REG_R15:615printf(" r15=[CFA-%d]", saved_registers_offset * 8);616saved_registers_offset++;617break;618case UNWIND_X86_64_REG_RBP:619printf(" rbp=[CFA-%d]", saved_registers_offset * 8);620saved_registers_offset++;621break;622}623}624}625626} break;627628case UNWIND_X86_64_MODE_DWARF: {629uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;630printf(631"DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64632")",633dwarf_offset, dwarf_offset + baton.eh_section_file_address);634} break;635636case 0: {637printf(" no unwind information");638} break;639}640}641642void print_encoding_i386(struct baton baton, uint8_t *function_start,643uint32_t encoding) {644int mode = encoding & UNWIND_X86_MODE_MASK;645switch (mode) {646case UNWIND_X86_MODE_EBP_FRAME: {647printf("frame func: CFA is ebp+%d ", 8);648printf(" eip=[CFA-4] ebp=[CFA-8]");649uint32_t saved_registers_offset =650EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);651652uint32_t saved_registers_locations =653EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);654655saved_registers_offset += 2;656657for (int i = 0; i < 5; i++) {658switch (saved_registers_locations & 0x7) {659case UNWIND_X86_REG_NONE:660break;661case UNWIND_X86_REG_EBX:662printf(" ebx=[CFA-%d]", saved_registers_offset * 4);663break;664case UNWIND_X86_REG_ECX:665printf(" ecx=[CFA-%d]", saved_registers_offset * 4);666break;667case UNWIND_X86_REG_EDX:668printf(" edx=[CFA-%d]", saved_registers_offset * 4);669break;670case UNWIND_X86_REG_EDI:671printf(" edi=[CFA-%d]", saved_registers_offset * 4);672break;673case UNWIND_X86_REG_ESI:674printf(" esi=[CFA-%d]", saved_registers_offset * 4);675break;676}677saved_registers_offset--;678saved_registers_locations >>= 3;679}680} break;681682case UNWIND_X86_MODE_STACK_IND:683case UNWIND_X86_MODE_STACK_IMMD: {684uint32_t stack_size =685EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);686uint32_t register_count =687EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);688uint32_t permutation =689EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);690691if (mode == UNWIND_X86_MODE_STACK_IND && function_start) {692uint32_t stack_adjust =693EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);694695// offset into the function instructions; 0 == beginning of first696// instruction697uint32_t offset_to_subl_insn =698EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);699700stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));701702stack_size += stack_adjust * 4;703704printf("large stack ");705}706707if (mode == UNWIND_X86_MODE_STACK_IND) {708printf("frameless function: stack size %d, register count %d ",709stack_size, register_count);710} else {711printf("frameless function: stack size %d, register count %d ",712stack_size * 4, register_count);713}714715if (register_count == 0) {716printf(" no registers saved");717} else {718719// We need to include (up to) 6 registers in 10 bits.720// That would be 18 bits if we just used 3 bits per reg to indicate721// the order they're saved on the stack.722//723// This is done with Lehmer code permutation, e.g. see724// http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms725int permunreg[6];726727// This decodes the variable-base number in the 10 bits728// and gives us the Lehmer code sequence which can then729// be decoded.730731switch (register_count) {732case 6:733permunreg[0] = permutation / 120; // 120 == 5!734permutation -= (permunreg[0] * 120);735permunreg[1] = permutation / 24; // 24 == 4!736permutation -= (permunreg[1] * 24);737permunreg[2] = permutation / 6; // 6 == 3!738permutation -= (permunreg[2] * 6);739permunreg[3] = permutation / 2; // 2 == 2!740permutation -= (permunreg[3] * 2);741permunreg[4] = permutation; // 1 == 1!742permunreg[5] = 0;743break;744case 5:745permunreg[0] = permutation / 120;746permutation -= (permunreg[0] * 120);747permunreg[1] = permutation / 24;748permutation -= (permunreg[1] * 24);749permunreg[2] = permutation / 6;750permutation -= (permunreg[2] * 6);751permunreg[3] = permutation / 2;752permutation -= (permunreg[3] * 2);753permunreg[4] = permutation;754break;755case 4:756permunreg[0] = permutation / 60;757permutation -= (permunreg[0] * 60);758permunreg[1] = permutation / 12;759permutation -= (permunreg[1] * 12);760permunreg[2] = permutation / 3;761permutation -= (permunreg[2] * 3);762permunreg[3] = permutation;763break;764case 3:765permunreg[0] = permutation / 20;766permutation -= (permunreg[0] * 20);767permunreg[1] = permutation / 4;768permutation -= (permunreg[1] * 4);769permunreg[2] = permutation;770break;771case 2:772permunreg[0] = permutation / 5;773permutation -= (permunreg[0] * 5);774permunreg[1] = permutation;775break;776case 1:777permunreg[0] = permutation;778break;779}780781// Decode the Lehmer code for this permutation of782// the registers v. http://en.wikipedia.org/wiki/Lehmer_code783784int registers[6];785bool used[7] = {false, false, false, false, false, false, false};786for (int i = 0; i < register_count; i++) {787int renum = 0;788for (int j = 1; j < 7; j++) {789if (used[j] == false) {790if (renum == permunreg[i]) {791registers[i] = j;792used[j] = true;793break;794}795renum++;796}797}798}799800if (mode == UNWIND_X86_MODE_STACK_IND) {801printf(" CFA is esp+%d ", stack_size);802} else {803printf(" CFA is esp+%d ", stack_size * 4);804}805806uint32_t saved_registers_offset = 1;807printf(" eip=[CFA-%d]", saved_registers_offset * 4);808saved_registers_offset++;809810for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {811switch (registers[i]) {812case UNWIND_X86_REG_NONE:813break;814case UNWIND_X86_REG_EBX:815printf(" ebx=[CFA-%d]", saved_registers_offset * 4);816saved_registers_offset++;817break;818case UNWIND_X86_REG_ECX:819printf(" ecx=[CFA-%d]", saved_registers_offset * 4);820saved_registers_offset++;821break;822case UNWIND_X86_REG_EDX:823printf(" edx=[CFA-%d]", saved_registers_offset * 4);824saved_registers_offset++;825break;826case UNWIND_X86_REG_EDI:827printf(" edi=[CFA-%d]", saved_registers_offset * 4);828saved_registers_offset++;829break;830case UNWIND_X86_REG_ESI:831printf(" esi=[CFA-%d]", saved_registers_offset * 4);832saved_registers_offset++;833break;834case UNWIND_X86_REG_EBP:835printf(" ebp=[CFA-%d]", saved_registers_offset * 4);836saved_registers_offset++;837break;838}839}840}841842} break;843844case UNWIND_X86_MODE_DWARF: {845uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;846printf(847"DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64848")",849dwarf_offset, dwarf_offset + baton.eh_section_file_address);850} break;851852case 0: {853printf(" no unwind information");854} break;855}856}857858void print_encoding_arm64(struct baton baton, uint8_t *function_start,859uint32_t encoding) {860const int wordsize = 8;861int mode = encoding & UNWIND_ARM64_MODE_MASK;862switch (mode) {863case UNWIND_ARM64_MODE_FRAME: {864printf("frame func: CFA is fp+%d ", 16);865printf(" pc=[CFA-8] fp=[CFA-16]");866int reg_pairs_saved_count = 1;867uint32_t saved_register_bits = encoding & 0xfff;868if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) {869int cfa_offset = reg_pairs_saved_count * -2 * wordsize;870cfa_offset -= wordsize;871printf(" x19=[CFA%d]", cfa_offset);872cfa_offset -= wordsize;873printf(" x20=[CFA%d]", cfa_offset);874reg_pairs_saved_count++;875}876if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) {877int cfa_offset = reg_pairs_saved_count * -2 * wordsize;878cfa_offset -= wordsize;879printf(" x21=[CFA%d]", cfa_offset);880cfa_offset -= wordsize;881printf(" x22=[CFA%d]", cfa_offset);882reg_pairs_saved_count++;883}884if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) {885int cfa_offset = reg_pairs_saved_count * -2 * wordsize;886cfa_offset -= wordsize;887printf(" x23=[CFA%d]", cfa_offset);888cfa_offset -= wordsize;889printf(" x24=[CFA%d]", cfa_offset);890reg_pairs_saved_count++;891}892if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) {893int cfa_offset = reg_pairs_saved_count * -2 * wordsize;894cfa_offset -= wordsize;895printf(" x25=[CFA%d]", cfa_offset);896cfa_offset -= wordsize;897printf(" x26=[CFA%d]", cfa_offset);898reg_pairs_saved_count++;899}900if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) {901int cfa_offset = reg_pairs_saved_count * -2 * wordsize;902cfa_offset -= wordsize;903printf(" x27=[CFA%d]", cfa_offset);904cfa_offset -= wordsize;905printf(" x28=[CFA%d]", cfa_offset);906reg_pairs_saved_count++;907}908if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) {909int cfa_offset = reg_pairs_saved_count * -2 * wordsize;910cfa_offset -= wordsize;911printf(" d8=[CFA%d]", cfa_offset);912cfa_offset -= wordsize;913printf(" d9=[CFA%d]", cfa_offset);914reg_pairs_saved_count++;915}916if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) {917int cfa_offset = reg_pairs_saved_count * -2 * wordsize;918cfa_offset -= wordsize;919printf(" d10=[CFA%d]", cfa_offset);920cfa_offset -= wordsize;921printf(" d11=[CFA%d]", cfa_offset);922reg_pairs_saved_count++;923}924if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) {925int cfa_offset = reg_pairs_saved_count * -2 * wordsize;926cfa_offset -= wordsize;927printf(" d12=[CFA%d]", cfa_offset);928cfa_offset -= wordsize;929printf(" d13=[CFA%d]", cfa_offset);930reg_pairs_saved_count++;931}932if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) {933int cfa_offset = reg_pairs_saved_count * -2 * wordsize;934cfa_offset -= wordsize;935printf(" d14=[CFA%d]", cfa_offset);936cfa_offset -= wordsize;937printf(" d15=[CFA%d]", cfa_offset);938reg_pairs_saved_count++;939}940941} break;942943case UNWIND_ARM64_MODE_FRAMELESS: {944uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK;945printf("frameless function: stack size %d ", stack_size * 16);946947} break;948949case UNWIND_ARM64_MODE_DWARF: {950uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET;951printf(952"DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64953")",954dwarf_offset, dwarf_offset + baton.eh_section_file_address);955} break;956957case 0: {958printf(" no unwind information");959} break;960}961}962963void print_encoding_armv7(struct baton baton, uint8_t *function_start,964uint32_t encoding) {965const int wordsize = 4;966int mode = encoding & UNWIND_ARM_MODE_MASK;967switch (mode) {968case UNWIND_ARM_MODE_FRAME_D:969case UNWIND_ARM_MODE_FRAME: {970int stack_adjust =971EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize;972973printf("frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust);974int cfa_offset = -stack_adjust;975976cfa_offset -= wordsize;977printf(" pc=[CFA%d]", cfa_offset);978cfa_offset -= wordsize;979printf(" fp=[CFA%d]", cfa_offset);980981uint32_t saved_register_bits = encoding & 0xff;982if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) {983cfa_offset -= wordsize;984printf(" r6=[CFA%d]", cfa_offset);985}986if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) {987cfa_offset -= wordsize;988printf(" r5=[CFA%d]", cfa_offset);989}990if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) {991cfa_offset -= wordsize;992printf(" r4=[CFA%d]", cfa_offset);993}994if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) {995cfa_offset -= wordsize;996printf(" r12=[CFA%d]", cfa_offset);997}998if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) {999cfa_offset -= wordsize;1000printf(" r11=[CFA%d]", cfa_offset);1001}1002if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) {1003cfa_offset -= wordsize;1004printf(" r10=[CFA%d]", cfa_offset);1005}1006if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) {1007cfa_offset -= wordsize;1008printf(" r9=[CFA%d]", cfa_offset);1009}1010if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) {1011cfa_offset -= wordsize;1012printf(" r8=[CFA%d]", cfa_offset);1013}10141015if (mode == UNWIND_ARM_MODE_FRAME_D) {1016uint32_t d_reg_bits =1017EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK);1018switch (d_reg_bits) {1019case 0:1020// vpush {d8}1021cfa_offset -= 8;1022printf(" d8=[CFA%d]", cfa_offset);1023break;1024case 1:1025// vpush {d10}1026// vpush {d8}1027cfa_offset -= 8;1028printf(" d10=[CFA%d]", cfa_offset);1029cfa_offset -= 8;1030printf(" d8=[CFA%d]", cfa_offset);1031break;1032case 2:1033// vpush {d12}1034// vpush {d10}1035// vpush {d8}1036cfa_offset -= 8;1037printf(" d12=[CFA%d]", cfa_offset);1038cfa_offset -= 8;1039printf(" d10=[CFA%d]", cfa_offset);1040cfa_offset -= 8;1041printf(" d8=[CFA%d]", cfa_offset);1042break;1043case 3:1044// vpush {d14}1045// vpush {d12}1046// vpush {d10}1047// vpush {d8}1048cfa_offset -= 8;1049printf(" d14=[CFA%d]", cfa_offset);1050cfa_offset -= 8;1051printf(" d12=[CFA%d]", cfa_offset);1052cfa_offset -= 8;1053printf(" d10=[CFA%d]", cfa_offset);1054cfa_offset -= 8;1055printf(" d8=[CFA%d]", cfa_offset);1056break;1057case 4:1058// vpush {d14}1059// vpush {d12}1060// sp = (sp - 24) & (-16);1061// vst {d8, d9, d10}1062printf(" d14, d12, d10, d9, d8");1063break;1064case 5:1065// vpush {d14}1066// sp = (sp - 40) & (-16);1067// vst {d8, d9, d10, d11}1068// vst {d12}1069printf(" d14, d11, d10, d9, d8, d12");1070break;1071case 6:1072// sp = (sp - 56) & (-16);1073// vst {d8, d9, d10, d11}1074// vst {d12, d13, d14}1075printf(" d11, d10, d9, d8, d14, d13, d12");1076break;1077case 7:1078// sp = (sp - 64) & (-16);1079// vst {d8, d9, d10, d11}1080// vst {d12, d13, d14, d15}1081printf(" d11, d10, d9, d8, d15, d14, d13, d12");1082break;1083}1084}1085} break;10861087case UNWIND_ARM_MODE_DWARF: {1088uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET;1089printf(1090"DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx641091")",1092dwarf_offset, dwarf_offset + baton.eh_section_file_address);1093} break;10941095case 0: {1096printf(" no unwind information");1097} break;1098}1099}11001101void print_encoding(struct baton baton, uint8_t *function_start,1102uint32_t encoding) {11031104if (baton.cputype == CPU_TYPE_X86_64) {1105print_encoding_x86_64(baton, function_start, encoding);1106} else if (baton.cputype == CPU_TYPE_I386) {1107print_encoding_i386(baton, function_start, encoding);1108} else if (baton.cputype == CPU_TYPE_ARM64 || baton.cputype == CPU_TYPE_ARM64_32) {1109print_encoding_arm64(baton, function_start, encoding);1110} else if (baton.cputype == CPU_TYPE_ARM) {1111print_encoding_armv7(baton, function_start, encoding);1112} else {1113printf(" -- unsupported encoding arch -- ");1114}1115}11161117void print_function_encoding(struct baton baton, uint32_t idx,1118uint32_t encoding, uint32_t entry_encoding_index,1119uint32_t entry_func_offset) {11201121char *entry_encoding_index_str = "";1122if (entry_encoding_index != (uint32_t)-1) {1123asprintf(&entry_encoding_index_str, ", encoding #%d", entry_encoding_index);1124} else {1125asprintf(&entry_encoding_index_str, "");1126}11271128uint64_t file_address = baton.first_level_index_entry.functionOffset +1129entry_func_offset + baton.text_segment_vmaddr;11301131if (baton.cputype == CPU_TYPE_ARM)1132file_address = file_address & ~1;11331134printf(1135" func [%d] offset %d (file addr 0x%" PRIx64 ")%s, encoding is 0x%x",1136idx, entry_func_offset, file_address, entry_encoding_index_str, encoding);11371138struct symbol *symbol = NULL;1139for (int i = 0; i < baton.symbols_count; i++) {1140if (i == baton.symbols_count - 1 &&1141baton.symbols[i].file_address <= file_address) {1142symbol = &(baton.symbols[i]);1143break;1144} else {1145if (baton.symbols[i].file_address <= file_address &&1146baton.symbols[i + 1].file_address > file_address) {1147symbol = &(baton.symbols[i]);1148break;1149}1150}1151}11521153printf("\n ");1154if (symbol) {1155int offset = file_address - symbol->file_address;11561157// FIXME this is a poor heuristic - if we're greater than 16 bytes past the1158// start of the function, this is the unwind info for a stripped function.1159// In reality the compact unwind entry may not line up exactly with the1160// function bounds.1161if (offset >= 0) {1162printf("name: %s", symbol->name);1163if (offset > 0) {1164printf(" + %d", offset);1165}1166}1167printf("\n ");1168}11691170print_encoding(baton, baton.mach_header_start +1171baton.first_level_index_entry.functionOffset +1172baton.text_section_file_offset + entry_func_offset,1173encoding);11741175bool has_lsda = encoding & UNWIND_HAS_LSDA;11761177if (has_lsda) {1178uint32_t func_offset =1179entry_func_offset + baton.first_level_index_entry.functionOffset;11801181int lsda_entry_number = -1;11821183uint32_t low = 0;1184uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) /1185sizeof(struct unwind_info_section_header_lsda_index_entry);11861187while (low < high) {1188uint32_t mid = (low + high) / 2;11891190uint8_t *mid_lsda_entry_addr =1191(baton.lsda_array_start +1192(mid * sizeof(struct unwind_info_section_header_lsda_index_entry)));1193struct unwind_info_section_header_lsda_index_entry mid_lsda_entry;1194memcpy(&mid_lsda_entry, mid_lsda_entry_addr,1195sizeof(struct unwind_info_section_header_lsda_index_entry));1196if (mid_lsda_entry.functionOffset == func_offset) {1197lsda_entry_number =1198(mid_lsda_entry_addr - baton.lsda_array_start) /1199sizeof(struct unwind_info_section_header_lsda_index_entry);1200break;1201} else if (mid_lsda_entry.functionOffset < func_offset) {1202low = mid + 1;1203} else {1204high = mid;1205}1206}12071208if (lsda_entry_number != -1) {1209printf(", LSDA entry #%d", lsda_entry_number);1210} else {1211printf(", LSDA entry not found");1212}1213}12141215uint32_t pers_idx = EXTRACT_BITS(encoding, UNWIND_PERSONALITY_MASK);1216if (pers_idx != 0) {1217pers_idx--; // Change 1-based to 0-based index1218printf(", personality entry #%d", pers_idx);1219}12201221printf("\n");1222}12231224void print_second_level_index_regular(struct baton baton) {1225uint8_t *page_entries =1226baton.compact_unwind_start +1227baton.first_level_index_entry.secondLevelPagesSectionOffset +1228baton.regular_second_level_page_header.entryPageOffset;1229uint32_t entries_count = baton.regular_second_level_page_header.entryCount;12301231uint8_t *offset = page_entries;12321233uint32_t idx = 0;1234while (idx < entries_count) {1235uint32_t func_offset = *((uint32_t *)(offset));1236uint32_t encoding = *((uint32_t *)(offset + 4));12371238// UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the1239// functionOffset from the containing index table already.1240// UNWIND_SECOND_LEVEL_COMPRESSED1241// entries only have the offset from the containing index table1242// functionOffset.1243// So strip off the containing index table functionOffset value here so they1244// can1245// be treated the same at the lower layers.12461247print_function_encoding(baton, idx, encoding, (uint32_t)-1,1248func_offset -1249baton.first_level_index_entry.functionOffset);1250idx++;1251offset += 8;1252}1253}12541255void print_second_level_index_compressed(struct baton baton) {1256uint8_t *this_index =1257baton.compact_unwind_start +1258baton.first_level_index_entry.secondLevelPagesSectionOffset;1259uint8_t *start_of_entries =1260this_index + baton.compressed_second_level_page_header.entryPageOffset;1261uint8_t *offset = start_of_entries;1262for (uint16_t idx = 0;1263idx < baton.compressed_second_level_page_header.entryCount; idx++) {1264uint32_t entry = *((uint32_t *)offset);1265offset += 4;1266uint32_t encoding;12671268uint32_t entry_encoding_index =1269UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry);1270uint32_t entry_func_offset =1271UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry);12721273if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) {1274// encoding is in common table in section header1275encoding =1276*((uint32_t *)(baton.compact_unwind_start +1277baton.unwind_header.commonEncodingsArraySectionOffset +1278(entry_encoding_index * sizeof(uint32_t))));1279} else {1280// encoding is in page specific table1281uint32_t page_encoding_index =1282entry_encoding_index - baton.unwind_header.commonEncodingsArrayCount;1283encoding = *((uint32_t *)(this_index +1284baton.compressed_second_level_page_header1285.encodingsPageOffset +1286(page_encoding_index * sizeof(uint32_t))));1287}12881289print_function_encoding(baton, idx, encoding, entry_encoding_index,1290entry_func_offset);1291}1292}12931294void print_second_level_index(struct baton baton) {1295uint8_t *index_start =1296baton.compact_unwind_start +1297baton.first_level_index_entry.secondLevelPagesSectionOffset;12981299if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_REGULAR) {1300struct unwind_info_regular_second_level_page_header header;1301memcpy(&header, index_start,1302sizeof(struct unwind_info_regular_second_level_page_header));1303printf(1304" UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n",1305baton.current_index_table_number, header.entryPageOffset,1306header.entryCount);1307baton.regular_second_level_page_header = header;1308print_second_level_index_regular(baton);1309}13101311if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_COMPRESSED) {1312struct unwind_info_compressed_second_level_page_header header;1313memcpy(&header, index_start,1314sizeof(struct unwind_info_compressed_second_level_page_header));1315printf(" UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, "1316"entryCount %d, encodingsPageOffset %d, encodingsCount %d\n",1317baton.current_index_table_number, header.entryPageOffset,1318header.entryCount, header.encodingsPageOffset,1319header.encodingsCount);1320baton.compressed_second_level_page_header = header;1321print_second_level_index_compressed(baton);1322}1323}13241325void print_index_sections(struct baton baton) {1326uint8_t *index_section_offset =1327baton.compact_unwind_start + baton.unwind_header.indexSectionOffset;1328uint32_t index_count = baton.unwind_header.indexCount;13291330uint32_t cur_idx = 0;13311332uint8_t *offset = index_section_offset;1333while (cur_idx < index_count) {1334baton.current_index_table_number = cur_idx;1335struct unwind_info_section_header_index_entry index_entry;1336memcpy(&index_entry, offset,1337sizeof(struct unwind_info_section_header_index_entry));1338printf("index section #%d: functionOffset %d, "1339"secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n",1340cur_idx, index_entry.functionOffset,1341index_entry.secondLevelPagesSectionOffset,1342index_entry.lsdaIndexArraySectionOffset);13431344// secondLevelPagesSectionOffset == 0 means this is a sentinel entry1345if (index_entry.secondLevelPagesSectionOffset != 0) {1346struct unwind_info_section_header_index_entry next_index_entry;1347memcpy(&next_index_entry,1348offset + sizeof(struct unwind_info_section_header_index_entry),1349sizeof(struct unwind_info_section_header_index_entry));13501351baton.lsda_array_start =1352baton.compact_unwind_start + index_entry.lsdaIndexArraySectionOffset;1353baton.lsda_array_end = baton.compact_unwind_start +1354next_index_entry.lsdaIndexArraySectionOffset;13551356uint8_t *lsda_entry_offset = baton.lsda_array_start;1357uint32_t lsda_count = 0;1358while (lsda_entry_offset < baton.lsda_array_end) {1359struct unwind_info_section_header_lsda_index_entry lsda_entry;1360memcpy(&lsda_entry, lsda_entry_offset,1361sizeof(struct unwind_info_section_header_lsda_index_entry));1362uint64_t function_file_address =1363baton.first_level_index_entry.functionOffset +1364lsda_entry.functionOffset + baton.text_segment_vmaddr;1365uint64_t lsda_file_address =1366lsda_entry.lsdaOffset + baton.text_segment_vmaddr;1367printf(" LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx641368"), lsdaOffset %d (file address 0x%" PRIx64 ")\n",1369lsda_count, lsda_entry.functionOffset,1370lsda_entry.functionOffset - index_entry.functionOffset,1371function_file_address, lsda_entry.lsdaOffset, lsda_file_address);1372lsda_count++;1373lsda_entry_offset +=1374sizeof(struct unwind_info_section_header_lsda_index_entry);1375}13761377printf("\n");13781379baton.first_level_index_entry = index_entry;1380print_second_level_index(baton);1381}13821383printf("\n");13841385cur_idx++;1386offset += sizeof(struct unwind_info_section_header_index_entry);1387}1388}13891390int main(int argc, char **argv) {1391struct stat st;1392char *file = argv[0];1393if (argc > 1)1394file = argv[1];1395int fd = open(file, O_RDONLY);1396if (fd == -1) {1397printf("Failed to open '%s'\n", file);1398exit(1);1399}1400fstat(fd, &st);1401uint8_t *file_mem =1402(uint8_t *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);1403if (file_mem == MAP_FAILED) {1404printf("Failed to mmap() '%s'\n", file);1405}14061407FILE *f = fopen("a.out", "r");14081409struct baton baton;1410baton.mach_header_start = file_mem;1411baton.symbols = NULL;1412baton.symbols_count = 0;1413baton.function_start_addresses = NULL;1414baton.function_start_addresses_count = 0;14151416scan_macho_load_commands(&baton);14171418if (baton.compact_unwind_start == NULL) {1419printf("could not find __TEXT,__unwind_info section\n");1420exit(1);1421}14221423struct unwind_info_section_header header;1424memcpy(&header, baton.compact_unwind_start,1425sizeof(struct unwind_info_section_header));1426printf("Header:\n");1427printf(" version %u\n", header.version);1428printf(" commonEncodingsArraySectionOffset is %d\n",1429header.commonEncodingsArraySectionOffset);1430printf(" commonEncodingsArrayCount is %d\n",1431header.commonEncodingsArrayCount);1432printf(" personalityArraySectionOffset is %d\n",1433header.personalityArraySectionOffset);1434printf(" personalityArrayCount is %d\n", header.personalityArrayCount);1435printf(" indexSectionOffset is %d\n", header.indexSectionOffset);1436printf(" indexCount is %d\n", header.indexCount);14371438uint8_t *common_encodings =1439baton.compact_unwind_start + header.commonEncodingsArraySectionOffset;1440uint32_t encoding_idx = 0;1441while (encoding_idx < header.commonEncodingsArrayCount) {1442uint32_t encoding = *((uint32_t *)common_encodings);1443printf(" Common Encoding [%d]: 0x%x ", encoding_idx, encoding);1444print_encoding(baton, NULL, encoding);1445printf("\n");1446common_encodings += sizeof(uint32_t);1447encoding_idx++;1448}14491450uint8_t *pers_arr =1451baton.compact_unwind_start + header.personalityArraySectionOffset;1452uint32_t pers_idx = 0;1453while (pers_idx < header.personalityArrayCount) {1454int32_t pers_delta = *((int32_t *)(baton.compact_unwind_start +1455header.personalityArraySectionOffset +1456(pers_idx * sizeof(uint32_t))));1457printf(" Personality [%d]: personality function ptr @ offset %d (file "1458"address 0x%" PRIx64 ")\n",1459pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta);1460pers_idx++;1461pers_arr += sizeof(uint32_t);1462}14631464printf("\n");14651466baton.unwind_header = header;14671468print_index_sections(baton);14691470return 0;1471}147214731474