xref: /freebsd/contrib/llvm-project/lldb/tools/compact-unwind/compact-unwind-dumper.c (revision 349cc55c9796c4596a5b9904cd3281af295f878f)
10b57cec5SDimitry Andric #include <fcntl.h>
20b57cec5SDimitry Andric #include <inttypes.h>
30b57cec5SDimitry Andric #include <mach-o/compact_unwind_encoding.h>
40b57cec5SDimitry Andric #include <mach-o/loader.h>
50b57cec5SDimitry Andric #include <mach-o/nlist.h>
60b57cec5SDimitry Andric #include <mach/machine.h>
70b57cec5SDimitry Andric #include <stdbool.h>
80b57cec5SDimitry Andric #include <stdint.h>
90b57cec5SDimitry Andric #include <stdio.h>
100b57cec5SDimitry Andric #include <stdlib.h>
110b57cec5SDimitry Andric #include <string.h>
120b57cec5SDimitry Andric #include <sys/errno.h>
130b57cec5SDimitry Andric #include <sys/mman.h>
140b57cec5SDimitry Andric #include <sys/stat.h>
150b57cec5SDimitry Andric #include <sys/types.h>
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #define EXTRACT_BITS(value, mask)                                              \
180b57cec5SDimitry Andric   ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric // A quick sketch of a program which can parse the compact unwind info
210b57cec5SDimitry Andric // used on Darwin systems for exception handling.  The output of
220b57cec5SDimitry Andric // unwinddump will be more authoritative/reliable but this program
230b57cec5SDimitry Andric // can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries
240b57cec5SDimitry Andric // correctly.
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric struct symbol {
270b57cec5SDimitry Andric   uint64_t file_address;
280b57cec5SDimitry Andric   const char *name;
290b57cec5SDimitry Andric };
300b57cec5SDimitry Andric 
symbol_compare(const void * a,const void * b)310b57cec5SDimitry Andric int symbol_compare(const void *a, const void *b) {
320b57cec5SDimitry Andric   return (int)((struct symbol *)a)->file_address -
330b57cec5SDimitry Andric          ((struct symbol *)b)->file_address;
340b57cec5SDimitry Andric }
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric struct baton {
370b57cec5SDimitry Andric   cpu_type_t cputype;
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric   uint8_t *mach_header_start;    // pointer into this program's address space
400b57cec5SDimitry Andric   uint8_t *compact_unwind_start; // pointer into this program's address space
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric   int addr_size; // 4 or 8 bytes, the size of addresses in this file
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric   uint64_t text_segment_vmaddr; // __TEXT segment vmaddr
450b57cec5SDimitry Andric   uint64_t text_segment_file_offset;
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr
480b57cec5SDimitry Andric   uint64_t text_section_file_offset;
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric   uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame
510b57cec5SDimitry Andric                                     // section
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric   uint8_t
540b57cec5SDimitry Andric       *lsda_array_start; // for the currently-being-processed first-level index
550b57cec5SDimitry Andric   uint8_t
560b57cec5SDimitry Andric       *lsda_array_end; // the lsda_array_start for the NEXT first-level index
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric   struct symbol *symbols;
590b57cec5SDimitry Andric   int symbols_count;
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric   uint64_t *function_start_addresses;
620b57cec5SDimitry Andric   int function_start_addresses_count;
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric   int current_index_table_number;
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   struct unwind_info_section_header unwind_header;
670b57cec5SDimitry Andric   struct unwind_info_section_header_index_entry first_level_index_entry;
680b57cec5SDimitry Andric   struct unwind_info_compressed_second_level_page_header
690b57cec5SDimitry Andric       compressed_second_level_page_header;
700b57cec5SDimitry Andric   struct unwind_info_regular_second_level_page_header
710b57cec5SDimitry Andric       regular_second_level_page_header;
720b57cec5SDimitry Andric };
730b57cec5SDimitry Andric 
read_leb128(uint8_t ** offset)740b57cec5SDimitry Andric uint64_t read_leb128(uint8_t **offset) {
750b57cec5SDimitry Andric   uint64_t result = 0;
760b57cec5SDimitry Andric   int shift = 0;
770b57cec5SDimitry Andric   while (1) {
780b57cec5SDimitry Andric     uint8_t byte = **offset;
790b57cec5SDimitry Andric     *offset = *offset + 1;
800b57cec5SDimitry Andric     result |= (byte & 0x7f) << shift;
810b57cec5SDimitry Andric     if ((byte & 0x80) == 0)
820b57cec5SDimitry Andric       break;
830b57cec5SDimitry Andric     shift += 7;
840b57cec5SDimitry Andric   }
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric   return result;
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric // step through the load commands in a thin mach-o binary,
900b57cec5SDimitry Andric // find the cputype and the start of the __TEXT,__unwind_info
910b57cec5SDimitry Andric // section, return a pointer to that section or NULL if not found.
920b57cec5SDimitry Andric 
scan_macho_load_commands(struct baton * baton)930b57cec5SDimitry Andric static void scan_macho_load_commands(struct baton *baton) {
940b57cec5SDimitry Andric   struct symtab_command symtab_cmd;
950b57cec5SDimitry Andric   uint64_t linkedit_segment_vmaddr;
960b57cec5SDimitry Andric   uint64_t linkedit_segment_file_offset;
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric   baton->compact_unwind_start = 0;
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric   uint32_t *magic = (uint32_t *)baton->mach_header_start;
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric   if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) {
1030b57cec5SDimitry Andric     printf("Unexpected magic number 0x%x in header, exiting.", *magic);
1040b57cec5SDimitry Andric     exit(1);
1050b57cec5SDimitry Andric   }
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric   bool is_64bit = false;
1080b57cec5SDimitry Andric   if (*magic == MH_MAGIC_64)
1090b57cec5SDimitry Andric     is_64bit = true;
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric   uint8_t *offset = baton->mach_header_start;
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   struct mach_header mh;
1140b57cec5SDimitry Andric   memcpy(&mh, offset, sizeof(struct mach_header));
1150b57cec5SDimitry Andric   if (is_64bit)
1160b57cec5SDimitry Andric     offset += sizeof(struct mach_header_64);
1170b57cec5SDimitry Andric   else
1180b57cec5SDimitry Andric     offset += sizeof(struct mach_header);
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric   if (is_64bit)
1210b57cec5SDimitry Andric     baton->addr_size = 8;
1220b57cec5SDimitry Andric   else
1230b57cec5SDimitry Andric     baton->addr_size = 4;
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric   baton->cputype = mh.cputype;
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   uint8_t *start_of_load_commands = offset;
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   uint32_t cur_cmd = 0;
1300b57cec5SDimitry Andric   while (cur_cmd < mh.ncmds &&
1310b57cec5SDimitry Andric          (offset - start_of_load_commands) < mh.sizeofcmds) {
1320b57cec5SDimitry Andric     struct load_command lc;
1330b57cec5SDimitry Andric     uint32_t *lc_cmd = (uint32_t *)offset;
1340b57cec5SDimitry Andric     uint32_t *lc_cmdsize = (uint32_t *)offset + 1;
1350b57cec5SDimitry Andric     uint8_t *start_of_this_load_cmd = offset;
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric     if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) {
1380b57cec5SDimitry Andric       char segment_name[17];
1390b57cec5SDimitry Andric       segment_name[0] = '\0';
1400b57cec5SDimitry Andric       uint32_t nsects = 0;
1410b57cec5SDimitry Andric       uint64_t segment_offset = 0;
1420b57cec5SDimitry Andric       uint64_t segment_vmaddr = 0;
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric       if (*lc_cmd == LC_SEGMENT_64) {
1450b57cec5SDimitry Andric         struct segment_command_64 seg;
1460b57cec5SDimitry Andric         memcpy(&seg, offset, sizeof(struct segment_command_64));
1470b57cec5SDimitry Andric         memcpy(&segment_name, &seg.segname, 16);
1480b57cec5SDimitry Andric         segment_name[16] = '\0';
1490b57cec5SDimitry Andric         nsects = seg.nsects;
1500b57cec5SDimitry Andric         segment_offset = seg.fileoff;
1510b57cec5SDimitry Andric         segment_vmaddr = seg.vmaddr;
1520b57cec5SDimitry Andric         offset += sizeof(struct segment_command_64);
1530b57cec5SDimitry Andric         if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) {
1540b57cec5SDimitry Andric           printf("Segment '%s' is encrypted.\n", segment_name);
1550b57cec5SDimitry Andric         }
1560b57cec5SDimitry Andric       }
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric       if (*lc_cmd == LC_SEGMENT) {
1590b57cec5SDimitry Andric         struct segment_command seg;
1600b57cec5SDimitry Andric         memcpy(&seg, offset, sizeof(struct segment_command));
1610b57cec5SDimitry Andric         memcpy(&segment_name, &seg.segname, 16);
1620b57cec5SDimitry Andric         segment_name[16] = '\0';
1630b57cec5SDimitry Andric         nsects = seg.nsects;
1640b57cec5SDimitry Andric         segment_offset = seg.fileoff;
1650b57cec5SDimitry Andric         segment_vmaddr = seg.vmaddr;
1660b57cec5SDimitry Andric         offset += sizeof(struct segment_command);
1670b57cec5SDimitry Andric         if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) {
1680b57cec5SDimitry Andric           printf("Segment '%s' is encrypted.\n", segment_name);
1690b57cec5SDimitry Andric         }
1700b57cec5SDimitry Andric       }
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric       if (nsects != 0 && strcmp(segment_name, "__TEXT") == 0) {
1730b57cec5SDimitry Andric         baton->text_segment_vmaddr = segment_vmaddr;
1740b57cec5SDimitry Andric         baton->text_segment_file_offset = segment_offset;
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric         uint32_t current_sect = 0;
1770b57cec5SDimitry Andric         while (current_sect < nsects &&
1780b57cec5SDimitry Andric                (offset - start_of_this_load_cmd) < *lc_cmdsize) {
1790b57cec5SDimitry Andric           char sect_name[17];
1800b57cec5SDimitry Andric           memcpy(&sect_name, offset, 16);
1810b57cec5SDimitry Andric           sect_name[16] = '\0';
1820b57cec5SDimitry Andric           if (strcmp(sect_name, "__unwind_info") == 0) {
1830b57cec5SDimitry Andric             if (is_64bit) {
1840b57cec5SDimitry Andric               struct section_64 sect;
1850b57cec5SDimitry Andric               memset(&sect, 0, sizeof(struct section_64));
1860b57cec5SDimitry Andric               memcpy(&sect, offset, sizeof(struct section_64));
1870b57cec5SDimitry Andric               baton->compact_unwind_start =
1880b57cec5SDimitry Andric                   baton->mach_header_start + sect.offset;
1890b57cec5SDimitry Andric             } else {
1900b57cec5SDimitry Andric               struct section sect;
1910b57cec5SDimitry Andric               memset(&sect, 0, sizeof(struct section));
1920b57cec5SDimitry Andric               memcpy(&sect, offset, sizeof(struct section));
1930b57cec5SDimitry Andric               baton->compact_unwind_start =
1940b57cec5SDimitry Andric                   baton->mach_header_start + sect.offset;
1950b57cec5SDimitry Andric             }
1960b57cec5SDimitry Andric           }
1970b57cec5SDimitry Andric           if (strcmp(sect_name, "__eh_frame") == 0) {
1980b57cec5SDimitry Andric             if (is_64bit) {
1990b57cec5SDimitry Andric               struct section_64 sect;
2000b57cec5SDimitry Andric               memset(&sect, 0, sizeof(struct section_64));
2010b57cec5SDimitry Andric               memcpy(&sect, offset, sizeof(struct section_64));
2020b57cec5SDimitry Andric               baton->eh_section_file_address = sect.addr;
2030b57cec5SDimitry Andric             } else {
2040b57cec5SDimitry Andric               struct section sect;
2050b57cec5SDimitry Andric               memset(&sect, 0, sizeof(struct section));
2060b57cec5SDimitry Andric               memcpy(&sect, offset, sizeof(struct section));
2070b57cec5SDimitry Andric               baton->eh_section_file_address = sect.addr;
2080b57cec5SDimitry Andric             }
2090b57cec5SDimitry Andric           }
2100b57cec5SDimitry Andric           if (strcmp(sect_name, "__text") == 0) {
2110b57cec5SDimitry Andric             if (is_64bit) {
2120b57cec5SDimitry Andric               struct section_64 sect;
2130b57cec5SDimitry Andric               memset(&sect, 0, sizeof(struct section_64));
2140b57cec5SDimitry Andric               memcpy(&sect, offset, sizeof(struct section_64));
2150b57cec5SDimitry Andric               baton->text_section_vmaddr = sect.addr;
2160b57cec5SDimitry Andric               baton->text_section_file_offset = sect.offset;
2170b57cec5SDimitry Andric             } else {
2180b57cec5SDimitry Andric               struct section sect;
2190b57cec5SDimitry Andric               memset(&sect, 0, sizeof(struct section));
2200b57cec5SDimitry Andric               memcpy(&sect, offset, sizeof(struct section));
2210b57cec5SDimitry Andric               baton->text_section_vmaddr = sect.addr;
2220b57cec5SDimitry Andric             }
2230b57cec5SDimitry Andric           }
2240b57cec5SDimitry Andric           if (is_64bit) {
2250b57cec5SDimitry Andric             offset += sizeof(struct section_64);
2260b57cec5SDimitry Andric           } else {
2270b57cec5SDimitry Andric             offset += sizeof(struct section);
2280b57cec5SDimitry Andric           }
2290b57cec5SDimitry Andric         }
2300b57cec5SDimitry Andric       }
2310b57cec5SDimitry Andric 
2320b57cec5SDimitry Andric       if (strcmp(segment_name, "__LINKEDIT") == 0) {
2330b57cec5SDimitry Andric         linkedit_segment_vmaddr = segment_vmaddr;
2340b57cec5SDimitry Andric         linkedit_segment_file_offset = segment_offset;
2350b57cec5SDimitry Andric       }
2360b57cec5SDimitry Andric     }
2370b57cec5SDimitry Andric 
2380b57cec5SDimitry Andric     if (*lc_cmd == LC_SYMTAB) {
2390b57cec5SDimitry Andric       memcpy(&symtab_cmd, offset, sizeof(struct symtab_command));
2400b57cec5SDimitry Andric     }
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric     if (*lc_cmd == LC_DYSYMTAB) {
2430b57cec5SDimitry Andric       struct dysymtab_command dysymtab_cmd;
2440b57cec5SDimitry Andric       memcpy(&dysymtab_cmd, offset, sizeof(struct dysymtab_command));
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric       int nlist_size = 12;
2470b57cec5SDimitry Andric       if (is_64bit)
2480b57cec5SDimitry Andric         nlist_size = 16;
2490b57cec5SDimitry Andric 
2500b57cec5SDimitry Andric       char *string_table =
2510b57cec5SDimitry Andric           (char *)(baton->mach_header_start + symtab_cmd.stroff);
2520b57cec5SDimitry Andric       uint8_t *local_syms = baton->mach_header_start + symtab_cmd.symoff +
2530b57cec5SDimitry Andric                             (dysymtab_cmd.ilocalsym * nlist_size);
2540b57cec5SDimitry Andric       int local_syms_count = dysymtab_cmd.nlocalsym;
2550b57cec5SDimitry Andric       uint8_t *exported_syms = baton->mach_header_start + symtab_cmd.symoff +
2560b57cec5SDimitry Andric                                (dysymtab_cmd.iextdefsym * nlist_size);
2570b57cec5SDimitry Andric       int exported_syms_count = dysymtab_cmd.nextdefsym;
2580b57cec5SDimitry Andric 
2590b57cec5SDimitry Andric       // We're only going to create records for a small number of these symbols
2600b57cec5SDimitry Andric       // but to
2610b57cec5SDimitry Andric       // simplify the memory management I'll allocate enough space to store all
2620b57cec5SDimitry Andric       // of them.
2630b57cec5SDimitry Andric       baton->symbols = (struct symbol *)malloc(
2640b57cec5SDimitry Andric           sizeof(struct symbol) * (local_syms_count + exported_syms_count));
2650b57cec5SDimitry Andric       baton->symbols_count = 0;
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric       for (int i = 0; i < local_syms_count; i++) {
2680b57cec5SDimitry Andric         struct nlist_64 nlist;
2690b57cec5SDimitry Andric         memset(&nlist, 0, sizeof(struct nlist_64));
2700b57cec5SDimitry Andric         if (is_64bit) {
2710b57cec5SDimitry Andric           memcpy(&nlist, local_syms + (i * nlist_size),
2720b57cec5SDimitry Andric                  sizeof(struct nlist_64));
2730b57cec5SDimitry Andric         } else {
2740b57cec5SDimitry Andric           struct nlist nlist_32;
2750b57cec5SDimitry Andric           memset(&nlist_32, 0, sizeof(struct nlist));
2760b57cec5SDimitry Andric           memcpy(&nlist_32, local_syms + (i * nlist_size),
2770b57cec5SDimitry Andric                  sizeof(struct nlist));
2780b57cec5SDimitry Andric           nlist.n_un.n_strx = nlist_32.n_un.n_strx;
2790b57cec5SDimitry Andric           nlist.n_type = nlist_32.n_type;
2800b57cec5SDimitry Andric           nlist.n_sect = nlist_32.n_sect;
2810b57cec5SDimitry Andric           nlist.n_desc = nlist_32.n_desc;
2820b57cec5SDimitry Andric           nlist.n_value = nlist_32.n_value;
2830b57cec5SDimitry Andric         }
2840b57cec5SDimitry Andric         if ((nlist.n_type & N_STAB) == 0 &&
2850b57cec5SDimitry Andric             ((nlist.n_type & N_EXT) == 1 ||
2860b57cec5SDimitry Andric              ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) &&
2870b57cec5SDimitry Andric             nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) {
2880b57cec5SDimitry Andric           baton->symbols[baton->symbols_count].file_address = nlist.n_value;
2890b57cec5SDimitry Andric           if (baton->cputype == CPU_TYPE_ARM)
2900b57cec5SDimitry Andric             baton->symbols[baton->symbols_count].file_address =
2910b57cec5SDimitry Andric                 baton->symbols[baton->symbols_count].file_address & ~1;
2920b57cec5SDimitry Andric           baton->symbols[baton->symbols_count].name =
2930b57cec5SDimitry Andric               string_table + nlist.n_un.n_strx;
2940b57cec5SDimitry Andric           baton->symbols_count++;
2950b57cec5SDimitry Andric         }
2960b57cec5SDimitry Andric       }
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric       for (int i = 0; i < exported_syms_count; i++) {
2990b57cec5SDimitry Andric         struct nlist_64 nlist;
3000b57cec5SDimitry Andric         memset(&nlist, 0, sizeof(struct nlist_64));
3010b57cec5SDimitry Andric         if (is_64bit) {
3020b57cec5SDimitry Andric           memcpy(&nlist, exported_syms + (i * nlist_size),
3030b57cec5SDimitry Andric                  sizeof(struct nlist_64));
3040b57cec5SDimitry Andric         } else {
3050b57cec5SDimitry Andric           struct nlist nlist_32;
3060b57cec5SDimitry Andric           memcpy(&nlist_32, exported_syms + (i * nlist_size),
3070b57cec5SDimitry Andric                  sizeof(struct nlist));
3080b57cec5SDimitry Andric           nlist.n_un.n_strx = nlist_32.n_un.n_strx;
3090b57cec5SDimitry Andric           nlist.n_type = nlist_32.n_type;
3100b57cec5SDimitry Andric           nlist.n_sect = nlist_32.n_sect;
3110b57cec5SDimitry Andric           nlist.n_desc = nlist_32.n_desc;
3120b57cec5SDimitry Andric           nlist.n_value = nlist_32.n_value;
3130b57cec5SDimitry Andric         }
3140b57cec5SDimitry Andric         if ((nlist.n_type & N_STAB) == 0 &&
3150b57cec5SDimitry Andric             ((nlist.n_type & N_EXT) == 1 ||
3160b57cec5SDimitry Andric              ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) &&
3170b57cec5SDimitry Andric             nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) {
3180b57cec5SDimitry Andric           baton->symbols[baton->symbols_count].file_address = nlist.n_value;
3190b57cec5SDimitry Andric           if (baton->cputype == CPU_TYPE_ARM)
3200b57cec5SDimitry Andric             baton->symbols[baton->symbols_count].file_address =
3210b57cec5SDimitry Andric                 baton->symbols[baton->symbols_count].file_address & ~1;
3220b57cec5SDimitry Andric           baton->symbols[baton->symbols_count].name =
3230b57cec5SDimitry Andric               string_table + nlist.n_un.n_strx;
3240b57cec5SDimitry Andric           baton->symbols_count++;
3250b57cec5SDimitry Andric         }
3260b57cec5SDimitry Andric       }
3270b57cec5SDimitry Andric 
3280b57cec5SDimitry Andric       qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol),
3290b57cec5SDimitry Andric             symbol_compare);
3300b57cec5SDimitry Andric     }
3310b57cec5SDimitry Andric 
3320b57cec5SDimitry Andric     if (*lc_cmd == LC_FUNCTION_STARTS) {
3330b57cec5SDimitry Andric       struct linkedit_data_command function_starts_cmd;
3340b57cec5SDimitry Andric       memcpy(&function_starts_cmd, offset,
3350b57cec5SDimitry Andric              sizeof(struct linkedit_data_command));
3360b57cec5SDimitry Andric 
3370b57cec5SDimitry Andric       uint8_t *funcstarts_offset =
3380b57cec5SDimitry Andric           baton->mach_header_start + function_starts_cmd.dataoff;
3390b57cec5SDimitry Andric       uint8_t *function_end = funcstarts_offset + function_starts_cmd.datasize;
3400b57cec5SDimitry Andric       int count = 0;
3410b57cec5SDimitry Andric 
3420b57cec5SDimitry Andric       while (funcstarts_offset < function_end) {
3430b57cec5SDimitry Andric         if (read_leb128(&funcstarts_offset) != 0) {
3440b57cec5SDimitry Andric           count++;
3450b57cec5SDimitry Andric         }
3460b57cec5SDimitry Andric       }
3470b57cec5SDimitry Andric 
3480b57cec5SDimitry Andric       baton->function_start_addresses =
3490b57cec5SDimitry Andric           (uint64_t *)malloc(sizeof(uint64_t) * count);
3500b57cec5SDimitry Andric       baton->function_start_addresses_count = count;
3510b57cec5SDimitry Andric 
3520b57cec5SDimitry Andric       funcstarts_offset =
3530b57cec5SDimitry Andric           baton->mach_header_start + function_starts_cmd.dataoff;
3540b57cec5SDimitry Andric       uint64_t current_pc = baton->text_segment_vmaddr;
3550b57cec5SDimitry Andric       int i = 0;
3560b57cec5SDimitry Andric       while (funcstarts_offset < function_end) {
3570b57cec5SDimitry Andric         uint64_t func_start = read_leb128(&funcstarts_offset);
3580b57cec5SDimitry Andric         if (func_start != 0) {
3590b57cec5SDimitry Andric           current_pc += func_start;
3600b57cec5SDimitry Andric           baton->function_start_addresses[i++] = current_pc;
3610b57cec5SDimitry Andric         }
3620b57cec5SDimitry Andric       }
3630b57cec5SDimitry Andric     }
3640b57cec5SDimitry Andric 
3650b57cec5SDimitry Andric     offset = start_of_this_load_cmd + *lc_cmdsize;
3660b57cec5SDimitry Andric     cur_cmd++;
3670b57cec5SDimitry Andric   }
3680b57cec5SDimitry Andric 
3690b57cec5SDimitry Andric   // Augment the symbol table with the function starts table -- adding symbol
3700b57cec5SDimitry Andric   // entries
3710b57cec5SDimitry Andric   // for functions that were stripped.
3720b57cec5SDimitry Andric 
3730b57cec5SDimitry Andric   int unnamed_functions_to_add = 0;
3740b57cec5SDimitry Andric   for (int i = 0; i < baton->function_start_addresses_count; i++) {
3750b57cec5SDimitry Andric     struct symbol search_key;
3760b57cec5SDimitry Andric     search_key.file_address = baton->function_start_addresses[i];
3770b57cec5SDimitry Andric     if (baton->cputype == CPU_TYPE_ARM)
3780b57cec5SDimitry Andric       search_key.file_address = search_key.file_address & ~1;
3790b57cec5SDimitry Andric     struct symbol *sym =
3800b57cec5SDimitry Andric         bsearch(&search_key, baton->symbols, baton->symbols_count,
3810b57cec5SDimitry Andric                 sizeof(struct symbol), symbol_compare);
3820b57cec5SDimitry Andric     if (sym == NULL)
3830b57cec5SDimitry Andric       unnamed_functions_to_add++;
3840b57cec5SDimitry Andric   }
3850b57cec5SDimitry Andric 
3860b57cec5SDimitry Andric   baton->symbols = (struct symbol *)realloc(
3870b57cec5SDimitry Andric       baton->symbols, sizeof(struct symbol) *
3880b57cec5SDimitry Andric                           (baton->symbols_count + unnamed_functions_to_add));
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric   int current_unnamed_symbol = 1;
3910b57cec5SDimitry Andric   int number_symbols_added = 0;
3920b57cec5SDimitry Andric   for (int i = 0; i < baton->function_start_addresses_count; i++) {
3930b57cec5SDimitry Andric     struct symbol search_key;
3940b57cec5SDimitry Andric     search_key.file_address = baton->function_start_addresses[i];
3950b57cec5SDimitry Andric     if (baton->cputype == CPU_TYPE_ARM)
3960b57cec5SDimitry Andric       search_key.file_address = search_key.file_address & ~1;
3970b57cec5SDimitry Andric     struct symbol *sym =
3980b57cec5SDimitry Andric         bsearch(&search_key, baton->symbols, baton->symbols_count,
3990b57cec5SDimitry Andric                 sizeof(struct symbol), symbol_compare);
4000b57cec5SDimitry Andric     if (sym == NULL) {
4010b57cec5SDimitry Andric       char *name;
4020b57cec5SDimitry Andric       asprintf(&name, "unnamed function #%d", current_unnamed_symbol++);
4030b57cec5SDimitry Andric       baton->symbols[baton->symbols_count + number_symbols_added].file_address =
4040b57cec5SDimitry Andric           baton->function_start_addresses[i];
4050b57cec5SDimitry Andric       baton->symbols[baton->symbols_count + number_symbols_added].name = name;
4060b57cec5SDimitry Andric       number_symbols_added++;
4070b57cec5SDimitry Andric     }
4080b57cec5SDimitry Andric   }
4090b57cec5SDimitry Andric   baton->symbols_count += number_symbols_added;
4100b57cec5SDimitry Andric   qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol),
4110b57cec5SDimitry Andric         symbol_compare);
4120b57cec5SDimitry Andric 
4130b57cec5SDimitry Andric   //    printf ("function start addresses\n");
4140b57cec5SDimitry Andric   //    for (int i = 0; i < baton->function_start_addresses_count; i++)
4150b57cec5SDimitry Andric   //    {
4160b57cec5SDimitry Andric   //        printf ("0x%012llx\n", baton->function_start_addresses[i]);
4170b57cec5SDimitry Andric   //    }
4180b57cec5SDimitry Andric 
4190b57cec5SDimitry Andric   //    printf ("symbol table names & addresses\n");
4200b57cec5SDimitry Andric   //    for (int i = 0; i < baton->symbols_count; i++)
4210b57cec5SDimitry Andric   //    {
4220b57cec5SDimitry Andric   //        printf ("0x%012llx %s\n", baton->symbols[i].file_address,
4230b57cec5SDimitry Andric   //        baton->symbols[i].name);
4240b57cec5SDimitry Andric   //    }
4250b57cec5SDimitry Andric }
4260b57cec5SDimitry Andric 
print_encoding_x86_64(struct baton baton,uint8_t * function_start,uint32_t encoding)4270b57cec5SDimitry Andric void print_encoding_x86_64(struct baton baton, uint8_t *function_start,
4280b57cec5SDimitry Andric                            uint32_t encoding) {
4290b57cec5SDimitry Andric   int mode = encoding & UNWIND_X86_64_MODE_MASK;
4300b57cec5SDimitry Andric   switch (mode) {
4310b57cec5SDimitry Andric   case UNWIND_X86_64_MODE_RBP_FRAME: {
4320b57cec5SDimitry Andric     printf("frame func: CFA is rbp+%d ", 16);
4330b57cec5SDimitry Andric     printf(" rip=[CFA-8] rbp=[CFA-16]");
4340b57cec5SDimitry Andric     uint32_t saved_registers_offset =
4350b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
4360b57cec5SDimitry Andric 
4370b57cec5SDimitry Andric     uint32_t saved_registers_locations =
4380b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
4390b57cec5SDimitry Andric 
4400b57cec5SDimitry Andric     saved_registers_offset += 2;
4410b57cec5SDimitry Andric 
4420b57cec5SDimitry Andric     for (int i = 0; i < 5; i++) {
4430b57cec5SDimitry Andric       switch (saved_registers_locations & 0x7) {
4440b57cec5SDimitry Andric       case UNWIND_X86_64_REG_NONE:
4450b57cec5SDimitry Andric         break;
4460b57cec5SDimitry Andric       case UNWIND_X86_64_REG_RBX:
4470b57cec5SDimitry Andric         printf(" rbx=[CFA-%d]", saved_registers_offset * 8);
4480b57cec5SDimitry Andric         break;
4490b57cec5SDimitry Andric       case UNWIND_X86_64_REG_R12:
4500b57cec5SDimitry Andric         printf(" r12=[CFA-%d]", saved_registers_offset * 8);
4510b57cec5SDimitry Andric         break;
4520b57cec5SDimitry Andric       case UNWIND_X86_64_REG_R13:
4530b57cec5SDimitry Andric         printf(" r13=[CFA-%d]", saved_registers_offset * 8);
4540b57cec5SDimitry Andric         break;
4550b57cec5SDimitry Andric       case UNWIND_X86_64_REG_R14:
4560b57cec5SDimitry Andric         printf(" r14=[CFA-%d]", saved_registers_offset * 8);
4570b57cec5SDimitry Andric         break;
4580b57cec5SDimitry Andric       case UNWIND_X86_64_REG_R15:
4590b57cec5SDimitry Andric         printf(" r15=[CFA-%d]", saved_registers_offset * 8);
4600b57cec5SDimitry Andric         break;
4610b57cec5SDimitry Andric       }
4620b57cec5SDimitry Andric       saved_registers_offset--;
4630b57cec5SDimitry Andric       saved_registers_locations >>= 3;
4640b57cec5SDimitry Andric     }
4650b57cec5SDimitry Andric   } break;
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric   case UNWIND_X86_64_MODE_STACK_IND:
4680b57cec5SDimitry Andric   case UNWIND_X86_64_MODE_STACK_IMMD: {
4690b57cec5SDimitry Andric     uint32_t stack_size =
4700b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
4710b57cec5SDimitry Andric     uint32_t register_count =
4720b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
4730b57cec5SDimitry Andric     uint32_t permutation =
4740b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
4750b57cec5SDimitry Andric 
4760b57cec5SDimitry Andric     if (mode == UNWIND_X86_64_MODE_STACK_IND && function_start) {
4770b57cec5SDimitry Andric       uint32_t stack_adjust =
4780b57cec5SDimitry Andric           EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
4790b57cec5SDimitry Andric 
4800b57cec5SDimitry Andric       // offset into the function instructions; 0 == beginning of first
4810b57cec5SDimitry Andric       // instruction
4820b57cec5SDimitry Andric       uint32_t offset_to_subl_insn =
4830b57cec5SDimitry Andric           EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
4840b57cec5SDimitry Andric 
4850b57cec5SDimitry Andric       stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));
4860b57cec5SDimitry Andric 
4870b57cec5SDimitry Andric       stack_size += stack_adjust * 8;
4880b57cec5SDimitry Andric 
4890b57cec5SDimitry Andric       printf("large stack ");
4900b57cec5SDimitry Andric     }
4910b57cec5SDimitry Andric 
4920b57cec5SDimitry Andric     if (mode == UNWIND_X86_64_MODE_STACK_IND) {
4930b57cec5SDimitry Andric       printf("frameless function: stack size %d, register count %d ",
4940b57cec5SDimitry Andric              stack_size * 8, register_count);
4950b57cec5SDimitry Andric     } else {
4960b57cec5SDimitry Andric       printf("frameless function: stack size %d, register count %d ",
4970b57cec5SDimitry Andric              stack_size, register_count);
4980b57cec5SDimitry Andric     }
4990b57cec5SDimitry Andric 
5000b57cec5SDimitry Andric     if (register_count == 0) {
5010b57cec5SDimitry Andric       printf(" no registers saved");
5020b57cec5SDimitry Andric     } else {
5030b57cec5SDimitry Andric 
5040b57cec5SDimitry Andric       // We need to include (up to) 6 registers in 10 bits.
5050b57cec5SDimitry Andric       // That would be 18 bits if we just used 3 bits per reg to indicate
5060b57cec5SDimitry Andric       // the order they're saved on the stack.
5070b57cec5SDimitry Andric       //
5080b57cec5SDimitry Andric       // This is done with Lehmer code permutation, e.g. see
5090b57cec5SDimitry Andric       // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
5100b57cec5SDimitry Andric       int permunreg[6];
5110b57cec5SDimitry Andric 
5120b57cec5SDimitry Andric       // This decodes the variable-base number in the 10 bits
5130b57cec5SDimitry Andric       // and gives us the Lehmer code sequence which can then
5140b57cec5SDimitry Andric       // be decoded.
5150b57cec5SDimitry Andric 
5160b57cec5SDimitry Andric       switch (register_count) {
5170b57cec5SDimitry Andric       case 6:
5180b57cec5SDimitry Andric         permunreg[0] = permutation / 120; // 120 == 5!
5190b57cec5SDimitry Andric         permutation -= (permunreg[0] * 120);
5200b57cec5SDimitry Andric         permunreg[1] = permutation / 24; // 24 == 4!
5210b57cec5SDimitry Andric         permutation -= (permunreg[1] * 24);
5220b57cec5SDimitry Andric         permunreg[2] = permutation / 6; // 6 == 3!
5230b57cec5SDimitry Andric         permutation -= (permunreg[2] * 6);
5240b57cec5SDimitry Andric         permunreg[3] = permutation / 2; // 2 == 2!
5250b57cec5SDimitry Andric         permutation -= (permunreg[3] * 2);
5260b57cec5SDimitry Andric         permunreg[4] = permutation; // 1 == 1!
5270b57cec5SDimitry Andric         permunreg[5] = 0;
5280b57cec5SDimitry Andric         break;
5290b57cec5SDimitry Andric       case 5:
5300b57cec5SDimitry Andric         permunreg[0] = permutation / 120;
5310b57cec5SDimitry Andric         permutation -= (permunreg[0] * 120);
5320b57cec5SDimitry Andric         permunreg[1] = permutation / 24;
5330b57cec5SDimitry Andric         permutation -= (permunreg[1] * 24);
5340b57cec5SDimitry Andric         permunreg[2] = permutation / 6;
5350b57cec5SDimitry Andric         permutation -= (permunreg[2] * 6);
5360b57cec5SDimitry Andric         permunreg[3] = permutation / 2;
5370b57cec5SDimitry Andric         permutation -= (permunreg[3] * 2);
5380b57cec5SDimitry Andric         permunreg[4] = permutation;
5390b57cec5SDimitry Andric         break;
5400b57cec5SDimitry Andric       case 4:
5410b57cec5SDimitry Andric         permunreg[0] = permutation / 60;
5420b57cec5SDimitry Andric         permutation -= (permunreg[0] * 60);
5430b57cec5SDimitry Andric         permunreg[1] = permutation / 12;
5440b57cec5SDimitry Andric         permutation -= (permunreg[1] * 12);
5450b57cec5SDimitry Andric         permunreg[2] = permutation / 3;
5460b57cec5SDimitry Andric         permutation -= (permunreg[2] * 3);
5470b57cec5SDimitry Andric         permunreg[3] = permutation;
5480b57cec5SDimitry Andric         break;
5490b57cec5SDimitry Andric       case 3:
5500b57cec5SDimitry Andric         permunreg[0] = permutation / 20;
5510b57cec5SDimitry Andric         permutation -= (permunreg[0] * 20);
5520b57cec5SDimitry Andric         permunreg[1] = permutation / 4;
5530b57cec5SDimitry Andric         permutation -= (permunreg[1] * 4);
5540b57cec5SDimitry Andric         permunreg[2] = permutation;
5550b57cec5SDimitry Andric         break;
5560b57cec5SDimitry Andric       case 2:
5570b57cec5SDimitry Andric         permunreg[0] = permutation / 5;
5580b57cec5SDimitry Andric         permutation -= (permunreg[0] * 5);
5590b57cec5SDimitry Andric         permunreg[1] = permutation;
5600b57cec5SDimitry Andric         break;
5610b57cec5SDimitry Andric       case 1:
5620b57cec5SDimitry Andric         permunreg[0] = permutation;
5630b57cec5SDimitry Andric         break;
5640b57cec5SDimitry Andric       }
5650b57cec5SDimitry Andric 
5660b57cec5SDimitry Andric       // Decode the Lehmer code for this permutation of
5670b57cec5SDimitry Andric       // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
5680b57cec5SDimitry Andric 
5690b57cec5SDimitry Andric       int registers[6];
5700b57cec5SDimitry Andric       bool used[7] = {false, false, false, false, false, false, false};
5710b57cec5SDimitry Andric       for (int i = 0; i < register_count; i++) {
5720b57cec5SDimitry Andric         int renum = 0;
5730b57cec5SDimitry Andric         for (int j = 1; j < 7; j++) {
5740b57cec5SDimitry Andric           if (used[j] == false) {
5750b57cec5SDimitry Andric             if (renum == permunreg[i]) {
5760b57cec5SDimitry Andric               registers[i] = j;
5770b57cec5SDimitry Andric               used[j] = true;
5780b57cec5SDimitry Andric               break;
5790b57cec5SDimitry Andric             }
5800b57cec5SDimitry Andric             renum++;
5810b57cec5SDimitry Andric           }
5820b57cec5SDimitry Andric         }
5830b57cec5SDimitry Andric       }
5840b57cec5SDimitry Andric 
5850b57cec5SDimitry Andric       if (mode == UNWIND_X86_64_MODE_STACK_IND) {
5860b57cec5SDimitry Andric         printf(" CFA is rsp+%d ", stack_size);
5870b57cec5SDimitry Andric       } else {
5880b57cec5SDimitry Andric         printf(" CFA is rsp+%d ", stack_size * 8);
5890b57cec5SDimitry Andric       }
5900b57cec5SDimitry Andric 
5910b57cec5SDimitry Andric       uint32_t saved_registers_offset = 1;
5920b57cec5SDimitry Andric       printf(" rip=[CFA-%d]", saved_registers_offset * 8);
5930b57cec5SDimitry Andric       saved_registers_offset++;
5940b57cec5SDimitry Andric 
5950b57cec5SDimitry Andric       for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
5960b57cec5SDimitry Andric         switch (registers[i]) {
5970b57cec5SDimitry Andric         case UNWIND_X86_64_REG_NONE:
5980b57cec5SDimitry Andric           break;
5990b57cec5SDimitry Andric         case UNWIND_X86_64_REG_RBX:
6000b57cec5SDimitry Andric           printf(" rbx=[CFA-%d]", saved_registers_offset * 8);
6010b57cec5SDimitry Andric           saved_registers_offset++;
6020b57cec5SDimitry Andric           break;
6030b57cec5SDimitry Andric         case UNWIND_X86_64_REG_R12:
6040b57cec5SDimitry Andric           printf(" r12=[CFA-%d]", saved_registers_offset * 8);
6050b57cec5SDimitry Andric           saved_registers_offset++;
6060b57cec5SDimitry Andric           break;
6070b57cec5SDimitry Andric         case UNWIND_X86_64_REG_R13:
6080b57cec5SDimitry Andric           printf(" r13=[CFA-%d]", saved_registers_offset * 8);
6090b57cec5SDimitry Andric           saved_registers_offset++;
6100b57cec5SDimitry Andric           break;
6110b57cec5SDimitry Andric         case UNWIND_X86_64_REG_R14:
6120b57cec5SDimitry Andric           printf(" r14=[CFA-%d]", saved_registers_offset * 8);
6130b57cec5SDimitry Andric           saved_registers_offset++;
6140b57cec5SDimitry Andric           break;
6150b57cec5SDimitry Andric         case UNWIND_X86_64_REG_R15:
6160b57cec5SDimitry Andric           printf(" r15=[CFA-%d]", saved_registers_offset * 8);
6170b57cec5SDimitry Andric           saved_registers_offset++;
6180b57cec5SDimitry Andric           break;
6190b57cec5SDimitry Andric         case UNWIND_X86_64_REG_RBP:
6200b57cec5SDimitry Andric           printf(" rbp=[CFA-%d]", saved_registers_offset * 8);
6210b57cec5SDimitry Andric           saved_registers_offset++;
6220b57cec5SDimitry Andric           break;
6230b57cec5SDimitry Andric         }
6240b57cec5SDimitry Andric       }
6250b57cec5SDimitry Andric     }
6260b57cec5SDimitry Andric 
6270b57cec5SDimitry Andric   } break;
6280b57cec5SDimitry Andric 
6290b57cec5SDimitry Andric   case UNWIND_X86_64_MODE_DWARF: {
6300b57cec5SDimitry Andric     uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;
6310b57cec5SDimitry Andric     printf(
6320b57cec5SDimitry Andric         "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
6330b57cec5SDimitry Andric         ")",
6340b57cec5SDimitry Andric         dwarf_offset, dwarf_offset + baton.eh_section_file_address);
6350b57cec5SDimitry Andric   } break;
6360b57cec5SDimitry Andric 
6370b57cec5SDimitry Andric   case 0: {
6380b57cec5SDimitry Andric     printf(" no unwind information");
6390b57cec5SDimitry Andric   } break;
6400b57cec5SDimitry Andric   }
6410b57cec5SDimitry Andric }
6420b57cec5SDimitry Andric 
print_encoding_i386(struct baton baton,uint8_t * function_start,uint32_t encoding)6430b57cec5SDimitry Andric void print_encoding_i386(struct baton baton, uint8_t *function_start,
6440b57cec5SDimitry Andric                          uint32_t encoding) {
6450b57cec5SDimitry Andric   int mode = encoding & UNWIND_X86_MODE_MASK;
6460b57cec5SDimitry Andric   switch (mode) {
6470b57cec5SDimitry Andric   case UNWIND_X86_MODE_EBP_FRAME: {
6480b57cec5SDimitry Andric     printf("frame func: CFA is ebp+%d ", 8);
6490b57cec5SDimitry Andric     printf(" eip=[CFA-4] ebp=[CFA-8]");
6500b57cec5SDimitry Andric     uint32_t saved_registers_offset =
6510b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);
6520b57cec5SDimitry Andric 
6530b57cec5SDimitry Andric     uint32_t saved_registers_locations =
6540b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
6550b57cec5SDimitry Andric 
6560b57cec5SDimitry Andric     saved_registers_offset += 2;
6570b57cec5SDimitry Andric 
6580b57cec5SDimitry Andric     for (int i = 0; i < 5; i++) {
6590b57cec5SDimitry Andric       switch (saved_registers_locations & 0x7) {
6600b57cec5SDimitry Andric       case UNWIND_X86_REG_NONE:
6610b57cec5SDimitry Andric         break;
6620b57cec5SDimitry Andric       case UNWIND_X86_REG_EBX:
6630b57cec5SDimitry Andric         printf(" ebx=[CFA-%d]", saved_registers_offset * 4);
6640b57cec5SDimitry Andric         break;
6650b57cec5SDimitry Andric       case UNWIND_X86_REG_ECX:
6660b57cec5SDimitry Andric         printf(" ecx=[CFA-%d]", saved_registers_offset * 4);
6670b57cec5SDimitry Andric         break;
6680b57cec5SDimitry Andric       case UNWIND_X86_REG_EDX:
6690b57cec5SDimitry Andric         printf(" edx=[CFA-%d]", saved_registers_offset * 4);
6700b57cec5SDimitry Andric         break;
6710b57cec5SDimitry Andric       case UNWIND_X86_REG_EDI:
6720b57cec5SDimitry Andric         printf(" edi=[CFA-%d]", saved_registers_offset * 4);
6730b57cec5SDimitry Andric         break;
6740b57cec5SDimitry Andric       case UNWIND_X86_REG_ESI:
6750b57cec5SDimitry Andric         printf(" esi=[CFA-%d]", saved_registers_offset * 4);
6760b57cec5SDimitry Andric         break;
6770b57cec5SDimitry Andric       }
6780b57cec5SDimitry Andric       saved_registers_offset--;
6790b57cec5SDimitry Andric       saved_registers_locations >>= 3;
6800b57cec5SDimitry Andric     }
6810b57cec5SDimitry Andric   } break;
6820b57cec5SDimitry Andric 
6830b57cec5SDimitry Andric   case UNWIND_X86_MODE_STACK_IND:
6840b57cec5SDimitry Andric   case UNWIND_X86_MODE_STACK_IMMD: {
6850b57cec5SDimitry Andric     uint32_t stack_size =
6860b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
6870b57cec5SDimitry Andric     uint32_t register_count =
6880b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
6890b57cec5SDimitry Andric     uint32_t permutation =
6900b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
6910b57cec5SDimitry Andric 
6920b57cec5SDimitry Andric     if (mode == UNWIND_X86_MODE_STACK_IND && function_start) {
6930b57cec5SDimitry Andric       uint32_t stack_adjust =
6940b57cec5SDimitry Andric           EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
6950b57cec5SDimitry Andric 
6960b57cec5SDimitry Andric       // offset into the function instructions; 0 == beginning of first
6970b57cec5SDimitry Andric       // instruction
6980b57cec5SDimitry Andric       uint32_t offset_to_subl_insn =
6990b57cec5SDimitry Andric           EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
7000b57cec5SDimitry Andric 
7010b57cec5SDimitry Andric       stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));
7020b57cec5SDimitry Andric 
7030b57cec5SDimitry Andric       stack_size += stack_adjust * 4;
7040b57cec5SDimitry Andric 
7050b57cec5SDimitry Andric       printf("large stack ");
7060b57cec5SDimitry Andric     }
7070b57cec5SDimitry Andric 
7080b57cec5SDimitry Andric     if (mode == UNWIND_X86_MODE_STACK_IND) {
7090b57cec5SDimitry Andric       printf("frameless function: stack size %d, register count %d ",
7100b57cec5SDimitry Andric              stack_size, register_count);
7110b57cec5SDimitry Andric     } else {
7120b57cec5SDimitry Andric       printf("frameless function: stack size %d, register count %d ",
7130b57cec5SDimitry Andric              stack_size * 4, register_count);
7140b57cec5SDimitry Andric     }
7150b57cec5SDimitry Andric 
7160b57cec5SDimitry Andric     if (register_count == 0) {
7170b57cec5SDimitry Andric       printf(" no registers saved");
7180b57cec5SDimitry Andric     } else {
7190b57cec5SDimitry Andric 
7200b57cec5SDimitry Andric       // We need to include (up to) 6 registers in 10 bits.
7210b57cec5SDimitry Andric       // That would be 18 bits if we just used 3 bits per reg to indicate
7220b57cec5SDimitry Andric       // the order they're saved on the stack.
7230b57cec5SDimitry Andric       //
7240b57cec5SDimitry Andric       // This is done with Lehmer code permutation, e.g. see
7250b57cec5SDimitry Andric       // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
7260b57cec5SDimitry Andric       int permunreg[6];
7270b57cec5SDimitry Andric 
7280b57cec5SDimitry Andric       // This decodes the variable-base number in the 10 bits
7290b57cec5SDimitry Andric       // and gives us the Lehmer code sequence which can then
7300b57cec5SDimitry Andric       // be decoded.
7310b57cec5SDimitry Andric 
7320b57cec5SDimitry Andric       switch (register_count) {
7330b57cec5SDimitry Andric       case 6:
7340b57cec5SDimitry Andric         permunreg[0] = permutation / 120; // 120 == 5!
7350b57cec5SDimitry Andric         permutation -= (permunreg[0] * 120);
7360b57cec5SDimitry Andric         permunreg[1] = permutation / 24; // 24 == 4!
7370b57cec5SDimitry Andric         permutation -= (permunreg[1] * 24);
7380b57cec5SDimitry Andric         permunreg[2] = permutation / 6; // 6 == 3!
7390b57cec5SDimitry Andric         permutation -= (permunreg[2] * 6);
7400b57cec5SDimitry Andric         permunreg[3] = permutation / 2; // 2 == 2!
7410b57cec5SDimitry Andric         permutation -= (permunreg[3] * 2);
7420b57cec5SDimitry Andric         permunreg[4] = permutation; // 1 == 1!
7430b57cec5SDimitry Andric         permunreg[5] = 0;
7440b57cec5SDimitry Andric         break;
7450b57cec5SDimitry Andric       case 5:
7460b57cec5SDimitry Andric         permunreg[0] = permutation / 120;
7470b57cec5SDimitry Andric         permutation -= (permunreg[0] * 120);
7480b57cec5SDimitry Andric         permunreg[1] = permutation / 24;
7490b57cec5SDimitry Andric         permutation -= (permunreg[1] * 24);
7500b57cec5SDimitry Andric         permunreg[2] = permutation / 6;
7510b57cec5SDimitry Andric         permutation -= (permunreg[2] * 6);
7520b57cec5SDimitry Andric         permunreg[3] = permutation / 2;
7530b57cec5SDimitry Andric         permutation -= (permunreg[3] * 2);
7540b57cec5SDimitry Andric         permunreg[4] = permutation;
7550b57cec5SDimitry Andric         break;
7560b57cec5SDimitry Andric       case 4:
7570b57cec5SDimitry Andric         permunreg[0] = permutation / 60;
7580b57cec5SDimitry Andric         permutation -= (permunreg[0] * 60);
7590b57cec5SDimitry Andric         permunreg[1] = permutation / 12;
7600b57cec5SDimitry Andric         permutation -= (permunreg[1] * 12);
7610b57cec5SDimitry Andric         permunreg[2] = permutation / 3;
7620b57cec5SDimitry Andric         permutation -= (permunreg[2] * 3);
7630b57cec5SDimitry Andric         permunreg[3] = permutation;
7640b57cec5SDimitry Andric         break;
7650b57cec5SDimitry Andric       case 3:
7660b57cec5SDimitry Andric         permunreg[0] = permutation / 20;
7670b57cec5SDimitry Andric         permutation -= (permunreg[0] * 20);
7680b57cec5SDimitry Andric         permunreg[1] = permutation / 4;
7690b57cec5SDimitry Andric         permutation -= (permunreg[1] * 4);
7700b57cec5SDimitry Andric         permunreg[2] = permutation;
7710b57cec5SDimitry Andric         break;
7720b57cec5SDimitry Andric       case 2:
7730b57cec5SDimitry Andric         permunreg[0] = permutation / 5;
7740b57cec5SDimitry Andric         permutation -= (permunreg[0] * 5);
7750b57cec5SDimitry Andric         permunreg[1] = permutation;
7760b57cec5SDimitry Andric         break;
7770b57cec5SDimitry Andric       case 1:
7780b57cec5SDimitry Andric         permunreg[0] = permutation;
7790b57cec5SDimitry Andric         break;
7800b57cec5SDimitry Andric       }
7810b57cec5SDimitry Andric 
7820b57cec5SDimitry Andric       // Decode the Lehmer code for this permutation of
7830b57cec5SDimitry Andric       // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
7840b57cec5SDimitry Andric 
7850b57cec5SDimitry Andric       int registers[6];
7860b57cec5SDimitry Andric       bool used[7] = {false, false, false, false, false, false, false};
7870b57cec5SDimitry Andric       for (int i = 0; i < register_count; i++) {
7880b57cec5SDimitry Andric         int renum = 0;
7890b57cec5SDimitry Andric         for (int j = 1; j < 7; j++) {
7900b57cec5SDimitry Andric           if (used[j] == false) {
7910b57cec5SDimitry Andric             if (renum == permunreg[i]) {
7920b57cec5SDimitry Andric               registers[i] = j;
7930b57cec5SDimitry Andric               used[j] = true;
7940b57cec5SDimitry Andric               break;
7950b57cec5SDimitry Andric             }
7960b57cec5SDimitry Andric             renum++;
7970b57cec5SDimitry Andric           }
7980b57cec5SDimitry Andric         }
7990b57cec5SDimitry Andric       }
8000b57cec5SDimitry Andric 
8010b57cec5SDimitry Andric       if (mode == UNWIND_X86_MODE_STACK_IND) {
8020b57cec5SDimitry Andric         printf(" CFA is esp+%d ", stack_size);
8030b57cec5SDimitry Andric       } else {
8040b57cec5SDimitry Andric         printf(" CFA is esp+%d ", stack_size * 4);
8050b57cec5SDimitry Andric       }
8060b57cec5SDimitry Andric 
8070b57cec5SDimitry Andric       uint32_t saved_registers_offset = 1;
8080b57cec5SDimitry Andric       printf(" eip=[CFA-%d]", saved_registers_offset * 4);
8090b57cec5SDimitry Andric       saved_registers_offset++;
8100b57cec5SDimitry Andric 
8110b57cec5SDimitry Andric       for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
8120b57cec5SDimitry Andric         switch (registers[i]) {
8130b57cec5SDimitry Andric         case UNWIND_X86_REG_NONE:
8140b57cec5SDimitry Andric           break;
8150b57cec5SDimitry Andric         case UNWIND_X86_REG_EBX:
8160b57cec5SDimitry Andric           printf(" ebx=[CFA-%d]", saved_registers_offset * 4);
8170b57cec5SDimitry Andric           saved_registers_offset++;
8180b57cec5SDimitry Andric           break;
8190b57cec5SDimitry Andric         case UNWIND_X86_REG_ECX:
8200b57cec5SDimitry Andric           printf(" ecx=[CFA-%d]", saved_registers_offset * 4);
8210b57cec5SDimitry Andric           saved_registers_offset++;
8220b57cec5SDimitry Andric           break;
8230b57cec5SDimitry Andric         case UNWIND_X86_REG_EDX:
8240b57cec5SDimitry Andric           printf(" edx=[CFA-%d]", saved_registers_offset * 4);
8250b57cec5SDimitry Andric           saved_registers_offset++;
8260b57cec5SDimitry Andric           break;
8270b57cec5SDimitry Andric         case UNWIND_X86_REG_EDI:
8280b57cec5SDimitry Andric           printf(" edi=[CFA-%d]", saved_registers_offset * 4);
8290b57cec5SDimitry Andric           saved_registers_offset++;
8300b57cec5SDimitry Andric           break;
8310b57cec5SDimitry Andric         case UNWIND_X86_REG_ESI:
8320b57cec5SDimitry Andric           printf(" esi=[CFA-%d]", saved_registers_offset * 4);
8330b57cec5SDimitry Andric           saved_registers_offset++;
8340b57cec5SDimitry Andric           break;
8350b57cec5SDimitry Andric         case UNWIND_X86_REG_EBP:
8360b57cec5SDimitry Andric           printf(" ebp=[CFA-%d]", saved_registers_offset * 4);
8370b57cec5SDimitry Andric           saved_registers_offset++;
8380b57cec5SDimitry Andric           break;
8390b57cec5SDimitry Andric         }
8400b57cec5SDimitry Andric       }
8410b57cec5SDimitry Andric     }
8420b57cec5SDimitry Andric 
8430b57cec5SDimitry Andric   } break;
8440b57cec5SDimitry Andric 
8450b57cec5SDimitry Andric   case UNWIND_X86_MODE_DWARF: {
8460b57cec5SDimitry Andric     uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;
8470b57cec5SDimitry Andric     printf(
8480b57cec5SDimitry Andric         "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
8490b57cec5SDimitry Andric         ")",
8500b57cec5SDimitry Andric         dwarf_offset, dwarf_offset + baton.eh_section_file_address);
8510b57cec5SDimitry Andric   } break;
8520b57cec5SDimitry Andric 
8530b57cec5SDimitry Andric   case 0: {
8540b57cec5SDimitry Andric     printf(" no unwind information");
8550b57cec5SDimitry Andric   } break;
8560b57cec5SDimitry Andric   }
8570b57cec5SDimitry Andric }
8580b57cec5SDimitry Andric 
print_encoding_arm64(struct baton baton,uint8_t * function_start,uint32_t encoding)8590b57cec5SDimitry Andric void print_encoding_arm64(struct baton baton, uint8_t *function_start,
8600b57cec5SDimitry Andric                           uint32_t encoding) {
8610b57cec5SDimitry Andric   const int wordsize = 8;
8620b57cec5SDimitry Andric   int mode = encoding & UNWIND_ARM64_MODE_MASK;
8630b57cec5SDimitry Andric   switch (mode) {
8640b57cec5SDimitry Andric   case UNWIND_ARM64_MODE_FRAME: {
8650b57cec5SDimitry Andric     printf("frame func: CFA is fp+%d ", 16);
8660b57cec5SDimitry Andric     printf(" pc=[CFA-8] fp=[CFA-16]");
8670b57cec5SDimitry Andric     int reg_pairs_saved_count = 1;
8680b57cec5SDimitry Andric     uint32_t saved_register_bits = encoding & 0xfff;
8690b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
8700b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
8710b57cec5SDimitry Andric       cfa_offset -= wordsize;
8720b57cec5SDimitry Andric       printf(" x19=[CFA%d]", cfa_offset);
8730b57cec5SDimitry Andric       cfa_offset -= wordsize;
8740b57cec5SDimitry Andric       printf(" x20=[CFA%d]", cfa_offset);
8750b57cec5SDimitry Andric       reg_pairs_saved_count++;
8760b57cec5SDimitry Andric     }
8770b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
8780b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
8790b57cec5SDimitry Andric       cfa_offset -= wordsize;
8800b57cec5SDimitry Andric       printf(" x21=[CFA%d]", cfa_offset);
8810b57cec5SDimitry Andric       cfa_offset -= wordsize;
8820b57cec5SDimitry Andric       printf(" x22=[CFA%d]", cfa_offset);
8830b57cec5SDimitry Andric       reg_pairs_saved_count++;
8840b57cec5SDimitry Andric     }
8850b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
8860b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
8870b57cec5SDimitry Andric       cfa_offset -= wordsize;
8880b57cec5SDimitry Andric       printf(" x23=[CFA%d]", cfa_offset);
8890b57cec5SDimitry Andric       cfa_offset -= wordsize;
8900b57cec5SDimitry Andric       printf(" x24=[CFA%d]", cfa_offset);
8910b57cec5SDimitry Andric       reg_pairs_saved_count++;
8920b57cec5SDimitry Andric     }
8930b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
8940b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
8950b57cec5SDimitry Andric       cfa_offset -= wordsize;
8960b57cec5SDimitry Andric       printf(" x25=[CFA%d]", cfa_offset);
8970b57cec5SDimitry Andric       cfa_offset -= wordsize;
8980b57cec5SDimitry Andric       printf(" x26=[CFA%d]", cfa_offset);
8990b57cec5SDimitry Andric       reg_pairs_saved_count++;
9000b57cec5SDimitry Andric     }
9010b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
9020b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
9030b57cec5SDimitry Andric       cfa_offset -= wordsize;
9040b57cec5SDimitry Andric       printf(" x27=[CFA%d]", cfa_offset);
9050b57cec5SDimitry Andric       cfa_offset -= wordsize;
9060b57cec5SDimitry Andric       printf(" x28=[CFA%d]", cfa_offset);
9070b57cec5SDimitry Andric       reg_pairs_saved_count++;
9080b57cec5SDimitry Andric     }
9090b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
9100b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
9110b57cec5SDimitry Andric       cfa_offset -= wordsize;
9120b57cec5SDimitry Andric       printf(" d8=[CFA%d]", cfa_offset);
9130b57cec5SDimitry Andric       cfa_offset -= wordsize;
9140b57cec5SDimitry Andric       printf(" d9=[CFA%d]", cfa_offset);
9150b57cec5SDimitry Andric       reg_pairs_saved_count++;
9160b57cec5SDimitry Andric     }
9170b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
9180b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
9190b57cec5SDimitry Andric       cfa_offset -= wordsize;
9200b57cec5SDimitry Andric       printf(" d10=[CFA%d]", cfa_offset);
9210b57cec5SDimitry Andric       cfa_offset -= wordsize;
9220b57cec5SDimitry Andric       printf(" d11=[CFA%d]", cfa_offset);
9230b57cec5SDimitry Andric       reg_pairs_saved_count++;
9240b57cec5SDimitry Andric     }
9250b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
9260b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
9270b57cec5SDimitry Andric       cfa_offset -= wordsize;
9280b57cec5SDimitry Andric       printf(" d12=[CFA%d]", cfa_offset);
9290b57cec5SDimitry Andric       cfa_offset -= wordsize;
9300b57cec5SDimitry Andric       printf(" d13=[CFA%d]", cfa_offset);
9310b57cec5SDimitry Andric       reg_pairs_saved_count++;
9320b57cec5SDimitry Andric     }
9330b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
9340b57cec5SDimitry Andric       int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
9350b57cec5SDimitry Andric       cfa_offset -= wordsize;
9360b57cec5SDimitry Andric       printf(" d14=[CFA%d]", cfa_offset);
9370b57cec5SDimitry Andric       cfa_offset -= wordsize;
9380b57cec5SDimitry Andric       printf(" d15=[CFA%d]", cfa_offset);
9390b57cec5SDimitry Andric       reg_pairs_saved_count++;
9400b57cec5SDimitry Andric     }
9410b57cec5SDimitry Andric 
9420b57cec5SDimitry Andric   } break;
9430b57cec5SDimitry Andric 
9440b57cec5SDimitry Andric   case UNWIND_ARM64_MODE_FRAMELESS: {
9450b57cec5SDimitry Andric     uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK;
9460b57cec5SDimitry Andric     printf("frameless function: stack size %d ", stack_size * 16);
9470b57cec5SDimitry Andric 
9480b57cec5SDimitry Andric   } break;
9490b57cec5SDimitry Andric 
9500b57cec5SDimitry Andric   case UNWIND_ARM64_MODE_DWARF: {
9510b57cec5SDimitry Andric     uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET;
9520b57cec5SDimitry Andric     printf(
9530b57cec5SDimitry Andric         "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
9540b57cec5SDimitry Andric         ")",
9550b57cec5SDimitry Andric         dwarf_offset, dwarf_offset + baton.eh_section_file_address);
9560b57cec5SDimitry Andric   } break;
9570b57cec5SDimitry Andric 
9580b57cec5SDimitry Andric   case 0: {
9590b57cec5SDimitry Andric     printf(" no unwind information");
9600b57cec5SDimitry Andric   } break;
9610b57cec5SDimitry Andric   }
9620b57cec5SDimitry Andric }
9630b57cec5SDimitry Andric 
print_encoding_armv7(struct baton baton,uint8_t * function_start,uint32_t encoding)9640b57cec5SDimitry Andric void print_encoding_armv7(struct baton baton, uint8_t *function_start,
9650b57cec5SDimitry Andric                           uint32_t encoding) {
9660b57cec5SDimitry Andric   const int wordsize = 4;
9670b57cec5SDimitry Andric   int mode = encoding & UNWIND_ARM_MODE_MASK;
9680b57cec5SDimitry Andric   switch (mode) {
9690b57cec5SDimitry Andric   case UNWIND_ARM_MODE_FRAME_D:
9700b57cec5SDimitry Andric   case UNWIND_ARM_MODE_FRAME: {
9710b57cec5SDimitry Andric     int stack_adjust =
9720b57cec5SDimitry Andric         EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize;
9730b57cec5SDimitry Andric 
9740b57cec5SDimitry Andric     printf("frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust);
9750b57cec5SDimitry Andric     int cfa_offset = -stack_adjust;
9760b57cec5SDimitry Andric 
9770b57cec5SDimitry Andric     cfa_offset -= wordsize;
9780b57cec5SDimitry Andric     printf(" pc=[CFA%d]", cfa_offset);
9790b57cec5SDimitry Andric     cfa_offset -= wordsize;
9800b57cec5SDimitry Andric     printf(" fp=[CFA%d]", cfa_offset);
9810b57cec5SDimitry Andric 
9820b57cec5SDimitry Andric     uint32_t saved_register_bits = encoding & 0xff;
9830b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) {
9840b57cec5SDimitry Andric       cfa_offset -= wordsize;
9850b57cec5SDimitry Andric       printf(" r6=[CFA%d]", cfa_offset);
9860b57cec5SDimitry Andric     }
9870b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) {
9880b57cec5SDimitry Andric       cfa_offset -= wordsize;
9890b57cec5SDimitry Andric       printf(" r5=[CFA%d]", cfa_offset);
9900b57cec5SDimitry Andric     }
9910b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) {
9920b57cec5SDimitry Andric       cfa_offset -= wordsize;
9930b57cec5SDimitry Andric       printf(" r4=[CFA%d]", cfa_offset);
9940b57cec5SDimitry Andric     }
9950b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) {
9960b57cec5SDimitry Andric       cfa_offset -= wordsize;
9970b57cec5SDimitry Andric       printf(" r12=[CFA%d]", cfa_offset);
9980b57cec5SDimitry Andric     }
9990b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) {
10000b57cec5SDimitry Andric       cfa_offset -= wordsize;
10010b57cec5SDimitry Andric       printf(" r11=[CFA%d]", cfa_offset);
10020b57cec5SDimitry Andric     }
10030b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) {
10040b57cec5SDimitry Andric       cfa_offset -= wordsize;
10050b57cec5SDimitry Andric       printf(" r10=[CFA%d]", cfa_offset);
10060b57cec5SDimitry Andric     }
10070b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) {
10080b57cec5SDimitry Andric       cfa_offset -= wordsize;
10090b57cec5SDimitry Andric       printf(" r9=[CFA%d]", cfa_offset);
10100b57cec5SDimitry Andric     }
10110b57cec5SDimitry Andric     if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) {
10120b57cec5SDimitry Andric       cfa_offset -= wordsize;
10130b57cec5SDimitry Andric       printf(" r8=[CFA%d]", cfa_offset);
10140b57cec5SDimitry Andric     }
10150b57cec5SDimitry Andric 
10160b57cec5SDimitry Andric     if (mode == UNWIND_ARM_MODE_FRAME_D) {
10170b57cec5SDimitry Andric       uint32_t d_reg_bits =
10180b57cec5SDimitry Andric           EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK);
10190b57cec5SDimitry Andric       switch (d_reg_bits) {
10200b57cec5SDimitry Andric       case 0:
10210b57cec5SDimitry Andric         // vpush {d8}
10220b57cec5SDimitry Andric         cfa_offset -= 8;
10230b57cec5SDimitry Andric         printf(" d8=[CFA%d]", cfa_offset);
10240b57cec5SDimitry Andric         break;
10250b57cec5SDimitry Andric       case 1:
10260b57cec5SDimitry Andric         // vpush {d10}
10270b57cec5SDimitry Andric         // vpush {d8}
10280b57cec5SDimitry Andric         cfa_offset -= 8;
10290b57cec5SDimitry Andric         printf(" d10=[CFA%d]", cfa_offset);
10300b57cec5SDimitry Andric         cfa_offset -= 8;
10310b57cec5SDimitry Andric         printf(" d8=[CFA%d]", cfa_offset);
10320b57cec5SDimitry Andric         break;
10330b57cec5SDimitry Andric       case 2:
10340b57cec5SDimitry Andric         // vpush {d12}
10350b57cec5SDimitry Andric         // vpush {d10}
10360b57cec5SDimitry Andric         // vpush {d8}
10370b57cec5SDimitry Andric         cfa_offset -= 8;
10380b57cec5SDimitry Andric         printf(" d12=[CFA%d]", cfa_offset);
10390b57cec5SDimitry Andric         cfa_offset -= 8;
10400b57cec5SDimitry Andric         printf(" d10=[CFA%d]", cfa_offset);
10410b57cec5SDimitry Andric         cfa_offset -= 8;
10420b57cec5SDimitry Andric         printf(" d8=[CFA%d]", cfa_offset);
10430b57cec5SDimitry Andric         break;
10440b57cec5SDimitry Andric       case 3:
10450b57cec5SDimitry Andric         // vpush {d14}
10460b57cec5SDimitry Andric         // vpush {d12}
10470b57cec5SDimitry Andric         // vpush {d10}
10480b57cec5SDimitry Andric         // vpush {d8}
10490b57cec5SDimitry Andric         cfa_offset -= 8;
10500b57cec5SDimitry Andric         printf(" d14=[CFA%d]", cfa_offset);
10510b57cec5SDimitry Andric         cfa_offset -= 8;
10520b57cec5SDimitry Andric         printf(" d12=[CFA%d]", cfa_offset);
10530b57cec5SDimitry Andric         cfa_offset -= 8;
10540b57cec5SDimitry Andric         printf(" d10=[CFA%d]", cfa_offset);
10550b57cec5SDimitry Andric         cfa_offset -= 8;
10560b57cec5SDimitry Andric         printf(" d8=[CFA%d]", cfa_offset);
10570b57cec5SDimitry Andric         break;
10580b57cec5SDimitry Andric       case 4:
10590b57cec5SDimitry Andric         // vpush {d14}
10600b57cec5SDimitry Andric         // vpush {d12}
10610b57cec5SDimitry Andric         // sp = (sp - 24) & (-16);
10620b57cec5SDimitry Andric         // vst   {d8, d9, d10}
10630b57cec5SDimitry Andric         printf(" d14, d12, d10, d9, d8");
10640b57cec5SDimitry Andric         break;
10650b57cec5SDimitry Andric       case 5:
10660b57cec5SDimitry Andric         // vpush {d14}
10670b57cec5SDimitry Andric         // sp = (sp - 40) & (-16);
10680b57cec5SDimitry Andric         // vst   {d8, d9, d10, d11}
10690b57cec5SDimitry Andric         // vst   {d12}
10700b57cec5SDimitry Andric         printf(" d14, d11, d10, d9, d8, d12");
10710b57cec5SDimitry Andric         break;
10720b57cec5SDimitry Andric       case 6:
10730b57cec5SDimitry Andric         // sp = (sp - 56) & (-16);
10740b57cec5SDimitry Andric         // vst   {d8, d9, d10, d11}
10750b57cec5SDimitry Andric         // vst   {d12, d13, d14}
10760b57cec5SDimitry Andric         printf(" d11, d10, d9, d8, d14, d13, d12");
10770b57cec5SDimitry Andric         break;
10780b57cec5SDimitry Andric       case 7:
10790b57cec5SDimitry Andric         // sp = (sp - 64) & (-16);
10800b57cec5SDimitry Andric         // vst   {d8, d9, d10, d11}
10810b57cec5SDimitry Andric         // vst   {d12, d13, d14, d15}
10820b57cec5SDimitry Andric         printf(" d11, d10, d9, d8, d15, d14, d13, d12");
10830b57cec5SDimitry Andric         break;
10840b57cec5SDimitry Andric       }
10850b57cec5SDimitry Andric     }
10860b57cec5SDimitry Andric   } break;
10870b57cec5SDimitry Andric 
10880b57cec5SDimitry Andric   case UNWIND_ARM_MODE_DWARF: {
10890b57cec5SDimitry Andric     uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET;
10900b57cec5SDimitry Andric     printf(
10910b57cec5SDimitry Andric         "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
10920b57cec5SDimitry Andric         ")",
10930b57cec5SDimitry Andric         dwarf_offset, dwarf_offset + baton.eh_section_file_address);
10940b57cec5SDimitry Andric   } break;
10950b57cec5SDimitry Andric 
10960b57cec5SDimitry Andric   case 0: {
10970b57cec5SDimitry Andric     printf(" no unwind information");
10980b57cec5SDimitry Andric   } break;
10990b57cec5SDimitry Andric   }
11000b57cec5SDimitry Andric }
11010b57cec5SDimitry Andric 
print_encoding(struct baton baton,uint8_t * function_start,uint32_t encoding)11020b57cec5SDimitry Andric void print_encoding(struct baton baton, uint8_t *function_start,
11030b57cec5SDimitry Andric                     uint32_t encoding) {
11040b57cec5SDimitry Andric 
11050b57cec5SDimitry Andric   if (baton.cputype == CPU_TYPE_X86_64) {
11060b57cec5SDimitry Andric     print_encoding_x86_64(baton, function_start, encoding);
11070b57cec5SDimitry Andric   } else if (baton.cputype == CPU_TYPE_I386) {
11080b57cec5SDimitry Andric     print_encoding_i386(baton, function_start, encoding);
1109*9dba64beSDimitry Andric   } else if (baton.cputype == CPU_TYPE_ARM64 || baton.cputype == CPU_TYPE_ARM64_32) {
11100b57cec5SDimitry Andric     print_encoding_arm64(baton, function_start, encoding);
11110b57cec5SDimitry Andric   } else if (baton.cputype == CPU_TYPE_ARM) {
11120b57cec5SDimitry Andric     print_encoding_armv7(baton, function_start, encoding);
11130b57cec5SDimitry Andric   } else {
11140b57cec5SDimitry Andric     printf(" -- unsupported encoding arch -- ");
11150b57cec5SDimitry Andric   }
11160b57cec5SDimitry Andric }
11170b57cec5SDimitry Andric 
print_function_encoding(struct baton baton,uint32_t idx,uint32_t encoding,uint32_t entry_encoding_index,uint32_t entry_func_offset)11180b57cec5SDimitry Andric void print_function_encoding(struct baton baton, uint32_t idx,
11190b57cec5SDimitry Andric                              uint32_t encoding, uint32_t entry_encoding_index,
11200b57cec5SDimitry Andric                              uint32_t entry_func_offset) {
11210b57cec5SDimitry Andric 
11220b57cec5SDimitry Andric   char *entry_encoding_index_str = "";
11230b57cec5SDimitry Andric   if (entry_encoding_index != (uint32_t)-1) {
11240b57cec5SDimitry Andric     asprintf(&entry_encoding_index_str, ", encoding #%d", entry_encoding_index);
11250b57cec5SDimitry Andric   } else {
11260b57cec5SDimitry Andric     asprintf(&entry_encoding_index_str, "");
11270b57cec5SDimitry Andric   }
11280b57cec5SDimitry Andric 
11290b57cec5SDimitry Andric   uint64_t file_address = baton.first_level_index_entry.functionOffset +
11300b57cec5SDimitry Andric                           entry_func_offset + baton.text_segment_vmaddr;
11310b57cec5SDimitry Andric 
11320b57cec5SDimitry Andric   if (baton.cputype == CPU_TYPE_ARM)
11330b57cec5SDimitry Andric     file_address = file_address & ~1;
11340b57cec5SDimitry Andric 
11350b57cec5SDimitry Andric   printf(
11360b57cec5SDimitry Andric       "    func [%d] offset %d (file addr 0x%" PRIx64 ")%s, encoding is 0x%x",
11370b57cec5SDimitry Andric       idx, entry_func_offset, file_address, entry_encoding_index_str, encoding);
11380b57cec5SDimitry Andric 
11390b57cec5SDimitry Andric   struct symbol *symbol = NULL;
11400b57cec5SDimitry Andric   for (int i = 0; i < baton.symbols_count; i++) {
11410b57cec5SDimitry Andric     if (i == baton.symbols_count - 1 &&
11420b57cec5SDimitry Andric         baton.symbols[i].file_address <= file_address) {
11430b57cec5SDimitry Andric       symbol = &(baton.symbols[i]);
11440b57cec5SDimitry Andric       break;
11450b57cec5SDimitry Andric     } else {
11460b57cec5SDimitry Andric       if (baton.symbols[i].file_address <= file_address &&
11470b57cec5SDimitry Andric           baton.symbols[i + 1].file_address > file_address) {
11480b57cec5SDimitry Andric         symbol = &(baton.symbols[i]);
11490b57cec5SDimitry Andric         break;
11500b57cec5SDimitry Andric       }
11510b57cec5SDimitry Andric     }
11520b57cec5SDimitry Andric   }
11530b57cec5SDimitry Andric 
11540b57cec5SDimitry Andric   printf("\n         ");
11550b57cec5SDimitry Andric   if (symbol) {
11560b57cec5SDimitry Andric     int offset = file_address - symbol->file_address;
11570b57cec5SDimitry Andric 
11580b57cec5SDimitry Andric     // FIXME this is a poor heuristic - if we're greater than 16 bytes past the
11590b57cec5SDimitry Andric     // start of the function, this is the unwind info for a stripped function.
11600b57cec5SDimitry Andric     // In reality the compact unwind entry may not line up exactly with the
11610b57cec5SDimitry Andric     // function bounds.
11620b57cec5SDimitry Andric     if (offset >= 0) {
11630b57cec5SDimitry Andric       printf("name: %s", symbol->name);
11640b57cec5SDimitry Andric       if (offset > 0) {
11650b57cec5SDimitry Andric         printf(" + %d", offset);
11660b57cec5SDimitry Andric       }
11670b57cec5SDimitry Andric     }
11680b57cec5SDimitry Andric     printf("\n         ");
11690b57cec5SDimitry Andric   }
11700b57cec5SDimitry Andric 
11710b57cec5SDimitry Andric   print_encoding(baton, baton.mach_header_start +
11720b57cec5SDimitry Andric                             baton.first_level_index_entry.functionOffset +
11730b57cec5SDimitry Andric                             baton.text_section_file_offset + entry_func_offset,
11740b57cec5SDimitry Andric                  encoding);
11750b57cec5SDimitry Andric 
11760b57cec5SDimitry Andric   bool has_lsda = encoding & UNWIND_HAS_LSDA;
11770b57cec5SDimitry Andric 
11780b57cec5SDimitry Andric   if (has_lsda) {
11790b57cec5SDimitry Andric     uint32_t func_offset =
11800b57cec5SDimitry Andric         entry_func_offset + baton.first_level_index_entry.functionOffset;
11810b57cec5SDimitry Andric 
11820b57cec5SDimitry Andric     int lsda_entry_number = -1;
11830b57cec5SDimitry Andric 
11840b57cec5SDimitry Andric     uint32_t low = 0;
11850b57cec5SDimitry Andric     uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) /
11860b57cec5SDimitry Andric                     sizeof(struct unwind_info_section_header_lsda_index_entry);
11870b57cec5SDimitry Andric 
11880b57cec5SDimitry Andric     while (low < high) {
11890b57cec5SDimitry Andric       uint32_t mid = (low + high) / 2;
11900b57cec5SDimitry Andric 
11910b57cec5SDimitry Andric       uint8_t *mid_lsda_entry_addr =
11920b57cec5SDimitry Andric           (baton.lsda_array_start +
11930b57cec5SDimitry Andric            (mid * sizeof(struct unwind_info_section_header_lsda_index_entry)));
11940b57cec5SDimitry Andric       struct unwind_info_section_header_lsda_index_entry mid_lsda_entry;
11950b57cec5SDimitry Andric       memcpy(&mid_lsda_entry, mid_lsda_entry_addr,
11960b57cec5SDimitry Andric              sizeof(struct unwind_info_section_header_lsda_index_entry));
11970b57cec5SDimitry Andric       if (mid_lsda_entry.functionOffset == func_offset) {
11980b57cec5SDimitry Andric         lsda_entry_number =
11990b57cec5SDimitry Andric             (mid_lsda_entry_addr - baton.lsda_array_start) /
12000b57cec5SDimitry Andric             sizeof(struct unwind_info_section_header_lsda_index_entry);
12010b57cec5SDimitry Andric         break;
12020b57cec5SDimitry Andric       } else if (mid_lsda_entry.functionOffset < func_offset) {
12030b57cec5SDimitry Andric         low = mid + 1;
12040b57cec5SDimitry Andric       } else {
12050b57cec5SDimitry Andric         high = mid;
12060b57cec5SDimitry Andric       }
12070b57cec5SDimitry Andric     }
12080b57cec5SDimitry Andric 
12090b57cec5SDimitry Andric     if (lsda_entry_number != -1) {
12100b57cec5SDimitry Andric       printf(", LSDA entry #%d", lsda_entry_number);
12110b57cec5SDimitry Andric     } else {
12120b57cec5SDimitry Andric       printf(", LSDA entry not found");
12130b57cec5SDimitry Andric     }
12140b57cec5SDimitry Andric   }
12150b57cec5SDimitry Andric 
12160b57cec5SDimitry Andric   uint32_t pers_idx = EXTRACT_BITS(encoding, UNWIND_PERSONALITY_MASK);
12170b57cec5SDimitry Andric   if (pers_idx != 0) {
12180b57cec5SDimitry Andric     pers_idx--; // Change 1-based to 0-based index
12190b57cec5SDimitry Andric     printf(", personality entry #%d", pers_idx);
12200b57cec5SDimitry Andric   }
12210b57cec5SDimitry Andric 
12220b57cec5SDimitry Andric   printf("\n");
12230b57cec5SDimitry Andric }
12240b57cec5SDimitry Andric 
print_second_level_index_regular(struct baton baton)12250b57cec5SDimitry Andric void print_second_level_index_regular(struct baton baton) {
12260b57cec5SDimitry Andric   uint8_t *page_entries =
12270b57cec5SDimitry Andric       baton.compact_unwind_start +
12280b57cec5SDimitry Andric       baton.first_level_index_entry.secondLevelPagesSectionOffset +
12290b57cec5SDimitry Andric       baton.regular_second_level_page_header.entryPageOffset;
12300b57cec5SDimitry Andric   uint32_t entries_count = baton.regular_second_level_page_header.entryCount;
12310b57cec5SDimitry Andric 
12320b57cec5SDimitry Andric   uint8_t *offset = page_entries;
12330b57cec5SDimitry Andric 
12340b57cec5SDimitry Andric   uint32_t idx = 0;
12350b57cec5SDimitry Andric   while (idx < entries_count) {
12360b57cec5SDimitry Andric     uint32_t func_offset = *((uint32_t *)(offset));
12370b57cec5SDimitry Andric     uint32_t encoding = *((uint32_t *)(offset + 4));
12380b57cec5SDimitry Andric 
12390b57cec5SDimitry Andric     // UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the
12400b57cec5SDimitry Andric     // functionOffset from the containing index table already.
12410b57cec5SDimitry Andric     // UNWIND_SECOND_LEVEL_COMPRESSED
12420b57cec5SDimitry Andric     // entries only have the offset from the containing index table
12430b57cec5SDimitry Andric     // functionOffset.
12440b57cec5SDimitry Andric     // So strip off the containing index table functionOffset value here so they
12450b57cec5SDimitry Andric     // can
12460b57cec5SDimitry Andric     // be treated the same at the lower layers.
12470b57cec5SDimitry Andric 
12480b57cec5SDimitry Andric     print_function_encoding(baton, idx, encoding, (uint32_t)-1,
12490b57cec5SDimitry Andric                             func_offset -
12500b57cec5SDimitry Andric                                 baton.first_level_index_entry.functionOffset);
12510b57cec5SDimitry Andric     idx++;
12520b57cec5SDimitry Andric     offset += 8;
12530b57cec5SDimitry Andric   }
12540b57cec5SDimitry Andric }
12550b57cec5SDimitry Andric 
print_second_level_index_compressed(struct baton baton)12560b57cec5SDimitry Andric void print_second_level_index_compressed(struct baton baton) {
12570b57cec5SDimitry Andric   uint8_t *this_index =
12580b57cec5SDimitry Andric       baton.compact_unwind_start +
12590b57cec5SDimitry Andric       baton.first_level_index_entry.secondLevelPagesSectionOffset;
12600b57cec5SDimitry Andric   uint8_t *start_of_entries =
12610b57cec5SDimitry Andric       this_index + baton.compressed_second_level_page_header.entryPageOffset;
12620b57cec5SDimitry Andric   uint8_t *offset = start_of_entries;
12630b57cec5SDimitry Andric   for (uint16_t idx = 0;
12640b57cec5SDimitry Andric        idx < baton.compressed_second_level_page_header.entryCount; idx++) {
12650b57cec5SDimitry Andric     uint32_t entry = *((uint32_t *)offset);
12660b57cec5SDimitry Andric     offset += 4;
12670b57cec5SDimitry Andric     uint32_t encoding;
12680b57cec5SDimitry Andric 
12690b57cec5SDimitry Andric     uint32_t entry_encoding_index =
12700b57cec5SDimitry Andric         UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry);
12710b57cec5SDimitry Andric     uint32_t entry_func_offset =
12720b57cec5SDimitry Andric         UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry);
12730b57cec5SDimitry Andric 
12740b57cec5SDimitry Andric     if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) {
12750b57cec5SDimitry Andric       // encoding is in common table in section header
12760b57cec5SDimitry Andric       encoding =
12770b57cec5SDimitry Andric           *((uint32_t *)(baton.compact_unwind_start +
12780b57cec5SDimitry Andric                          baton.unwind_header.commonEncodingsArraySectionOffset +
12790b57cec5SDimitry Andric                          (entry_encoding_index * sizeof(uint32_t))));
12800b57cec5SDimitry Andric     } else {
12810b57cec5SDimitry Andric       // encoding is in page specific table
12820b57cec5SDimitry Andric       uint32_t page_encoding_index =
12830b57cec5SDimitry Andric           entry_encoding_index - baton.unwind_header.commonEncodingsArrayCount;
12840b57cec5SDimitry Andric       encoding = *((uint32_t *)(this_index +
12850b57cec5SDimitry Andric                                 baton.compressed_second_level_page_header
12860b57cec5SDimitry Andric                                     .encodingsPageOffset +
12870b57cec5SDimitry Andric                                 (page_encoding_index * sizeof(uint32_t))));
12880b57cec5SDimitry Andric     }
12890b57cec5SDimitry Andric 
12900b57cec5SDimitry Andric     print_function_encoding(baton, idx, encoding, entry_encoding_index,
12910b57cec5SDimitry Andric                             entry_func_offset);
12920b57cec5SDimitry Andric   }
12930b57cec5SDimitry Andric }
12940b57cec5SDimitry Andric 
print_second_level_index(struct baton baton)12950b57cec5SDimitry Andric void print_second_level_index(struct baton baton) {
12960b57cec5SDimitry Andric   uint8_t *index_start =
12970b57cec5SDimitry Andric       baton.compact_unwind_start +
12980b57cec5SDimitry Andric       baton.first_level_index_entry.secondLevelPagesSectionOffset;
12990b57cec5SDimitry Andric 
13000b57cec5SDimitry Andric   if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_REGULAR) {
13010b57cec5SDimitry Andric     struct unwind_info_regular_second_level_page_header header;
13020b57cec5SDimitry Andric     memcpy(&header, index_start,
13030b57cec5SDimitry Andric            sizeof(struct unwind_info_regular_second_level_page_header));
13040b57cec5SDimitry Andric     printf(
13050b57cec5SDimitry Andric         "  UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n",
13060b57cec5SDimitry Andric         baton.current_index_table_number, header.entryPageOffset,
13070b57cec5SDimitry Andric         header.entryCount);
13080b57cec5SDimitry Andric     baton.regular_second_level_page_header = header;
13090b57cec5SDimitry Andric     print_second_level_index_regular(baton);
13100b57cec5SDimitry Andric   }
13110b57cec5SDimitry Andric 
13120b57cec5SDimitry Andric   if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_COMPRESSED) {
13130b57cec5SDimitry Andric     struct unwind_info_compressed_second_level_page_header header;
13140b57cec5SDimitry Andric     memcpy(&header, index_start,
13150b57cec5SDimitry Andric            sizeof(struct unwind_info_compressed_second_level_page_header));
13160b57cec5SDimitry Andric     printf("  UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, "
13170b57cec5SDimitry Andric            "entryCount %d, encodingsPageOffset %d, encodingsCount %d\n",
13180b57cec5SDimitry Andric            baton.current_index_table_number, header.entryPageOffset,
13190b57cec5SDimitry Andric            header.entryCount, header.encodingsPageOffset,
13200b57cec5SDimitry Andric            header.encodingsCount);
13210b57cec5SDimitry Andric     baton.compressed_second_level_page_header = header;
13220b57cec5SDimitry Andric     print_second_level_index_compressed(baton);
13230b57cec5SDimitry Andric   }
13240b57cec5SDimitry Andric }
13250b57cec5SDimitry Andric 
print_index_sections(struct baton baton)13260b57cec5SDimitry Andric void print_index_sections(struct baton baton) {
13270b57cec5SDimitry Andric   uint8_t *index_section_offset =
13280b57cec5SDimitry Andric       baton.compact_unwind_start + baton.unwind_header.indexSectionOffset;
13290b57cec5SDimitry Andric   uint32_t index_count = baton.unwind_header.indexCount;
13300b57cec5SDimitry Andric 
13310b57cec5SDimitry Andric   uint32_t cur_idx = 0;
13320b57cec5SDimitry Andric 
13330b57cec5SDimitry Andric   uint8_t *offset = index_section_offset;
13340b57cec5SDimitry Andric   while (cur_idx < index_count) {
13350b57cec5SDimitry Andric     baton.current_index_table_number = cur_idx;
13360b57cec5SDimitry Andric     struct unwind_info_section_header_index_entry index_entry;
13370b57cec5SDimitry Andric     memcpy(&index_entry, offset,
13380b57cec5SDimitry Andric            sizeof(struct unwind_info_section_header_index_entry));
13390b57cec5SDimitry Andric     printf("index section #%d: functionOffset %d, "
13400b57cec5SDimitry Andric            "secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n",
13410b57cec5SDimitry Andric            cur_idx, index_entry.functionOffset,
13420b57cec5SDimitry Andric            index_entry.secondLevelPagesSectionOffset,
13430b57cec5SDimitry Andric            index_entry.lsdaIndexArraySectionOffset);
13440b57cec5SDimitry Andric 
13450b57cec5SDimitry Andric     // secondLevelPagesSectionOffset == 0 means this is a sentinel entry
13460b57cec5SDimitry Andric     if (index_entry.secondLevelPagesSectionOffset != 0) {
13470b57cec5SDimitry Andric       struct unwind_info_section_header_index_entry next_index_entry;
13480b57cec5SDimitry Andric       memcpy(&next_index_entry,
13490b57cec5SDimitry Andric              offset + sizeof(struct unwind_info_section_header_index_entry),
13500b57cec5SDimitry Andric              sizeof(struct unwind_info_section_header_index_entry));
13510b57cec5SDimitry Andric 
13520b57cec5SDimitry Andric       baton.lsda_array_start =
13530b57cec5SDimitry Andric           baton.compact_unwind_start + index_entry.lsdaIndexArraySectionOffset;
13540b57cec5SDimitry Andric       baton.lsda_array_end = baton.compact_unwind_start +
13550b57cec5SDimitry Andric                              next_index_entry.lsdaIndexArraySectionOffset;
13560b57cec5SDimitry Andric 
13570b57cec5SDimitry Andric       uint8_t *lsda_entry_offset = baton.lsda_array_start;
13580b57cec5SDimitry Andric       uint32_t lsda_count = 0;
13590b57cec5SDimitry Andric       while (lsda_entry_offset < baton.lsda_array_end) {
13600b57cec5SDimitry Andric         struct unwind_info_section_header_lsda_index_entry lsda_entry;
13610b57cec5SDimitry Andric         memcpy(&lsda_entry, lsda_entry_offset,
13620b57cec5SDimitry Andric                sizeof(struct unwind_info_section_header_lsda_index_entry));
13630b57cec5SDimitry Andric         uint64_t function_file_address =
13640b57cec5SDimitry Andric             baton.first_level_index_entry.functionOffset +
13650b57cec5SDimitry Andric             lsda_entry.functionOffset + baton.text_segment_vmaddr;
13660b57cec5SDimitry Andric         uint64_t lsda_file_address =
13670b57cec5SDimitry Andric             lsda_entry.lsdaOffset + baton.text_segment_vmaddr;
13680b57cec5SDimitry Andric         printf("    LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx64
13690b57cec5SDimitry Andric                "), lsdaOffset %d (file address 0x%" PRIx64 ")\n",
13700b57cec5SDimitry Andric                lsda_count, lsda_entry.functionOffset,
13710b57cec5SDimitry Andric                lsda_entry.functionOffset - index_entry.functionOffset,
13720b57cec5SDimitry Andric                function_file_address, lsda_entry.lsdaOffset, lsda_file_address);
13730b57cec5SDimitry Andric         lsda_count++;
13740b57cec5SDimitry Andric         lsda_entry_offset +=
13750b57cec5SDimitry Andric             sizeof(struct unwind_info_section_header_lsda_index_entry);
13760b57cec5SDimitry Andric       }
13770b57cec5SDimitry Andric 
13780b57cec5SDimitry Andric       printf("\n");
13790b57cec5SDimitry Andric 
13800b57cec5SDimitry Andric       baton.first_level_index_entry = index_entry;
13810b57cec5SDimitry Andric       print_second_level_index(baton);
13820b57cec5SDimitry Andric     }
13830b57cec5SDimitry Andric 
13840b57cec5SDimitry Andric     printf("\n");
13850b57cec5SDimitry Andric 
13860b57cec5SDimitry Andric     cur_idx++;
13870b57cec5SDimitry Andric     offset += sizeof(struct unwind_info_section_header_index_entry);
13880b57cec5SDimitry Andric   }
13890b57cec5SDimitry Andric }
13900b57cec5SDimitry Andric 
main(int argc,char ** argv)13910b57cec5SDimitry Andric int main(int argc, char **argv) {
13920b57cec5SDimitry Andric   struct stat st;
13930b57cec5SDimitry Andric   char *file = argv[0];
13940b57cec5SDimitry Andric   if (argc > 1)
13950b57cec5SDimitry Andric     file = argv[1];
13960b57cec5SDimitry Andric   int fd = open(file, O_RDONLY);
13970b57cec5SDimitry Andric   if (fd == -1) {
13980b57cec5SDimitry Andric     printf("Failed to open '%s'\n", file);
13990b57cec5SDimitry Andric     exit(1);
14000b57cec5SDimitry Andric   }
14010b57cec5SDimitry Andric   fstat(fd, &st);
14020b57cec5SDimitry Andric   uint8_t *file_mem =
14030b57cec5SDimitry Andric       (uint8_t *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);
14040b57cec5SDimitry Andric   if (file_mem == MAP_FAILED) {
14050b57cec5SDimitry Andric     printf("Failed to mmap() '%s'\n", file);
14060b57cec5SDimitry Andric   }
14070b57cec5SDimitry Andric 
14080b57cec5SDimitry Andric   FILE *f = fopen("a.out", "r");
14090b57cec5SDimitry Andric 
14100b57cec5SDimitry Andric   struct baton baton;
14110b57cec5SDimitry Andric   baton.mach_header_start = file_mem;
14120b57cec5SDimitry Andric   baton.symbols = NULL;
14130b57cec5SDimitry Andric   baton.symbols_count = 0;
14140b57cec5SDimitry Andric   baton.function_start_addresses = NULL;
14150b57cec5SDimitry Andric   baton.function_start_addresses_count = 0;
14160b57cec5SDimitry Andric 
14170b57cec5SDimitry Andric   scan_macho_load_commands(&baton);
14180b57cec5SDimitry Andric 
14190b57cec5SDimitry Andric   if (baton.compact_unwind_start == NULL) {
14200b57cec5SDimitry Andric     printf("could not find __TEXT,__unwind_info section\n");
14210b57cec5SDimitry Andric     exit(1);
14220b57cec5SDimitry Andric   }
14230b57cec5SDimitry Andric 
14240b57cec5SDimitry Andric   struct unwind_info_section_header header;
14250b57cec5SDimitry Andric   memcpy(&header, baton.compact_unwind_start,
14260b57cec5SDimitry Andric          sizeof(struct unwind_info_section_header));
14270b57cec5SDimitry Andric   printf("Header:\n");
14280b57cec5SDimitry Andric   printf("  version %u\n", header.version);
14290b57cec5SDimitry Andric   printf("  commonEncodingsArraySectionOffset is %d\n",
14300b57cec5SDimitry Andric          header.commonEncodingsArraySectionOffset);
14310b57cec5SDimitry Andric   printf("  commonEncodingsArrayCount is %d\n",
14320b57cec5SDimitry Andric          header.commonEncodingsArrayCount);
14330b57cec5SDimitry Andric   printf("  personalityArraySectionOffset is %d\n",
14340b57cec5SDimitry Andric          header.personalityArraySectionOffset);
14350b57cec5SDimitry Andric   printf("  personalityArrayCount is %d\n", header.personalityArrayCount);
14360b57cec5SDimitry Andric   printf("  indexSectionOffset is %d\n", header.indexSectionOffset);
14370b57cec5SDimitry Andric   printf("  indexCount is %d\n", header.indexCount);
14380b57cec5SDimitry Andric 
14390b57cec5SDimitry Andric   uint8_t *common_encodings =
14400b57cec5SDimitry Andric       baton.compact_unwind_start + header.commonEncodingsArraySectionOffset;
14410b57cec5SDimitry Andric   uint32_t encoding_idx = 0;
14420b57cec5SDimitry Andric   while (encoding_idx < header.commonEncodingsArrayCount) {
14430b57cec5SDimitry Andric     uint32_t encoding = *((uint32_t *)common_encodings);
14440b57cec5SDimitry Andric     printf("    Common Encoding [%d]: 0x%x ", encoding_idx, encoding);
14450b57cec5SDimitry Andric     print_encoding(baton, NULL, encoding);
14460b57cec5SDimitry Andric     printf("\n");
14470b57cec5SDimitry Andric     common_encodings += sizeof(uint32_t);
14480b57cec5SDimitry Andric     encoding_idx++;
14490b57cec5SDimitry Andric   }
14500b57cec5SDimitry Andric 
14510b57cec5SDimitry Andric   uint8_t *pers_arr =
14520b57cec5SDimitry Andric       baton.compact_unwind_start + header.personalityArraySectionOffset;
14530b57cec5SDimitry Andric   uint32_t pers_idx = 0;
14540b57cec5SDimitry Andric   while (pers_idx < header.personalityArrayCount) {
14550b57cec5SDimitry Andric     int32_t pers_delta = *((int32_t *)(baton.compact_unwind_start +
14560b57cec5SDimitry Andric                                        header.personalityArraySectionOffset +
14570b57cec5SDimitry Andric                                        (pers_idx * sizeof(uint32_t))));
14580b57cec5SDimitry Andric     printf("    Personality [%d]: personality function ptr @ offset %d (file "
14590b57cec5SDimitry Andric            "address 0x%" PRIx64 ")\n",
14600b57cec5SDimitry Andric            pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta);
14610b57cec5SDimitry Andric     pers_idx++;
14620b57cec5SDimitry Andric     pers_arr += sizeof(uint32_t);
14630b57cec5SDimitry Andric   }
14640b57cec5SDimitry Andric 
14650b57cec5SDimitry Andric   printf("\n");
14660b57cec5SDimitry Andric 
14670b57cec5SDimitry Andric   baton.unwind_header = header;
14680b57cec5SDimitry Andric 
14690b57cec5SDimitry Andric   print_index_sections(baton);
14700b57cec5SDimitry Andric 
14710b57cec5SDimitry Andric   return 0;
14720b57cec5SDimitry Andric }
1473