diff options
Diffstat (limited to 'lldb/tools/compact-unwind/compact-unwind-dumper.c')
-rw-r--r-- | lldb/tools/compact-unwind/compact-unwind-dumper.c | 2814 |
1 files changed, 1364 insertions, 1450 deletions
diff --git a/lldb/tools/compact-unwind/compact-unwind-dumper.c b/lldb/tools/compact-unwind/compact-unwind-dumper.c index f70f602326b..570c42981a2 100644 --- a/lldb/tools/compact-unwind/compact-unwind-dumper.c +++ b/lldb/tools/compact-unwind/compact-unwind-dumper.c @@ -1,66 +1,64 @@ -#include <stdint.h> -#include <mach-o/loader.h> +#include <fcntl.h> +#include <inttypes.h> #include <mach-o/compact_unwind_encoding.h> +#include <mach-o/loader.h> +#include <mach-o/nlist.h> #include <mach/machine.h> -#include <stdlib.h> #include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/mman.h> #include <sys/errno.h> +#include <sys/mman.h> #include <sys/stat.h> -#include <inttypes.h> -#include <stdio.h> -#include <mach-o/nlist.h> - +#include <sys/types.h> enum { - UNWIND_ARM64_MODE_MASK = 0x0F000000, - UNWIND_ARM64_MODE_FRAMELESS = 0x02000000, - UNWIND_ARM64_MODE_DWARF = 0x03000000, - UNWIND_ARM64_MODE_FRAME = 0x04000000, - - UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001, - UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002, - UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004, - UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008, - UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010, - UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100, - UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200, - UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400, - UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800, - - UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000, - UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF, + UNWIND_ARM64_MODE_MASK = 0x0F000000, + UNWIND_ARM64_MODE_FRAMELESS = 0x02000000, + UNWIND_ARM64_MODE_DWARF = 0x03000000, + UNWIND_ARM64_MODE_FRAME = 0x04000000, + + UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001, + UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002, + UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004, + UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008, + UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010, + UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100, + UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200, + UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400, + UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800, + + UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000, + UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF, }; enum { - UNWIND_ARM_MODE_MASK = 0x0F000000, - UNWIND_ARM_MODE_FRAME = 0x01000000, - UNWIND_ARM_MODE_FRAME_D = 0x02000000, - UNWIND_ARM_MODE_DWARF = 0x04000000, + UNWIND_ARM_MODE_MASK = 0x0F000000, + UNWIND_ARM_MODE_FRAME = 0x01000000, + UNWIND_ARM_MODE_FRAME_D = 0x02000000, + UNWIND_ARM_MODE_DWARF = 0x04000000, - UNWIND_ARM_FRAME_STACK_ADJUST_MASK = 0x00C00000, + UNWIND_ARM_FRAME_STACK_ADJUST_MASK = 0x00C00000, - UNWIND_ARM_FRAME_FIRST_PUSH_R4 = 0x00000001, - UNWIND_ARM_FRAME_FIRST_PUSH_R5 = 0x00000002, - UNWIND_ARM_FRAME_FIRST_PUSH_R6 = 0x00000004, + UNWIND_ARM_FRAME_FIRST_PUSH_R4 = 0x00000001, + UNWIND_ARM_FRAME_FIRST_PUSH_R5 = 0x00000002, + UNWIND_ARM_FRAME_FIRST_PUSH_R6 = 0x00000004, - UNWIND_ARM_FRAME_SECOND_PUSH_R8 = 0x00000008, - UNWIND_ARM_FRAME_SECOND_PUSH_R9 = 0x00000010, - UNWIND_ARM_FRAME_SECOND_PUSH_R10 = 0x00000020, - UNWIND_ARM_FRAME_SECOND_PUSH_R11 = 0x00000040, - UNWIND_ARM_FRAME_SECOND_PUSH_R12 = 0x00000080, + UNWIND_ARM_FRAME_SECOND_PUSH_R8 = 0x00000008, + UNWIND_ARM_FRAME_SECOND_PUSH_R9 = 0x00000010, + UNWIND_ARM_FRAME_SECOND_PUSH_R10 = 0x00000020, + UNWIND_ARM_FRAME_SECOND_PUSH_R11 = 0x00000040, + UNWIND_ARM_FRAME_SECOND_PUSH_R12 = 0x00000080, - UNWIND_ARM_FRAME_D_REG_COUNT_MASK = 0x00000700, + UNWIND_ARM_FRAME_D_REG_COUNT_MASK = 0x00000700, - UNWIND_ARM_DWARF_SECTION_OFFSET = 0x00FFFFFF, + UNWIND_ARM_DWARF_SECTION_OFFSET = 0x00FFFFFF, }; -#define EXTRACT_BITS(value, mask) \ - ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) - +#define EXTRACT_BITS(value, mask) \ + ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) // A quick sketch of a program which can parse the compact unwind info // used on Darwin systems for exception handling. The output of @@ -68,1534 +66,1450 @@ enum { // can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries // correctly. -struct symbol -{ - uint64_t file_address; - const char *name; +struct symbol { + uint64_t file_address; + const char *name; }; -int -symbol_compare (const void *a, const void *b) -{ - return (int) ((struct symbol *)a)->file_address - ((struct symbol *)b)->file_address; +int symbol_compare(const void *a, const void *b) { + return (int)((struct symbol *)a)->file_address - + ((struct symbol *)b)->file_address; } -struct baton -{ - cpu_type_t cputype; +struct baton { + cpu_type_t cputype; - uint8_t *mach_header_start; // pointer into this program's address space - uint8_t *compact_unwind_start; // pointer into this program's address space + uint8_t *mach_header_start; // pointer into this program's address space + uint8_t *compact_unwind_start; // pointer into this program's address space - int addr_size; // 4 or 8 bytes, the size of addresses in this file + int addr_size; // 4 or 8 bytes, the size of addresses in this file - uint64_t text_segment_vmaddr; // __TEXT segment vmaddr - uint64_t text_segment_file_offset; + uint64_t text_segment_vmaddr; // __TEXT segment vmaddr + uint64_t text_segment_file_offset; - uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr - uint64_t text_section_file_offset; + uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr + uint64_t text_section_file_offset; - uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame section + uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame + // section - uint8_t *lsda_array_start; // for the currently-being-processed first-level index - uint8_t *lsda_array_end; // the lsda_array_start for the NEXT first-level index + uint8_t + *lsda_array_start; // for the currently-being-processed first-level index + uint8_t + *lsda_array_end; // the lsda_array_start for the NEXT first-level index - struct symbol *symbols; - int symbols_count; + struct symbol *symbols; + int symbols_count; - uint64_t *function_start_addresses; - int function_start_addresses_count; + uint64_t *function_start_addresses; + int function_start_addresses_count; - int current_index_table_number; + int current_index_table_number; - struct unwind_info_section_header unwind_header; - struct unwind_info_section_header_index_entry first_level_index_entry; - struct unwind_info_compressed_second_level_page_header compressed_second_level_page_header; - struct unwind_info_regular_second_level_page_header regular_second_level_page_header; + struct unwind_info_section_header unwind_header; + struct unwind_info_section_header_index_entry first_level_index_entry; + struct unwind_info_compressed_second_level_page_header + compressed_second_level_page_header; + struct unwind_info_regular_second_level_page_header + regular_second_level_page_header; }; - -uint64_t -read_leb128 (uint8_t **offset) -{ - uint64_t result = 0; - int shift = 0; - while (1) - { - uint8_t byte = **offset; - *offset = *offset + 1; - result |= (byte & 0x7f) << shift; - if ((byte & 0x80) == 0) - break; - shift += 7; - } - - return result; +uint64_t read_leb128(uint8_t **offset) { + uint64_t result = 0; + int shift = 0; + while (1) { + uint8_t byte = **offset; + *offset = *offset + 1; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + + return result; } // step through the load commands in a thin mach-o binary, // find the cputype and the start of the __TEXT,__unwind_info // section, return a pointer to that section or NULL if not found. -static void -scan_macho_load_commands (struct baton *baton) -{ - struct symtab_command symtab_cmd; - uint64_t linkedit_segment_vmaddr; - uint64_t linkedit_segment_file_offset; - - baton->compact_unwind_start = 0; - - uint32_t *magic = (uint32_t *) baton->mach_header_start; - - if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) - { - printf ("Unexpected magic number 0x%x in header, exiting.", *magic); - exit (1); - } - - bool is_64bit = false; - if (*magic == MH_MAGIC_64) - is_64bit = true; - - uint8_t *offset = baton->mach_header_start; - - struct mach_header mh; - memcpy (&mh, offset, sizeof (struct mach_header)); - if (is_64bit) - offset += sizeof (struct mach_header_64); - else - offset += sizeof (struct mach_header); - - if (is_64bit) - baton->addr_size = 8; - else - baton->addr_size = 4; - - baton->cputype = mh.cputype; - - uint8_t *start_of_load_commands = offset; - - uint32_t cur_cmd = 0; - while (cur_cmd < mh.ncmds && (offset - start_of_load_commands) < mh.sizeofcmds) - { - struct load_command lc; - uint32_t *lc_cmd = (uint32_t *) offset; - uint32_t *lc_cmdsize = (uint32_t *) offset + 1; - uint8_t *start_of_this_load_cmd = offset; - - if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) - { - char segment_name[17]; - segment_name[0] = '\0'; - uint32_t nsects = 0; - uint64_t segment_offset = 0; - uint64_t segment_vmaddr = 0; - - if (*lc_cmd == LC_SEGMENT_64) - { - struct segment_command_64 seg; - memcpy (&seg, offset, sizeof (struct segment_command_64)); - memcpy (&segment_name, &seg.segname, 16); - segment_name[16] = '\0'; - nsects = seg.nsects; - segment_offset = seg.fileoff; - segment_vmaddr = seg.vmaddr; - offset += sizeof (struct segment_command_64); - if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) - { - printf ("Segment '%s' is encrypted.\n", segment_name); - } +static void scan_macho_load_commands(struct baton *baton) { + struct symtab_command symtab_cmd; + uint64_t linkedit_segment_vmaddr; + uint64_t linkedit_segment_file_offset; + + baton->compact_unwind_start = 0; + + uint32_t *magic = (uint32_t *)baton->mach_header_start; + + if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) { + printf("Unexpected magic number 0x%x in header, exiting.", *magic); + exit(1); + } + + bool is_64bit = false; + if (*magic == MH_MAGIC_64) + is_64bit = true; + + uint8_t *offset = baton->mach_header_start; + + struct mach_header mh; + memcpy(&mh, offset, sizeof(struct mach_header)); + if (is_64bit) + offset += sizeof(struct mach_header_64); + else + offset += sizeof(struct mach_header); + + if (is_64bit) + baton->addr_size = 8; + else + baton->addr_size = 4; + + baton->cputype = mh.cputype; + + uint8_t *start_of_load_commands = offset; + + uint32_t cur_cmd = 0; + while (cur_cmd < mh.ncmds && + (offset - start_of_load_commands) < mh.sizeofcmds) { + struct load_command lc; + uint32_t *lc_cmd = (uint32_t *)offset; + uint32_t *lc_cmdsize = (uint32_t *)offset + 1; + uint8_t *start_of_this_load_cmd = offset; + + if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) { + char segment_name[17]; + segment_name[0] = '\0'; + uint32_t nsects = 0; + uint64_t segment_offset = 0; + uint64_t segment_vmaddr = 0; + + if (*lc_cmd == LC_SEGMENT_64) { + struct segment_command_64 seg; + memcpy(&seg, offset, sizeof(struct segment_command_64)); + memcpy(&segment_name, &seg.segname, 16); + segment_name[16] = '\0'; + nsects = seg.nsects; + segment_offset = seg.fileoff; + segment_vmaddr = seg.vmaddr; + offset += sizeof(struct segment_command_64); + if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) { + printf("Segment '%s' is encrypted.\n", segment_name); + } + } + + if (*lc_cmd == LC_SEGMENT) { + struct segment_command seg; + memcpy(&seg, offset, sizeof(struct segment_command)); + memcpy(&segment_name, &seg.segname, 16); + segment_name[16] = '\0'; + nsects = seg.nsects; + segment_offset = seg.fileoff; + segment_vmaddr = seg.vmaddr; + offset += sizeof(struct segment_command); + if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) { + printf("Segment '%s' is encrypted.\n", segment_name); + } + } + + if (nsects != 0 && strcmp(segment_name, "__TEXT") == 0) { + baton->text_segment_vmaddr = segment_vmaddr; + baton->text_segment_file_offset = segment_offset; + + uint32_t current_sect = 0; + while (current_sect < nsects && + (offset - start_of_this_load_cmd) < *lc_cmdsize) { + char sect_name[17]; + memcpy(§_name, offset, 16); + sect_name[16] = '\0'; + if (strcmp(sect_name, "__unwind_info") == 0) { + if (is_64bit) { + struct section_64 sect; + memset(§, 0, sizeof(struct section_64)); + memcpy(§, offset, sizeof(struct section_64)); + baton->compact_unwind_start = + baton->mach_header_start + sect.offset; + } else { + struct section sect; + memset(§, 0, sizeof(struct section)); + memcpy(§, offset, sizeof(struct section)); + baton->compact_unwind_start = + baton->mach_header_start + sect.offset; } - - if (*lc_cmd == LC_SEGMENT) - { - struct segment_command seg; - memcpy (&seg, offset, sizeof (struct segment_command)); - memcpy (&segment_name, &seg.segname, 16); - segment_name[16] = '\0'; - nsects = seg.nsects; - segment_offset = seg.fileoff; - segment_vmaddr = seg.vmaddr; - offset += sizeof (struct segment_command); - if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) - { - printf ("Segment '%s' is encrypted.\n", segment_name); - } + } + if (strcmp(sect_name, "__eh_frame") == 0) { + if (is_64bit) { + struct section_64 sect; + memset(§, 0, sizeof(struct section_64)); + memcpy(§, offset, sizeof(struct section_64)); + baton->eh_section_file_address = sect.addr; + } else { + struct section sect; + memset(§, 0, sizeof(struct section)); + memcpy(§, offset, sizeof(struct section)); + baton->eh_section_file_address = sect.addr; } - - if (nsects != 0 && strcmp (segment_name, "__TEXT") == 0) - { - baton->text_segment_vmaddr = segment_vmaddr; - baton->text_segment_file_offset = segment_offset; - - uint32_t current_sect = 0; - while (current_sect < nsects && (offset - start_of_this_load_cmd) < *lc_cmdsize) - { - char sect_name[17]; - memcpy (§_name, offset, 16); - sect_name[16] = '\0'; - if (strcmp (sect_name, "__unwind_info") == 0) - { - if (is_64bit) - { - struct section_64 sect; - memset (§, 0, sizeof (struct section_64)); - memcpy (§, offset, sizeof (struct section_64)); - baton->compact_unwind_start = baton->mach_header_start + sect.offset; - } - else - { - struct section sect; - memset (§, 0, sizeof (struct section)); - memcpy (§, offset, sizeof (struct section)); - baton->compact_unwind_start = baton->mach_header_start + sect.offset; - } - } - if (strcmp (sect_name, "__eh_frame") == 0) - { - if (is_64bit) - { - struct section_64 sect; - memset (§, 0, sizeof (struct section_64)); - memcpy (§, offset, sizeof (struct section_64)); - baton->eh_section_file_address = sect.addr; - } - else - { - struct section sect; - memset (§, 0, sizeof (struct section)); - memcpy (§, offset, sizeof (struct section)); - baton->eh_section_file_address = sect.addr; - } - } - if (strcmp (sect_name, "__text") == 0) - { - if (is_64bit) - { - struct section_64 sect; - memset (§, 0, sizeof (struct section_64)); - memcpy (§, offset, sizeof (struct section_64)); - baton->text_section_vmaddr = sect.addr; - baton->text_section_file_offset = sect.offset; - } - else - { - struct section sect; - memset (§, 0, sizeof (struct section)); - memcpy (§, offset, sizeof (struct section)); - baton->text_section_vmaddr = sect.addr; - } - } - if (is_64bit) - { - offset += sizeof (struct section_64); - } - else - { - offset += sizeof (struct section); - } - } + } + if (strcmp(sect_name, "__text") == 0) { + if (is_64bit) { + struct section_64 sect; + memset(§, 0, sizeof(struct section_64)); + memcpy(§, offset, sizeof(struct section_64)); + baton->text_section_vmaddr = sect.addr; + baton->text_section_file_offset = sect.offset; + } else { + struct section sect; + memset(§, 0, sizeof(struct section)); + memcpy(§, offset, sizeof(struct section)); + baton->text_section_vmaddr = sect.addr; } - - if (strcmp (segment_name, "__LINKEDIT") == 0) - { - linkedit_segment_vmaddr = segment_vmaddr; - linkedit_segment_file_offset = segment_offset; - } - } - - if (*lc_cmd == LC_SYMTAB) - { - memcpy (&symtab_cmd, offset, sizeof (struct symtab_command)); + } + if (is_64bit) { + offset += sizeof(struct section_64); + } else { + offset += sizeof(struct section); + } } + } - if (*lc_cmd == LC_DYSYMTAB) - { - struct dysymtab_command dysymtab_cmd; - memcpy (&dysymtab_cmd, offset, sizeof (struct dysymtab_command)); - - int nlist_size = 12; - if (is_64bit) - nlist_size = 16; - - char *string_table = (char *) (baton->mach_header_start + symtab_cmd.stroff); - uint8_t *local_syms = baton->mach_header_start + symtab_cmd.symoff + (dysymtab_cmd.ilocalsym * nlist_size); - int local_syms_count = dysymtab_cmd.nlocalsym; - uint8_t *exported_syms = baton->mach_header_start + symtab_cmd.symoff + (dysymtab_cmd.iextdefsym * nlist_size); - int exported_syms_count = dysymtab_cmd.nextdefsym; - - // We're only going to create records for a small number of these symbols but to - // simplify the memory management I'll allocate enough space to store all of them. - baton->symbols = (struct symbol *) malloc (sizeof (struct symbol) * (local_syms_count + exported_syms_count)); - baton->symbols_count = 0; - - for (int i = 0; i < local_syms_count; i++) - { - struct nlist_64 nlist; - memset (&nlist, 0, sizeof (struct nlist_64)); - if (is_64bit) - { - memcpy (&nlist, local_syms + (i * nlist_size), sizeof (struct nlist_64)); - } - else - { - struct nlist nlist_32; - memset (&nlist_32, 0, sizeof (struct nlist)); - memcpy (&nlist_32, local_syms + (i * nlist_size), sizeof (struct nlist)); - nlist.n_un.n_strx = nlist_32.n_un.n_strx; - nlist.n_type = nlist_32.n_type; - nlist.n_sect = nlist_32.n_sect; - nlist.n_desc = nlist_32.n_desc; - nlist.n_value = nlist_32.n_value; - } - if ((nlist.n_type & N_STAB) == 0 - && ((nlist.n_type & N_EXT) == 1 || - ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) - && nlist.n_value != 0 - && nlist.n_value != baton->text_segment_vmaddr) - { - baton->symbols[baton->symbols_count].file_address = nlist.n_value; - if (baton->cputype == CPU_TYPE_ARM) - baton->symbols[baton->symbols_count].file_address = baton->symbols[baton->symbols_count].file_address & ~1; - baton->symbols[baton->symbols_count].name = string_table + nlist.n_un.n_strx; - baton->symbols_count++; - } - } + if (strcmp(segment_name, "__LINKEDIT") == 0) { + linkedit_segment_vmaddr = segment_vmaddr; + linkedit_segment_file_offset = segment_offset; + } + } - for (int i = 0; i < exported_syms_count; i++) - { - struct nlist_64 nlist; - memset (&nlist, 0, sizeof (struct nlist_64)); - if (is_64bit) - { - memcpy (&nlist, exported_syms + (i * nlist_size), sizeof (struct nlist_64)); - } - else - { - struct nlist nlist_32; - memcpy (&nlist_32, exported_syms + (i * nlist_size), sizeof (struct nlist)); - nlist.n_un.n_strx = nlist_32.n_un.n_strx; - nlist.n_type = nlist_32.n_type; - nlist.n_sect = nlist_32.n_sect; - nlist.n_desc = nlist_32.n_desc; - nlist.n_value = nlist_32.n_value; - } - if ((nlist.n_type & N_STAB) == 0 - && ((nlist.n_type & N_EXT) == 1 || - ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) - && nlist.n_value != 0 - && nlist.n_value != baton->text_segment_vmaddr) - { - baton->symbols[baton->symbols_count].file_address = nlist.n_value; - if (baton->cputype == CPU_TYPE_ARM) - baton->symbols[baton->symbols_count].file_address = baton->symbols[baton->symbols_count].file_address & ~1; - baton->symbols[baton->symbols_count].name = string_table + nlist.n_un.n_strx; - baton->symbols_count++; - } - } + if (*lc_cmd == LC_SYMTAB) { + memcpy(&symtab_cmd, offset, sizeof(struct symtab_command)); + } - qsort (baton->symbols, baton->symbols_count, sizeof (struct symbol), symbol_compare); + if (*lc_cmd == LC_DYSYMTAB) { + struct dysymtab_command dysymtab_cmd; + memcpy(&dysymtab_cmd, offset, sizeof(struct dysymtab_command)); + + int nlist_size = 12; + if (is_64bit) + nlist_size = 16; + + char *string_table = + (char *)(baton->mach_header_start + symtab_cmd.stroff); + uint8_t *local_syms = baton->mach_header_start + symtab_cmd.symoff + + (dysymtab_cmd.ilocalsym * nlist_size); + int local_syms_count = dysymtab_cmd.nlocalsym; + uint8_t *exported_syms = baton->mach_header_start + symtab_cmd.symoff + + (dysymtab_cmd.iextdefsym * nlist_size); + int exported_syms_count = dysymtab_cmd.nextdefsym; + + // We're only going to create records for a small number of these symbols + // but to + // simplify the memory management I'll allocate enough space to store all + // of them. + baton->symbols = (struct symbol *)malloc( + sizeof(struct symbol) * (local_syms_count + exported_syms_count)); + baton->symbols_count = 0; + + for (int i = 0; i < local_syms_count; i++) { + struct nlist_64 nlist; + memset(&nlist, 0, sizeof(struct nlist_64)); + if (is_64bit) { + memcpy(&nlist, local_syms + (i * nlist_size), + sizeof(struct nlist_64)); + } else { + struct nlist nlist_32; + memset(&nlist_32, 0, sizeof(struct nlist)); + memcpy(&nlist_32, local_syms + (i * nlist_size), + sizeof(struct nlist)); + nlist.n_un.n_strx = nlist_32.n_un.n_strx; + nlist.n_type = nlist_32.n_type; + nlist.n_sect = nlist_32.n_sect; + nlist.n_desc = nlist_32.n_desc; + nlist.n_value = nlist_32.n_value; } - - if (*lc_cmd == LC_FUNCTION_STARTS) - { - struct linkedit_data_command function_starts_cmd; - memcpy (&function_starts_cmd, offset, sizeof (struct linkedit_data_command)); - - uint8_t *funcstarts_offset = baton->mach_header_start + function_starts_cmd.dataoff; - uint8_t *function_end = funcstarts_offset + function_starts_cmd.datasize; - int count = 0; - - while (funcstarts_offset < function_end) - { - if (read_leb128 (&funcstarts_offset) != 0) - { - count++; - } - } - - baton->function_start_addresses = (uint64_t *) malloc (sizeof (uint64_t) * count); - baton->function_start_addresses_count = count; - - funcstarts_offset = baton->mach_header_start + function_starts_cmd.dataoff; - uint64_t current_pc = baton->text_segment_vmaddr; - int i = 0; - while (funcstarts_offset < function_end) - { - uint64_t func_start = read_leb128 (&funcstarts_offset); - if (func_start != 0) - { - current_pc += func_start; - baton->function_start_addresses[i++] = current_pc; - } - } + if ((nlist.n_type & N_STAB) == 0 && + ((nlist.n_type & N_EXT) == 1 || + ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) && + nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) { + baton->symbols[baton->symbols_count].file_address = nlist.n_value; + if (baton->cputype == CPU_TYPE_ARM) + baton->symbols[baton->symbols_count].file_address = + baton->symbols[baton->symbols_count].file_address & ~1; + baton->symbols[baton->symbols_count].name = + string_table + nlist.n_un.n_strx; + baton->symbols_count++; + } + } + + for (int i = 0; i < exported_syms_count; i++) { + struct nlist_64 nlist; + memset(&nlist, 0, sizeof(struct nlist_64)); + if (is_64bit) { + memcpy(&nlist, exported_syms + (i * nlist_size), + sizeof(struct nlist_64)); + } else { + struct nlist nlist_32; + memcpy(&nlist_32, exported_syms + (i * nlist_size), + sizeof(struct nlist)); + nlist.n_un.n_strx = nlist_32.n_un.n_strx; + nlist.n_type = nlist_32.n_type; + nlist.n_sect = nlist_32.n_sect; + nlist.n_desc = nlist_32.n_desc; + nlist.n_value = nlist_32.n_value; + } + if ((nlist.n_type & N_STAB) == 0 && + ((nlist.n_type & N_EXT) == 1 || + ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) && + nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) { + baton->symbols[baton->symbols_count].file_address = nlist.n_value; + if (baton->cputype == CPU_TYPE_ARM) + baton->symbols[baton->symbols_count].file_address = + baton->symbols[baton->symbols_count].file_address & ~1; + baton->symbols[baton->symbols_count].name = + string_table + nlist.n_un.n_strx; + baton->symbols_count++; } + } - offset = start_of_this_load_cmd + *lc_cmdsize; - cur_cmd++; + qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol), + symbol_compare); } + if (*lc_cmd == LC_FUNCTION_STARTS) { + struct linkedit_data_command function_starts_cmd; + memcpy(&function_starts_cmd, offset, + sizeof(struct linkedit_data_command)); - // Augment the symbol table with the function starts table -- adding symbol entries - // for functions that were stripped. + uint8_t *funcstarts_offset = + baton->mach_header_start + function_starts_cmd.dataoff; + uint8_t *function_end = funcstarts_offset + function_starts_cmd.datasize; + int count = 0; - int unnamed_functions_to_add = 0; - for (int i = 0; i < baton->function_start_addresses_count; i++) - { - struct symbol search_key; - search_key.file_address = baton->function_start_addresses[i]; - if (baton->cputype == CPU_TYPE_ARM) - search_key.file_address = search_key.file_address & ~1; - struct symbol *sym = bsearch (&search_key, baton->symbols, baton->symbols_count, sizeof (struct symbol), symbol_compare); - if (sym == NULL) - unnamed_functions_to_add++; - } - - baton->symbols = (struct symbol *) realloc (baton->symbols, sizeof (struct symbol) * (baton->symbols_count + unnamed_functions_to_add)); - - int current_unnamed_symbol = 1; - int number_symbols_added = 0; - for (int i = 0; i < baton->function_start_addresses_count; i++) - { - struct symbol search_key; - search_key.file_address = baton->function_start_addresses[i]; - if (baton->cputype == CPU_TYPE_ARM) - search_key.file_address = search_key.file_address & ~1; - struct symbol *sym = bsearch (&search_key, baton->symbols, baton->symbols_count, sizeof (struct symbol), symbol_compare); - if (sym == NULL) - { - char *name; - asprintf (&name, "unnamed function #%d", current_unnamed_symbol++); - baton->symbols[baton->symbols_count + number_symbols_added].file_address = baton->function_start_addresses[i]; - baton->symbols[baton->symbols_count + number_symbols_added].name = name; - number_symbols_added++; + while (funcstarts_offset < function_end) { + if (read_leb128(&funcstarts_offset) != 0) { + count++; + } + } + + baton->function_start_addresses = + (uint64_t *)malloc(sizeof(uint64_t) * count); + baton->function_start_addresses_count = count; + + funcstarts_offset = + baton->mach_header_start + function_starts_cmd.dataoff; + uint64_t current_pc = baton->text_segment_vmaddr; + int i = 0; + while (funcstarts_offset < function_end) { + uint64_t func_start = read_leb128(&funcstarts_offset); + if (func_start != 0) { + current_pc += func_start; + baton->function_start_addresses[i++] = current_pc; } + } } - baton->symbols_count += number_symbols_added; - qsort (baton->symbols, baton->symbols_count, sizeof (struct symbol), symbol_compare); + offset = start_of_this_load_cmd + *lc_cmdsize; + cur_cmd++; + } + + // Augment the symbol table with the function starts table -- adding symbol + // entries + // for functions that were stripped. + + int unnamed_functions_to_add = 0; + for (int i = 0; i < baton->function_start_addresses_count; i++) { + struct symbol search_key; + search_key.file_address = baton->function_start_addresses[i]; + if (baton->cputype == CPU_TYPE_ARM) + search_key.file_address = search_key.file_address & ~1; + struct symbol *sym = + bsearch(&search_key, baton->symbols, baton->symbols_count, + sizeof(struct symbol), symbol_compare); + if (sym == NULL) + unnamed_functions_to_add++; + } + + baton->symbols = (struct symbol *)realloc( + baton->symbols, sizeof(struct symbol) * + (baton->symbols_count + unnamed_functions_to_add)); + + int current_unnamed_symbol = 1; + int number_symbols_added = 0; + for (int i = 0; i < baton->function_start_addresses_count; i++) { + struct symbol search_key; + search_key.file_address = baton->function_start_addresses[i]; + if (baton->cputype == CPU_TYPE_ARM) + search_key.file_address = search_key.file_address & ~1; + struct symbol *sym = + bsearch(&search_key, baton->symbols, baton->symbols_count, + sizeof(struct symbol), symbol_compare); + if (sym == NULL) { + char *name; + asprintf(&name, "unnamed function #%d", current_unnamed_symbol++); + baton->symbols[baton->symbols_count + number_symbols_added].file_address = + baton->function_start_addresses[i]; + baton->symbols[baton->symbols_count + number_symbols_added].name = name; + number_symbols_added++; + } + } + baton->symbols_count += number_symbols_added; + qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol), + symbol_compare); + + // printf ("function start addresses\n"); + // for (int i = 0; i < baton->function_start_addresses_count; i++) + // { + // printf ("0x%012llx\n", baton->function_start_addresses[i]); + // } + + // printf ("symbol table names & addresses\n"); + // for (int i = 0; i < baton->symbols_count; i++) + // { + // printf ("0x%012llx %s\n", baton->symbols[i].file_address, + // baton->symbols[i].name); + // } +} -// printf ("function start addresses\n"); -// for (int i = 0; i < baton->function_start_addresses_count; i++) -// { -// printf ("0x%012llx\n", baton->function_start_addresses[i]); -// } +void print_encoding_x86_64(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + int mode = encoding & UNWIND_X86_64_MODE_MASK; + switch (mode) { + case UNWIND_X86_64_MODE_RBP_FRAME: { + printf("frame func: CFA is rbp+%d ", 16); + printf(" rip=[CFA-8] rbp=[CFA-16]"); + uint32_t saved_registers_offset = + EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); -// printf ("symbol table names & addresses\n"); -// for (int i = 0; i < baton->symbols_count; i++) -// { -// printf ("0x%012llx %s\n", baton->symbols[i].file_address, baton->symbols[i].name); -// } + uint32_t saved_registers_locations = + EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); -} + saved_registers_offset += 2; -void -print_encoding_x86_64 (struct baton baton, uint8_t *function_start, uint32_t encoding) -{ - int mode = encoding & UNWIND_X86_64_MODE_MASK; - switch (mode) - { - case UNWIND_X86_64_MODE_RBP_FRAME: - { - printf ("frame func: CFA is rbp+%d ", 16); - printf (" rip=[CFA-8] rbp=[CFA-16]"); - uint32_t saved_registers_offset = EXTRACT_BITS (encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); - - uint32_t saved_registers_locations = EXTRACT_BITS (encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); - - - saved_registers_offset += 2; - - for (int i = 0; i < 5; i++) - { - switch (saved_registers_locations & 0x7) - { - case UNWIND_X86_64_REG_NONE: - break; - case UNWIND_X86_64_REG_RBX: - printf (" rbx=[CFA-%d]", saved_registers_offset * 8); - break; - case UNWIND_X86_64_REG_R12: - printf (" r12=[CFA-%d]", saved_registers_offset * 8); - break; - case UNWIND_X86_64_REG_R13: - printf (" r13=[CFA-%d]", saved_registers_offset * 8); - break; - case UNWIND_X86_64_REG_R14: - printf (" r14=[CFA-%d]", saved_registers_offset * 8); - break; - case UNWIND_X86_64_REG_R15: - printf (" r15=[CFA-%d]", saved_registers_offset * 8); - break; - } - saved_registers_offset--; - saved_registers_locations >>= 3; - } - } + for (int i = 0; i < 5; i++) { + switch (saved_registers_locations & 0x7) { + case UNWIND_X86_64_REG_NONE: break; + case UNWIND_X86_64_REG_RBX: + printf(" rbx=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R12: + printf(" r12=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R13: + printf(" r13=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R14: + printf(" r14=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R15: + printf(" r15=[CFA-%d]", saved_registers_offset * 8); + break; + } + saved_registers_offset--; + saved_registers_locations >>= 3; + } + } break; - case UNWIND_X86_64_MODE_STACK_IND: - case UNWIND_X86_64_MODE_STACK_IMMD: - { - uint32_t stack_size = EXTRACT_BITS (encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); - uint32_t register_count = EXTRACT_BITS (encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); - uint32_t permutation = EXTRACT_BITS (encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); - - if (mode == UNWIND_X86_64_MODE_STACK_IND && function_start) - { - uint32_t stack_adjust = EXTRACT_BITS (encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + case UNWIND_X86_64_MODE_STACK_IND: + case UNWIND_X86_64_MODE_STACK_IMMD: { + uint32_t stack_size = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t register_count = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); - // offset into the function instructions; 0 == beginning of first instruction - uint32_t offset_to_subl_insn = EXTRACT_BITS (encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + if (mode == UNWIND_X86_64_MODE_STACK_IND && function_start) { + uint32_t stack_adjust = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); - stack_size = *((uint32_t*) (function_start + offset_to_subl_insn)); + // offset into the function instructions; 0 == beginning of first + // instruction + uint32_t offset_to_subl_insn = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); - stack_size += stack_adjust * 8; + stack_size = *((uint32_t *)(function_start + offset_to_subl_insn)); - printf ("large stack "); - } - - if (mode == UNWIND_X86_64_MODE_STACK_IND) - { - printf ("frameless function: stack size %d, register count %d ", stack_size * 8, register_count); - } - else - { - printf ("frameless function: stack size %d, register count %d ", stack_size, register_count); - } + stack_size += stack_adjust * 8; - if (register_count == 0) - { - printf (" no registers saved"); - } - else - { - - // We need to include (up to) 6 registers in 10 bits. - // That would be 18 bits if we just used 3 bits per reg to indicate - // the order they're saved on the stack. - // - // This is done with Lehmer code permutation, e.g. see - // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms - int permunreg[6]; - - // This decodes the variable-base number in the 10 bits - // and gives us the Lehmer code sequence which can then - // be decoded. - - switch (register_count) - { - case 6: - permunreg[0] = permutation/120; // 120 == 5! - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; // 24 == 4! - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; // 6 == 3! - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; // 2 == 2! - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; // 1 == 1! - permunreg[5] = 0; - break; - case 5: - permunreg[0] = permutation/120; - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; - break; - case 4: - permunreg[0] = permutation/60; - permutation -= (permunreg[0]*60); - permunreg[1] = permutation/12; - permutation -= (permunreg[1]*12); - permunreg[2] = permutation/3; - permutation -= (permunreg[2]*3); - permunreg[3] = permutation; - break; - case 3: - permunreg[0] = permutation/20; - permutation -= (permunreg[0]*20); - permunreg[1] = permutation/4; - permutation -= (permunreg[1]*4); - permunreg[2] = permutation; - break; - case 2: - permunreg[0] = permutation/5; - permutation -= (permunreg[0]*5); - permunreg[1] = permutation; - break; - case 1: - permunreg[0] = permutation; - break; - } - - // Decode the Lehmer code for this permutation of - // the registers v. http://en.wikipedia.org/wiki/Lehmer_code - - int registers[6]; - bool used[7] = { false, false, false, false, false, false, false }; - for (int i = 0; i < register_count; i++) - { - int renum = 0; - for (int j = 1; j < 7; j++) - { - if (used[j] == false) - { - if (renum == permunreg[i]) - { - registers[i] = j; - used[j] = true; - break; - } - renum++; - } - } - } - - - if (mode == UNWIND_X86_64_MODE_STACK_IND) - { - printf (" CFA is rsp+%d ", stack_size); - } - else - { - printf (" CFA is rsp+%d ", stack_size * 8); - } - - uint32_t saved_registers_offset = 1; - printf (" rip=[CFA-%d]", saved_registers_offset * 8); - saved_registers_offset++; - - for (int i = (sizeof (registers) / sizeof (int)) - 1; i >= 0; i--) - { - switch (registers[i]) - { - case UNWIND_X86_64_REG_NONE: - break; - case UNWIND_X86_64_REG_RBX: - printf (" rbx=[CFA-%d]", saved_registers_offset * 8); - saved_registers_offset++; - break; - case UNWIND_X86_64_REG_R12: - printf (" r12=[CFA-%d]", saved_registers_offset * 8); - saved_registers_offset++; - break; - case UNWIND_X86_64_REG_R13: - printf (" r13=[CFA-%d]", saved_registers_offset * 8); - saved_registers_offset++; - break; - case UNWIND_X86_64_REG_R14: - printf (" r14=[CFA-%d]", saved_registers_offset * 8); - saved_registers_offset++; - break; - case UNWIND_X86_64_REG_R15: - printf (" r15=[CFA-%d]", saved_registers_offset * 8); - saved_registers_offset++; - break; - case UNWIND_X86_64_REG_RBP: - printf (" rbp=[CFA-%d]", saved_registers_offset * 8); - saved_registers_offset++; - break; - } - } + printf("large stack "); + } - } + if (mode == UNWIND_X86_64_MODE_STACK_IND) { + printf("frameless function: stack size %d, register count %d ", + stack_size * 8, register_count); + } else { + printf("frameless function: stack size %d, register count %d ", + stack_size, register_count); + } - } + if (register_count == 0) { + printf(" no registers saved"); + } else { + + // We need to include (up to) 6 registers in 10 bits. + // That would be 18 bits if we just used 3 bits per reg to indicate + // the order they're saved on the stack. + // + // This is done with Lehmer code permutation, e.g. see + // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms + int permunreg[6]; + + // This decodes the variable-base number in the 10 bits + // and gives us the Lehmer code sequence which can then + // be decoded. + + switch (register_count) { + case 6: + permunreg[0] = permutation / 120; // 120 == 5! + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; // 24 == 4! + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; // 6 == 3! + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; // 2 == 2! + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; // 1 == 1! + permunreg[5] = 0; break; - - case UNWIND_X86_64_MODE_DWARF: - { - uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; - printf ("DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 ")", - dwarf_offset, dwarf_offset + baton.eh_section_file_address); - } + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; break; - - case 0: - { - printf (" no unwind information"); - } + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; break; - } -} - -void -print_encoding_i386 (struct baton baton, uint8_t *function_start, uint32_t encoding) -{ - int mode = encoding & UNWIND_X86_MODE_MASK; - switch (mode) - { - case UNWIND_X86_MODE_EBP_FRAME: - { - printf ("frame func: CFA is ebp+%d ", 8); - printf (" eip=[CFA-4] ebp=[CFA-8]"); - uint32_t saved_registers_offset = EXTRACT_BITS (encoding, UNWIND_X86_EBP_FRAME_OFFSET); - - uint32_t saved_registers_locations = EXTRACT_BITS (encoding, UNWIND_X86_EBP_FRAME_REGISTERS); - - - saved_registers_offset += 2; - - for (int i = 0; i < 5; i++) - { - switch (saved_registers_locations & 0x7) - { - case UNWIND_X86_REG_NONE: - break; - case UNWIND_X86_REG_EBX: - printf (" ebx=[CFA-%d]", saved_registers_offset * 4); - break; - case UNWIND_X86_REG_ECX: - printf (" ecx=[CFA-%d]", saved_registers_offset * 4); - break; - case UNWIND_X86_REG_EDX: - printf (" edx=[CFA-%d]", saved_registers_offset * 4); - break; - case UNWIND_X86_REG_EDI: - printf (" edi=[CFA-%d]", saved_registers_offset * 4); - break; - case UNWIND_X86_REG_ESI: - printf (" esi=[CFA-%d]", saved_registers_offset * 4); - break; - } - saved_registers_offset--; - saved_registers_locations >>= 3; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + + // Decode the Lehmer code for this permutation of + // the registers v. http://en.wikipedia.org/wiki/Lehmer_code + + int registers[6]; + bool used[7] = {false, false, false, false, false, false, false}; + for (int i = 0; i < register_count; i++) { + int renum = 0; + for (int j = 1; j < 7; j++) { + if (used[j] == false) { + if (renum == permunreg[i]) { + registers[i] = j; + used[j] = true; + break; } + renum++; + } } - break; + } + + if (mode == UNWIND_X86_64_MODE_STACK_IND) { + printf(" CFA is rsp+%d ", stack_size); + } else { + printf(" CFA is rsp+%d ", stack_size * 8); + } + + uint32_t saved_registers_offset = 1; + printf(" rip=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + + for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { + switch (registers[i]) { + case UNWIND_X86_64_REG_NONE: + break; + case UNWIND_X86_64_REG_RBX: + printf(" rbx=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R12: + printf(" r12=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R13: + printf(" r13=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R14: + printf(" r14=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R15: + printf(" r15=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_RBP: + printf(" rbp=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + } + } + } - case UNWIND_X86_MODE_STACK_IND: - case UNWIND_X86_MODE_STACK_IMMD: - { - uint32_t stack_size = EXTRACT_BITS (encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); - uint32_t register_count = EXTRACT_BITS (encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); - uint32_t permutation = EXTRACT_BITS (encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + } break; - if (mode == UNWIND_X86_MODE_STACK_IND && function_start) - { - uint32_t stack_adjust = EXTRACT_BITS (encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + case UNWIND_X86_64_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; - // offset into the function instructions; 0 == beginning of first instruction - uint32_t offset_to_subl_insn = EXTRACT_BITS (encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + case 0: { + printf(" no unwind information"); + } break; + } +} - stack_size = *((uint32_t*) (function_start + offset_to_subl_insn)); +void print_encoding_i386(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + int mode = encoding & UNWIND_X86_MODE_MASK; + switch (mode) { + case UNWIND_X86_MODE_EBP_FRAME: { + printf("frame func: CFA is ebp+%d ", 8); + printf(" eip=[CFA-4] ebp=[CFA-8]"); + uint32_t saved_registers_offset = + EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET); - stack_size += stack_adjust * 4; + uint32_t saved_registers_locations = + EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS); - printf ("large stack "); - } - - if (mode == UNWIND_X86_MODE_STACK_IND) - { - printf ("frameless function: stack size %d, register count %d ", stack_size, register_count); - } - else - { - printf ("frameless function: stack size %d, register count %d ", stack_size * 4, register_count); - } - - if (register_count == 0) - { - printf (" no registers saved"); - } - else - { - - // We need to include (up to) 6 registers in 10 bits. - // That would be 18 bits if we just used 3 bits per reg to indicate - // the order they're saved on the stack. - // - // This is done with Lehmer code permutation, e.g. see - // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms - int permunreg[6]; - - // This decodes the variable-base number in the 10 bits - // and gives us the Lehmer code sequence which can then - // be decoded. - - switch (register_count) - { - case 6: - permunreg[0] = permutation/120; // 120 == 5! - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; // 24 == 4! - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; // 6 == 3! - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; // 2 == 2! - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; // 1 == 1! - permunreg[5] = 0; - break; - case 5: - permunreg[0] = permutation/120; - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; - break; - case 4: - permunreg[0] = permutation/60; - permutation -= (permunreg[0]*60); - permunreg[1] = permutation/12; - permutation -= (permunreg[1]*12); - permunreg[2] = permutation/3; - permutation -= (permunreg[2]*3); - permunreg[3] = permutation; - break; - case 3: - permunreg[0] = permutation/20; - permutation -= (permunreg[0]*20); - permunreg[1] = permutation/4; - permutation -= (permunreg[1]*4); - permunreg[2] = permutation; - break; - case 2: - permunreg[0] = permutation/5; - permutation -= (permunreg[0]*5); - permunreg[1] = permutation; - break; - case 1: - permunreg[0] = permutation; - break; - } - - // Decode the Lehmer code for this permutation of - // the registers v. http://en.wikipedia.org/wiki/Lehmer_code - - int registers[6]; - bool used[7] = { false, false, false, false, false, false, false }; - for (int i = 0; i < register_count; i++) - { - int renum = 0; - for (int j = 1; j < 7; j++) - { - if (used[j] == false) - { - if (renum == permunreg[i]) - { - registers[i] = j; - used[j] = true; - break; - } - renum++; - } - } - } - - - if (mode == UNWIND_X86_MODE_STACK_IND) - { - printf (" CFA is esp+%d ", stack_size); - } - else - { - printf (" CFA is esp+%d ", stack_size * 4); - } - - uint32_t saved_registers_offset = 1; - printf (" eip=[CFA-%d]", saved_registers_offset * 4); - saved_registers_offset++; - - for (int i = (sizeof (registers) / sizeof (int)) - 1; i >= 0; i--) - { - switch (registers[i]) - { - case UNWIND_X86_REG_NONE: - break; - case UNWIND_X86_REG_EBX: - printf (" ebx=[CFA-%d]", saved_registers_offset * 4); - saved_registers_offset++; - break; - case UNWIND_X86_REG_ECX: - printf (" ecx=[CFA-%d]", saved_registers_offset * 4); - saved_registers_offset++; - break; - case UNWIND_X86_REG_EDX: - printf (" edx=[CFA-%d]", saved_registers_offset * 4); - saved_registers_offset++; - break; - case UNWIND_X86_REG_EDI: - printf (" edi=[CFA-%d]", saved_registers_offset * 4); - saved_registers_offset++; - break; - case UNWIND_X86_REG_ESI: - printf (" esi=[CFA-%d]", saved_registers_offset * 4); - saved_registers_offset++; - break; - case UNWIND_X86_REG_EBP: - printf (" ebp=[CFA-%d]", saved_registers_offset * 4); - saved_registers_offset++; - break; - } - } + saved_registers_offset += 2; - } - - } + for (int i = 0; i < 5; i++) { + switch (saved_registers_locations & 0x7) { + case UNWIND_X86_REG_NONE: break; - - case UNWIND_X86_MODE_DWARF: - { - uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; - printf ("DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 ")", - dwarf_offset, dwarf_offset + baton.eh_section_file_address); - } + case UNWIND_X86_REG_EBX: + printf(" ebx=[CFA-%d]", saved_registers_offset * 4); break; - - case 0: - { - printf (" no unwind information"); - } + case UNWIND_X86_REG_ECX: + printf(" ecx=[CFA-%d]", saved_registers_offset * 4); + break; + case UNWIND_X86_REG_EDX: + printf(" edx=[CFA-%d]", saved_registers_offset * 4); + break; + case UNWIND_X86_REG_EDI: + printf(" edi=[CFA-%d]", saved_registers_offset * 4); + break; + case UNWIND_X86_REG_ESI: + printf(" esi=[CFA-%d]", saved_registers_offset * 4); break; + } + saved_registers_offset--; + saved_registers_locations >>= 3; } -} + } break; -void -print_encoding_arm64 (struct baton baton, uint8_t *function_start, uint32_t encoding) -{ - const int wordsize = 8; - int mode = encoding & UNWIND_ARM64_MODE_MASK; - switch (mode) - { - case UNWIND_ARM64_MODE_FRAME: - { - printf ("frame func: CFA is fp+%d ", 16); - printf (" pc=[CFA-8] fp=[CFA-16]"); - int reg_pairs_saved_count = 1; - uint32_t saved_register_bits = encoding & 0xfff; - if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" x19=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" x20=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" x21=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" x22=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" x23=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" x24=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" x25=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" x26=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" x27=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" x28=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" d8=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" d9=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" d10=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" d11=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" d12=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" d13=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } - if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) - { - int cfa_offset = reg_pairs_saved_count * -2 * wordsize; - cfa_offset -= wordsize; - printf (" d14=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" d15=[CFA%d]", cfa_offset); - reg_pairs_saved_count++; - } + case UNWIND_X86_MODE_STACK_IND: + case UNWIND_X86_MODE_STACK_IMMD: { + uint32_t stack_size = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t register_count = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); - } - break; + if (mode == UNWIND_X86_MODE_STACK_IND && function_start) { + uint32_t stack_adjust = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); - case UNWIND_ARM64_MODE_FRAMELESS: - { - uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK; - printf ("frameless function: stack size %d ", stack_size * 16); + // offset into the function instructions; 0 == beginning of first + // instruction + uint32_t offset_to_subl_insn = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); - } - break; + stack_size = *((uint32_t *)(function_start + offset_to_subl_insn)); - case UNWIND_ARM64_MODE_DWARF: - { - uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET; - printf ("DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 ")", - dwarf_offset, dwarf_offset + baton.eh_section_file_address); - } - break; + stack_size += stack_adjust * 4; - case 0: - { - printf (" no unwind information"); - } - break; + printf("large stack "); } -} -void -print_encoding_armv7 (struct baton baton, uint8_t *function_start, uint32_t encoding) -{ - const int wordsize = 4; - int mode = encoding & UNWIND_ARM_MODE_MASK; - switch (mode) - { - case UNWIND_ARM_MODE_FRAME_D: - case UNWIND_ARM_MODE_FRAME: - { - int stack_adjust = EXTRACT_BITS (encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize; - - printf ("frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust); - int cfa_offset = -stack_adjust; - - cfa_offset -= wordsize; - printf (" pc=[CFA%d]", cfa_offset); - cfa_offset -= wordsize; - printf (" fp=[CFA%d]", cfa_offset); - - uint32_t saved_register_bits = encoding & 0xff; - if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) - { - cfa_offset -= wordsize; - printf (" r6=[CFA%d]", cfa_offset); - } - if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) - { - cfa_offset -= wordsize; - printf (" r5=[CFA%d]", cfa_offset); - } - if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) - { - cfa_offset -= wordsize; - printf (" r4=[CFA%d]", cfa_offset); - } - if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) - { - cfa_offset -= wordsize; - printf (" r12=[CFA%d]", cfa_offset); - } - if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) - { - cfa_offset -= wordsize; - printf (" r11=[CFA%d]", cfa_offset); - } - if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) - { - cfa_offset -= wordsize; - printf (" r10=[CFA%d]", cfa_offset); - } - if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) - { - cfa_offset -= wordsize; - printf (" r9=[CFA%d]", cfa_offset); - } - if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) - { - cfa_offset -= wordsize; - printf (" r8=[CFA%d]", cfa_offset); - } + if (mode == UNWIND_X86_MODE_STACK_IND) { + printf("frameless function: stack size %d, register count %d ", + stack_size, register_count); + } else { + printf("frameless function: stack size %d, register count %d ", + stack_size * 4, register_count); + } - if (mode == UNWIND_ARM_MODE_FRAME_D) - { - uint32_t d_reg_bits = EXTRACT_BITS (encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK); - switch (d_reg_bits) - { - case 0: - // vpush {d8} - cfa_offset -= 8; - printf (" d8=[CFA%d]", cfa_offset); - break; - case 1: - // vpush {d10} - // vpush {d8} - cfa_offset -= 8; - printf (" d10=[CFA%d]", cfa_offset); - cfa_offset -= 8; - printf (" d8=[CFA%d]", cfa_offset); - break; - case 2: - // vpush {d12} - // vpush {d10} - // vpush {d8} - cfa_offset -= 8; - printf (" d12=[CFA%d]", cfa_offset); - cfa_offset -= 8; - printf (" d10=[CFA%d]", cfa_offset); - cfa_offset -= 8; - printf (" d8=[CFA%d]", cfa_offset); - break; - case 3: - // vpush {d14} - // vpush {d12} - // vpush {d10} - // vpush {d8} - cfa_offset -= 8; - printf (" d14=[CFA%d]", cfa_offset); - cfa_offset -= 8; - printf (" d12=[CFA%d]", cfa_offset); - cfa_offset -= 8; - printf (" d10=[CFA%d]", cfa_offset); - cfa_offset -= 8; - printf (" d8=[CFA%d]", cfa_offset); - break; - case 4: - // vpush {d14} - // vpush {d12} - // sp = (sp - 24) & (-16); - // vst {d8, d9, d10} - printf (" d14, d12, d10, d9, d8"); - break; - case 5: - // vpush {d14} - // sp = (sp - 40) & (-16); - // vst {d8, d9, d10, d11} - // vst {d12} - printf (" d14, d11, d10, d9, d8, d12"); - break; - case 6: - // sp = (sp - 56) & (-16); - // vst {d8, d9, d10, d11} - // vst {d12, d13, d14} - printf (" d11, d10, d9, d8, d14, d13, d12"); - break; - case 7: - // sp = (sp - 64) & (-16); - // vst {d8, d9, d10, d11} - // vst {d12, d13, d14, d15} - printf (" d11, d10, d9, d8, d15, d14, d13, d12"); - break; - } - } - } + if (register_count == 0) { + printf(" no registers saved"); + } else { + + // We need to include (up to) 6 registers in 10 bits. + // That would be 18 bits if we just used 3 bits per reg to indicate + // the order they're saved on the stack. + // + // This is done with Lehmer code permutation, e.g. see + // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms + int permunreg[6]; + + // This decodes the variable-base number in the 10 bits + // and gives us the Lehmer code sequence which can then + // be decoded. + + switch (register_count) { + case 6: + permunreg[0] = permutation / 120; // 120 == 5! + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; // 24 == 4! + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; // 6 == 3! + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; // 2 == 2! + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; // 1 == 1! + permunreg[5] = 0; break; - - case UNWIND_ARM_MODE_DWARF: - { - uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET; - printf ("DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 ")", - dwarf_offset, dwarf_offset + baton.eh_section_file_address); - } + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; break; - - case 0: - { - printf (" no unwind information"); - } + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; break; + case 1: + permunreg[0] = permutation; + break; + } + + // Decode the Lehmer code for this permutation of + // the registers v. http://en.wikipedia.org/wiki/Lehmer_code + + int registers[6]; + bool used[7] = {false, false, false, false, false, false, false}; + for (int i = 0; i < register_count; i++) { + int renum = 0; + for (int j = 1; j < 7; j++) { + if (used[j] == false) { + if (renum == permunreg[i]) { + registers[i] = j; + used[j] = true; + break; + } + renum++; + } + } + } + + if (mode == UNWIND_X86_MODE_STACK_IND) { + printf(" CFA is esp+%d ", stack_size); + } else { + printf(" CFA is esp+%d ", stack_size * 4); + } + + uint32_t saved_registers_offset = 1; + printf(" eip=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + + for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { + switch (registers[i]) { + case UNWIND_X86_REG_NONE: + break; + case UNWIND_X86_REG_EBX: + printf(" ebx=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_ECX: + printf(" ecx=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_EDX: + printf(" edx=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_EDI: + printf(" edi=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_ESI: + printf(" esi=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_EBP: + printf(" ebp=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + } + } } -} - + } break; + case UNWIND_X86_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; -void print_encoding (struct baton baton, uint8_t *function_start, uint32_t encoding) -{ + case 0: { + printf(" no unwind information"); + } break; + } +} - if (baton.cputype == CPU_TYPE_X86_64) - { - print_encoding_x86_64 (baton, function_start, encoding); +void print_encoding_arm64(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + const int wordsize = 8; + int mode = encoding & UNWIND_ARM64_MODE_MASK; + switch (mode) { + case UNWIND_ARM64_MODE_FRAME: { + printf("frame func: CFA is fp+%d ", 16); + printf(" pc=[CFA-8] fp=[CFA-16]"); + int reg_pairs_saved_count = 1; + uint32_t saved_register_bits = encoding & 0xfff; + if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x19=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x20=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - else if (baton.cputype == CPU_TYPE_I386) - { - print_encoding_i386 (baton, function_start, encoding); + if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x21=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x22=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - else if (baton.cputype == CPU_TYPE_ARM64) - { - print_encoding_arm64 (baton, function_start, encoding); + if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x23=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x24=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - else if (baton.cputype == CPU_TYPE_ARM) - { - print_encoding_armv7 (baton, function_start, encoding); + if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x25=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x26=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - else - { - printf (" -- unsupported encoding arch -- "); + if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x27=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x28=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } -} - -void -print_function_encoding (struct baton baton, uint32_t idx, uint32_t encoding, uint32_t entry_encoding_index, uint32_t entry_func_offset) -{ - - char *entry_encoding_index_str = ""; - if (entry_encoding_index != (uint32_t) -1) - { - asprintf (&entry_encoding_index_str, ", encoding #%d", entry_encoding_index); + if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d8=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d9=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - else - { - asprintf (&entry_encoding_index_str, ""); + if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d11=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - - uint64_t file_address = baton.first_level_index_entry.functionOffset + entry_func_offset + baton.text_segment_vmaddr; - - if (baton.cputype == CPU_TYPE_ARM) - file_address = file_address & ~1; - - printf (" func [%d] offset %d (file addr 0x%" PRIx64 ")%s, encoding is 0x%x", - idx, entry_func_offset, - file_address, - entry_encoding_index_str, - encoding); - - struct symbol *symbol = NULL; - for (int i = 0; i < baton.symbols_count; i++) - { - if (i == baton.symbols_count - 1 && baton.symbols[i].file_address <= file_address) - { - symbol = &(baton.symbols[i]); - break; - } - else - { - if (baton.symbols[i].file_address <= file_address && baton.symbols[i + 1].file_address > file_address) - { - symbol = &(baton.symbols[i]); - break; - } - } + if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d12=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d13=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - - printf ("\n "); - if (symbol) - { - int offset = file_address - symbol->file_address; - - // FIXME this is a poor heuristic - if we're greater than 16 bytes past the - // start of the function, this is the unwind info for a stripped function. - // In reality the compact unwind entry may not line up exactly with the - // function bounds. - if (offset >= 0) - { - printf ("name: %s", symbol->name); - if (offset > 0) - { - printf (" + %d", offset); - } - } - printf ("\n "); + if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d14=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d15=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; } - print_encoding (baton, baton.mach_header_start + baton.first_level_index_entry.functionOffset + baton.text_section_file_offset + entry_func_offset, encoding); - - bool has_lsda = encoding & UNWIND_HAS_LSDA; - - if (has_lsda) - { - uint32_t func_offset = entry_func_offset + baton.first_level_index_entry.functionOffset; + } break; - int lsda_entry_number = -1; + case UNWIND_ARM64_MODE_FRAMELESS: { + uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK; + printf("frameless function: stack size %d ", stack_size * 16); - uint32_t low = 0; - uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) / sizeof (struct unwind_info_section_header_lsda_index_entry); + } break; - while (low < high) - { - uint32_t mid = (low + high) / 2; + case UNWIND_ARM64_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; - uint8_t *mid_lsda_entry_addr = (baton.lsda_array_start + (mid * sizeof (struct unwind_info_section_header_lsda_index_entry))); - struct unwind_info_section_header_lsda_index_entry mid_lsda_entry; - memcpy (&mid_lsda_entry, mid_lsda_entry_addr, sizeof (struct unwind_info_section_header_lsda_index_entry)); - if (mid_lsda_entry.functionOffset == func_offset) - { - lsda_entry_number = (mid_lsda_entry_addr - baton.lsda_array_start) / sizeof (struct unwind_info_section_header_lsda_index_entry); - break; - } - else if (mid_lsda_entry.functionOffset < func_offset) - { - low = mid + 1; - } - else - { - high = mid; - } - } + case 0: { + printf(" no unwind information"); + } break; + } +} - if (lsda_entry_number != -1) - { - printf (", LSDA entry #%d", lsda_entry_number); - } - else - { - printf (", LSDA entry not found"); - } +void print_encoding_armv7(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + const int wordsize = 4; + int mode = encoding & UNWIND_ARM_MODE_MASK; + switch (mode) { + case UNWIND_ARM_MODE_FRAME_D: + case UNWIND_ARM_MODE_FRAME: { + int stack_adjust = + EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize; + + printf("frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust); + int cfa_offset = -stack_adjust; + + cfa_offset -= wordsize; + printf(" pc=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" fp=[CFA%d]", cfa_offset); + + uint32_t saved_register_bits = encoding & 0xff; + if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) { + cfa_offset -= wordsize; + printf(" r6=[CFA%d]", cfa_offset); } - - uint32_t pers_idx = EXTRACT_BITS (encoding, UNWIND_PERSONALITY_MASK); - if (pers_idx != 0) - { - pers_idx--; // Change 1-based to 0-based index - printf (", personality entry #%d", pers_idx); + if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) { + cfa_offset -= wordsize; + printf(" r5=[CFA%d]", cfa_offset); } - - printf ("\n"); -} - -void -print_second_level_index_regular (struct baton baton) -{ - uint8_t *page_entries = baton.compact_unwind_start + baton.first_level_index_entry.secondLevelPagesSectionOffset + baton.regular_second_level_page_header.entryPageOffset; - uint32_t entries_count = baton.regular_second_level_page_header.entryCount; - - uint8_t *offset = page_entries; - - uint32_t idx = 0; - while (idx < entries_count) - { - uint32_t func_offset = *((uint32_t *) (offset)); - uint32_t encoding = *((uint32_t *) (offset + 4)); - - // UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the - // functionOffset from the containing index table already. UNWIND_SECOND_LEVEL_COMPRESSED - // entries only have the offset from the containing index table functionOffset. - // So strip off the containing index table functionOffset value here so they can - // be treated the same at the lower layers. - - print_function_encoding (baton, idx, encoding, (uint32_t) -1, func_offset - baton.first_level_index_entry.functionOffset); - idx++; - offset += 8; + if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) { + cfa_offset -= wordsize; + printf(" r4=[CFA%d]", cfa_offset); } -} - -void -print_second_level_index_compressed (struct baton baton) -{ - uint8_t *this_index = baton.compact_unwind_start + baton.first_level_index_entry.secondLevelPagesSectionOffset; - uint8_t *start_of_entries = this_index + baton.compressed_second_level_page_header.entryPageOffset; - uint8_t *offset = start_of_entries; - for (uint16_t idx = 0; idx < baton.compressed_second_level_page_header.entryCount; idx++) - { - uint32_t entry = *((uint32_t*) offset); - offset += 4; - uint32_t encoding; - - uint32_t entry_encoding_index = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX (entry); - uint32_t entry_func_offset = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET (entry); - - if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) - { - // encoding is in common table in section header - encoding = *((uint32_t*) (baton.compact_unwind_start + baton.unwind_header.commonEncodingsArraySectionOffset + (entry_encoding_index * sizeof (uint32_t)))); - } - else - { - // encoding is in page specific table - uint32_t page_encoding_index = entry_encoding_index - baton.unwind_header.commonEncodingsArrayCount; - encoding = *((uint32_t*) (this_index + baton.compressed_second_level_page_header.encodingsPageOffset + (page_encoding_index * sizeof (uint32_t)))); - } - - - print_function_encoding (baton, idx, encoding, entry_encoding_index, entry_func_offset); + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) { + cfa_offset -= wordsize; + printf(" r12=[CFA%d]", cfa_offset); } -} - -void -print_second_level_index (struct baton baton) -{ - uint8_t *index_start = baton.compact_unwind_start + baton.first_level_index_entry.secondLevelPagesSectionOffset; - - if ((*(uint32_t*) index_start) == UNWIND_SECOND_LEVEL_REGULAR) - { - struct unwind_info_regular_second_level_page_header header; - memcpy (&header, index_start, sizeof (struct unwind_info_regular_second_level_page_header)); - printf (" UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n", baton.current_index_table_number, header.entryPageOffset, header.entryCount); - baton.regular_second_level_page_header = header; - print_second_level_index_regular (baton); + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) { + cfa_offset -= wordsize; + printf(" r11=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) { + cfa_offset -= wordsize; + printf(" r10=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) { + cfa_offset -= wordsize; + printf(" r9=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) { + cfa_offset -= wordsize; + printf(" r8=[CFA%d]", cfa_offset); } - if ((*(uint32_t*) index_start) == UNWIND_SECOND_LEVEL_COMPRESSED) - { - struct unwind_info_compressed_second_level_page_header header; - memcpy (&header, index_start, sizeof (struct unwind_info_compressed_second_level_page_header)); - printf (" UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, entryCount %d, encodingsPageOffset %d, encodingsCount %d\n", baton.current_index_table_number, header.entryPageOffset, header.entryCount, header.encodingsPageOffset, header.encodingsCount); - baton.compressed_second_level_page_header = header; - print_second_level_index_compressed (baton); + if (mode == UNWIND_ARM_MODE_FRAME_D) { + uint32_t d_reg_bits = + EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK); + switch (d_reg_bits) { + case 0: + // vpush {d8} + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 1: + // vpush {d10} + // vpush {d8} + cfa_offset -= 8; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 2: + // vpush {d12} + // vpush {d10} + // vpush {d8} + cfa_offset -= 8; + printf(" d12=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 3: + // vpush {d14} + // vpush {d12} + // vpush {d10} + // vpush {d8} + cfa_offset -= 8; + printf(" d14=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d12=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 4: + // vpush {d14} + // vpush {d12} + // sp = (sp - 24) & (-16); + // vst {d8, d9, d10} + printf(" d14, d12, d10, d9, d8"); + break; + case 5: + // vpush {d14} + // sp = (sp - 40) & (-16); + // vst {d8, d9, d10, d11} + // vst {d12} + printf(" d14, d11, d10, d9, d8, d12"); + break; + case 6: + // sp = (sp - 56) & (-16); + // vst {d8, d9, d10, d11} + // vst {d12, d13, d14} + printf(" d11, d10, d9, d8, d14, d13, d12"); + break; + case 7: + // sp = (sp - 64) & (-16); + // vst {d8, d9, d10, d11} + // vst {d12, d13, d14, d15} + printf(" d11, d10, d9, d8, d15, d14, d13, d12"); + break; + } } + } break; + + case UNWIND_ARM_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; + + case 0: { + printf(" no unwind information"); + } break; + } } - -void -print_index_sections (struct baton baton) -{ - uint8_t *index_section_offset = baton.compact_unwind_start + baton.unwind_header.indexSectionOffset; - uint32_t index_count = baton.unwind_header.indexCount; - - uint32_t cur_idx = 0; - - uint8_t *offset = index_section_offset; - while (cur_idx < index_count) - { - baton.current_index_table_number = cur_idx; - struct unwind_info_section_header_index_entry index_entry; - memcpy (&index_entry, offset, sizeof (struct unwind_info_section_header_index_entry)); - printf ("index section #%d: functionOffset %d, secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n", cur_idx, index_entry.functionOffset, index_entry.secondLevelPagesSectionOffset, index_entry.lsdaIndexArraySectionOffset); - - // secondLevelPagesSectionOffset == 0 means this is a sentinel entry - if (index_entry.secondLevelPagesSectionOffset != 0) - { - struct unwind_info_section_header_index_entry next_index_entry; - memcpy (&next_index_entry, offset + sizeof (struct unwind_info_section_header_index_entry), sizeof (struct unwind_info_section_header_index_entry)); - - baton.lsda_array_start = baton.compact_unwind_start + index_entry.lsdaIndexArraySectionOffset; - baton.lsda_array_end = baton.compact_unwind_start + next_index_entry.lsdaIndexArraySectionOffset; - - uint8_t *lsda_entry_offset = baton.lsda_array_start; - uint32_t lsda_count = 0; - while (lsda_entry_offset < baton.lsda_array_end) - { - struct unwind_info_section_header_lsda_index_entry lsda_entry; - memcpy (&lsda_entry, lsda_entry_offset, sizeof (struct unwind_info_section_header_lsda_index_entry)); - uint64_t function_file_address = baton.first_level_index_entry.functionOffset + lsda_entry.functionOffset + baton.text_segment_vmaddr; - uint64_t lsda_file_address = lsda_entry.lsdaOffset + baton.text_segment_vmaddr; - printf (" LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx64 "), lsdaOffset %d (file address 0x%" PRIx64 ")\n", - lsda_count, lsda_entry.functionOffset, - lsda_entry.functionOffset - index_entry.functionOffset, - function_file_address, - lsda_entry.lsdaOffset, lsda_file_address); - lsda_count++; - lsda_entry_offset += sizeof (struct unwind_info_section_header_lsda_index_entry); - } - - printf ("\n"); - - baton.first_level_index_entry = index_entry; - print_second_level_index (baton); - } - - printf ("\n"); - - cur_idx++; - offset += sizeof (struct unwind_info_section_header_index_entry); - } +void print_encoding(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + + if (baton.cputype == CPU_TYPE_X86_64) { + print_encoding_x86_64(baton, function_start, encoding); + } else if (baton.cputype == CPU_TYPE_I386) { + print_encoding_i386(baton, function_start, encoding); + } else if (baton.cputype == CPU_TYPE_ARM64) { + print_encoding_arm64(baton, function_start, encoding); + } else if (baton.cputype == CPU_TYPE_ARM) { + print_encoding_armv7(baton, function_start, encoding); + } else { + printf(" -- unsupported encoding arch -- "); + } } -int main (int argc, char **argv) -{ - struct stat st; - char *file = argv[0]; - if (argc > 1) - file = argv[1]; - int fd = open (file, O_RDONLY); - if (fd == -1) - { - printf ("Failed to open '%s'\n", file); - exit (1); +void print_function_encoding(struct baton baton, uint32_t idx, + uint32_t encoding, uint32_t entry_encoding_index, + uint32_t entry_func_offset) { + + char *entry_encoding_index_str = ""; + if (entry_encoding_index != (uint32_t)-1) { + asprintf(&entry_encoding_index_str, ", encoding #%d", entry_encoding_index); + } else { + asprintf(&entry_encoding_index_str, ""); + } + + uint64_t file_address = baton.first_level_index_entry.functionOffset + + entry_func_offset + baton.text_segment_vmaddr; + + if (baton.cputype == CPU_TYPE_ARM) + file_address = file_address & ~1; + + printf( + " func [%d] offset %d (file addr 0x%" PRIx64 ")%s, encoding is 0x%x", + idx, entry_func_offset, file_address, entry_encoding_index_str, encoding); + + struct symbol *symbol = NULL; + for (int i = 0; i < baton.symbols_count; i++) { + if (i == baton.symbols_count - 1 && + baton.symbols[i].file_address <= file_address) { + symbol = &(baton.symbols[i]); + break; + } else { + if (baton.symbols[i].file_address <= file_address && + baton.symbols[i + 1].file_address > file_address) { + symbol = &(baton.symbols[i]); + break; + } } - fstat (fd, &st); - uint8_t *file_mem = (uint8_t*) mmap (0, st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0); - if (file_mem == MAP_FAILED) - { - printf ("Failed to mmap() '%s'\n", file); + } + + printf("\n "); + if (symbol) { + int offset = file_address - symbol->file_address; + + // FIXME this is a poor heuristic - if we're greater than 16 bytes past the + // start of the function, this is the unwind info for a stripped function. + // In reality the compact unwind entry may not line up exactly with the + // function bounds. + if (offset >= 0) { + printf("name: %s", symbol->name); + if (offset > 0) { + printf(" + %d", offset); + } + } + printf("\n "); + } + + print_encoding(baton, baton.mach_header_start + + baton.first_level_index_entry.functionOffset + + baton.text_section_file_offset + entry_func_offset, + encoding); + + bool has_lsda = encoding & UNWIND_HAS_LSDA; + + if (has_lsda) { + uint32_t func_offset = + entry_func_offset + baton.first_level_index_entry.functionOffset; + + int lsda_entry_number = -1; + + uint32_t low = 0; + uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) / + sizeof(struct unwind_info_section_header_lsda_index_entry); + + while (low < high) { + uint32_t mid = (low + high) / 2; + + uint8_t *mid_lsda_entry_addr = + (baton.lsda_array_start + + (mid * sizeof(struct unwind_info_section_header_lsda_index_entry))); + struct unwind_info_section_header_lsda_index_entry mid_lsda_entry; + memcpy(&mid_lsda_entry, mid_lsda_entry_addr, + sizeof(struct unwind_info_section_header_lsda_index_entry)); + if (mid_lsda_entry.functionOffset == func_offset) { + lsda_entry_number = + (mid_lsda_entry_addr - baton.lsda_array_start) / + sizeof(struct unwind_info_section_header_lsda_index_entry); + break; + } else if (mid_lsda_entry.functionOffset < func_offset) { + low = mid + 1; + } else { + high = mid; + } } - FILE *f = fopen ("a.out", "r"); - - struct baton baton; - baton.mach_header_start = file_mem; - baton.symbols = NULL; - baton.symbols_count = 0; - baton.function_start_addresses = NULL; - baton.function_start_addresses_count = 0; + if (lsda_entry_number != -1) { + printf(", LSDA entry #%d", lsda_entry_number); + } else { + printf(", LSDA entry not found"); + } + } - scan_macho_load_commands (&baton); + uint32_t pers_idx = EXTRACT_BITS(encoding, UNWIND_PERSONALITY_MASK); + if (pers_idx != 0) { + pers_idx--; // Change 1-based to 0-based index + printf(", personality entry #%d", pers_idx); + } - if (baton.compact_unwind_start == NULL) - { - printf ("could not find __TEXT,__unwind_info section\n"); - exit (1); - } + printf("\n"); +} +void print_second_level_index_regular(struct baton baton) { + uint8_t *page_entries = + baton.compact_unwind_start + + baton.first_level_index_entry.secondLevelPagesSectionOffset + + baton.regular_second_level_page_header.entryPageOffset; + uint32_t entries_count = baton.regular_second_level_page_header.entryCount; + + uint8_t *offset = page_entries; + + uint32_t idx = 0; + while (idx < entries_count) { + uint32_t func_offset = *((uint32_t *)(offset)); + uint32_t encoding = *((uint32_t *)(offset + 4)); + + // UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the + // functionOffset from the containing index table already. + // UNWIND_SECOND_LEVEL_COMPRESSED + // entries only have the offset from the containing index table + // functionOffset. + // So strip off the containing index table functionOffset value here so they + // can + // be treated the same at the lower layers. + + print_function_encoding(baton, idx, encoding, (uint32_t)-1, + func_offset - + baton.first_level_index_entry.functionOffset); + idx++; + offset += 8; + } +} - struct unwind_info_section_header header; - memcpy (&header, baton.compact_unwind_start, sizeof (struct unwind_info_section_header)); - printf ("Header:\n"); - printf (" version %u\n", header.version); - printf (" commonEncodingsArraySectionOffset is %d\n", header.commonEncodingsArraySectionOffset); - printf (" commonEncodingsArrayCount is %d\n", header.commonEncodingsArrayCount); - printf (" personalityArraySectionOffset is %d\n", header.personalityArraySectionOffset); - printf (" personalityArrayCount is %d\n", header.personalityArrayCount); - printf (" indexSectionOffset is %d\n", header.indexSectionOffset); - printf (" indexCount is %d\n", header.indexCount); - - uint8_t *common_encodings = baton.compact_unwind_start + header.commonEncodingsArraySectionOffset; - uint32_t encoding_idx = 0; - while (encoding_idx < header.commonEncodingsArrayCount) - { - uint32_t encoding = *((uint32_t*) common_encodings); - printf (" Common Encoding [%d]: 0x%x ", encoding_idx, encoding); - print_encoding (baton, NULL, encoding); - printf ("\n"); - common_encodings += sizeof (uint32_t); - encoding_idx++; +void print_second_level_index_compressed(struct baton baton) { + uint8_t *this_index = + baton.compact_unwind_start + + baton.first_level_index_entry.secondLevelPagesSectionOffset; + uint8_t *start_of_entries = + this_index + baton.compressed_second_level_page_header.entryPageOffset; + uint8_t *offset = start_of_entries; + for (uint16_t idx = 0; + idx < baton.compressed_second_level_page_header.entryCount; idx++) { + uint32_t entry = *((uint32_t *)offset); + offset += 4; + uint32_t encoding; + + uint32_t entry_encoding_index = + UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry); + uint32_t entry_func_offset = + UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry); + + if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) { + // encoding is in common table in section header + encoding = + *((uint32_t *)(baton.compact_unwind_start + + baton.unwind_header.commonEncodingsArraySectionOffset + + (entry_encoding_index * sizeof(uint32_t)))); + } else { + // encoding is in page specific table + uint32_t page_encoding_index = + entry_encoding_index - baton.unwind_header.commonEncodingsArrayCount; + encoding = *((uint32_t *)(this_index + + baton.compressed_second_level_page_header + .encodingsPageOffset + + (page_encoding_index * sizeof(uint32_t)))); } - uint8_t *pers_arr = baton.compact_unwind_start + header.personalityArraySectionOffset; - uint32_t pers_idx = 0; - while (pers_idx < header.personalityArrayCount) - { - int32_t pers_delta = *((int32_t*) (baton.compact_unwind_start + header.personalityArraySectionOffset + (pers_idx * sizeof (uint32_t)))); - printf (" Personality [%d]: personality function ptr @ offset %d (file address 0x%" PRIx64 ")\n", pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta); - pers_idx++; - pers_arr += sizeof (uint32_t); - } + print_function_encoding(baton, idx, encoding, entry_encoding_index, + entry_func_offset); + } +} - printf ("\n"); +void print_second_level_index(struct baton baton) { + uint8_t *index_start = + baton.compact_unwind_start + + baton.first_level_index_entry.secondLevelPagesSectionOffset; + + if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_REGULAR) { + struct unwind_info_regular_second_level_page_header header; + memcpy(&header, index_start, + sizeof(struct unwind_info_regular_second_level_page_header)); + printf( + " UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n", + baton.current_index_table_number, header.entryPageOffset, + header.entryCount); + baton.regular_second_level_page_header = header; + print_second_level_index_regular(baton); + } + + if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_COMPRESSED) { + struct unwind_info_compressed_second_level_page_header header; + memcpy(&header, index_start, + sizeof(struct unwind_info_compressed_second_level_page_header)); + printf(" UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, " + "entryCount %d, encodingsPageOffset %d, encodingsCount %d\n", + baton.current_index_table_number, header.entryPageOffset, + header.entryCount, header.encodingsPageOffset, + header.encodingsCount); + baton.compressed_second_level_page_header = header; + print_second_level_index_compressed(baton); + } +} - baton.unwind_header = header; +void print_index_sections(struct baton baton) { + uint8_t *index_section_offset = + baton.compact_unwind_start + baton.unwind_header.indexSectionOffset; + uint32_t index_count = baton.unwind_header.indexCount; + + uint32_t cur_idx = 0; + + uint8_t *offset = index_section_offset; + while (cur_idx < index_count) { + baton.current_index_table_number = cur_idx; + struct unwind_info_section_header_index_entry index_entry; + memcpy(&index_entry, offset, + sizeof(struct unwind_info_section_header_index_entry)); + printf("index section #%d: functionOffset %d, " + "secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n", + cur_idx, index_entry.functionOffset, + index_entry.secondLevelPagesSectionOffset, + index_entry.lsdaIndexArraySectionOffset); + + // secondLevelPagesSectionOffset == 0 means this is a sentinel entry + if (index_entry.secondLevelPagesSectionOffset != 0) { + struct unwind_info_section_header_index_entry next_index_entry; + memcpy(&next_index_entry, + offset + sizeof(struct unwind_info_section_header_index_entry), + sizeof(struct unwind_info_section_header_index_entry)); + + baton.lsda_array_start = + baton.compact_unwind_start + index_entry.lsdaIndexArraySectionOffset; + baton.lsda_array_end = baton.compact_unwind_start + + next_index_entry.lsdaIndexArraySectionOffset; + + uint8_t *lsda_entry_offset = baton.lsda_array_start; + uint32_t lsda_count = 0; + while (lsda_entry_offset < baton.lsda_array_end) { + struct unwind_info_section_header_lsda_index_entry lsda_entry; + memcpy(&lsda_entry, lsda_entry_offset, + sizeof(struct unwind_info_section_header_lsda_index_entry)); + uint64_t function_file_address = + baton.first_level_index_entry.functionOffset + + lsda_entry.functionOffset + baton.text_segment_vmaddr; + uint64_t lsda_file_address = + lsda_entry.lsdaOffset + baton.text_segment_vmaddr; + printf(" LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx64 + "), lsdaOffset %d (file address 0x%" PRIx64 ")\n", + lsda_count, lsda_entry.functionOffset, + lsda_entry.functionOffset - index_entry.functionOffset, + function_file_address, lsda_entry.lsdaOffset, lsda_file_address); + lsda_count++; + lsda_entry_offset += + sizeof(struct unwind_info_section_header_lsda_index_entry); + } + + printf("\n"); + + baton.first_level_index_entry = index_entry; + print_second_level_index(baton); + } - print_index_sections (baton); + printf("\n"); + cur_idx++; + offset += sizeof(struct unwind_info_section_header_index_entry); + } +} - return 0; +int main(int argc, char **argv) { + struct stat st; + char *file = argv[0]; + if (argc > 1) + file = argv[1]; + int fd = open(file, O_RDONLY); + if (fd == -1) { + printf("Failed to open '%s'\n", file); + exit(1); + } + fstat(fd, &st); + uint8_t *file_mem = + (uint8_t *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0); + if (file_mem == MAP_FAILED) { + printf("Failed to mmap() '%s'\n", file); + } + + FILE *f = fopen("a.out", "r"); + + struct baton baton; + baton.mach_header_start = file_mem; + baton.symbols = NULL; + baton.symbols_count = 0; + baton.function_start_addresses = NULL; + baton.function_start_addresses_count = 0; + + scan_macho_load_commands(&baton); + + if (baton.compact_unwind_start == NULL) { + printf("could not find __TEXT,__unwind_info section\n"); + exit(1); + } + + struct unwind_info_section_header header; + memcpy(&header, baton.compact_unwind_start, + sizeof(struct unwind_info_section_header)); + printf("Header:\n"); + printf(" version %u\n", header.version); + printf(" commonEncodingsArraySectionOffset is %d\n", + header.commonEncodingsArraySectionOffset); + printf(" commonEncodingsArrayCount is %d\n", + header.commonEncodingsArrayCount); + printf(" personalityArraySectionOffset is %d\n", + header.personalityArraySectionOffset); + printf(" personalityArrayCount is %d\n", header.personalityArrayCount); + printf(" indexSectionOffset is %d\n", header.indexSectionOffset); + printf(" indexCount is %d\n", header.indexCount); + + uint8_t *common_encodings = + baton.compact_unwind_start + header.commonEncodingsArraySectionOffset; + uint32_t encoding_idx = 0; + while (encoding_idx < header.commonEncodingsArrayCount) { + uint32_t encoding = *((uint32_t *)common_encodings); + printf(" Common Encoding [%d]: 0x%x ", encoding_idx, encoding); + print_encoding(baton, NULL, encoding); + printf("\n"); + common_encodings += sizeof(uint32_t); + encoding_idx++; + } + + uint8_t *pers_arr = + baton.compact_unwind_start + header.personalityArraySectionOffset; + uint32_t pers_idx = 0; + while (pers_idx < header.personalityArrayCount) { + int32_t pers_delta = *((int32_t *)(baton.compact_unwind_start + + header.personalityArraySectionOffset + + (pers_idx * sizeof(uint32_t)))); + printf(" Personality [%d]: personality function ptr @ offset %d (file " + "address 0x%" PRIx64 ")\n", + pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta); + pers_idx++; + pers_arr += sizeof(uint32_t); + } + + printf("\n"); + + baton.unwind_header = header; + + print_index_sections(baton); + + return 0; } |