1 | #include <fcntl.h> |
2 | #include <inttypes.h> |
3 | #include <mach-o/compact_unwind_encoding.h> |
4 | #include <mach-o/loader.h> |
5 | #include <mach-o/nlist.h> |
6 | #include <mach/machine.h> |
7 | #include <stdbool.h> |
8 | #include <stdint.h> |
9 | #include <stdio.h> |
10 | #include <stdlib.h> |
11 | #include <string.h> |
12 | #include <sys/errno.h> |
13 | #include <sys/mman.h> |
14 | #include <sys/stat.h> |
15 | #include <sys/types.h> |
16 | |
17 | #define (value, mask) \ |
18 | ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) |
19 | |
20 | // A quick sketch of a program which can parse the compact unwind info |
21 | // used on Darwin systems for exception handling. The output of |
22 | // unwinddump will be more authoritative/reliable but this program |
23 | // can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries |
24 | // correctly. |
25 | |
26 | struct symbol { |
27 | uint64_t file_address; |
28 | const char *name; |
29 | }; |
30 | |
31 | int symbol_compare(const void *a, const void *b) { |
32 | return (int)((struct symbol *)a)->file_address - |
33 | ((struct symbol *)b)->file_address; |
34 | } |
35 | |
36 | struct baton { |
37 | cpu_type_t cputype; |
38 | |
39 | uint8_t *; // pointer into this program's address space |
40 | uint8_t *compact_unwind_start; // pointer into this program's address space |
41 | |
42 | int addr_size; // 4 or 8 bytes, the size of addresses in this file |
43 | |
44 | uint64_t text_segment_vmaddr; // __TEXT segment vmaddr |
45 | uint64_t text_segment_file_offset; |
46 | |
47 | uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr |
48 | uint64_t text_section_file_offset; |
49 | |
50 | uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame |
51 | // section |
52 | |
53 | uint8_t |
54 | *lsda_array_start; // for the currently-being-processed first-level index |
55 | uint8_t |
56 | *lsda_array_end; // the lsda_array_start for the NEXT first-level index |
57 | |
58 | struct symbol *symbols; |
59 | int symbols_count; |
60 | |
61 | uint64_t *function_start_addresses; |
62 | int function_start_addresses_count; |
63 | |
64 | int current_index_table_number; |
65 | |
66 | struct ; |
67 | struct first_level_index_entry; |
68 | struct |
69 | ; |
70 | struct |
71 | ; |
72 | }; |
73 | |
74 | uint64_t read_leb128(uint8_t **offset) { |
75 | uint64_t result = 0; |
76 | int shift = 0; |
77 | while (1) { |
78 | uint8_t byte = **offset; |
79 | *offset = *offset + 1; |
80 | result |= (byte & 0x7f) << shift; |
81 | if ((byte & 0x80) == 0) |
82 | break; |
83 | shift += 7; |
84 | } |
85 | |
86 | return result; |
87 | } |
88 | |
89 | // step through the load commands in a thin mach-o binary, |
90 | // find the cputype and the start of the __TEXT,__unwind_info |
91 | // section, return a pointer to that section or NULL if not found. |
92 | |
93 | static void scan_macho_load_commands(struct baton *baton) { |
94 | struct symtab_command symtab_cmd; |
95 | uint64_t linkedit_segment_vmaddr; |
96 | uint64_t linkedit_segment_file_offset; |
97 | |
98 | baton->compact_unwind_start = 0; |
99 | |
100 | uint32_t *magic = (uint32_t *)baton->mach_header_start; |
101 | |
102 | if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) { |
103 | printf(format: "Unexpected magic number 0x%x in header, exiting." , *magic); |
104 | exit(status: 1); |
105 | } |
106 | |
107 | bool is_64bit = false; |
108 | if (*magic == MH_MAGIC_64) |
109 | is_64bit = true; |
110 | |
111 | uint8_t *offset = baton->mach_header_start; |
112 | |
113 | struct mh; |
114 | memcpy(&mh, offset, sizeof(struct mach_header)); |
115 | if (is_64bit) |
116 | offset += sizeof(struct mach_header_64); |
117 | else |
118 | offset += sizeof(struct mach_header); |
119 | |
120 | if (is_64bit) |
121 | baton->addr_size = 8; |
122 | else |
123 | baton->addr_size = 4; |
124 | |
125 | baton->cputype = mh.cputype; |
126 | |
127 | uint8_t *start_of_load_commands = offset; |
128 | |
129 | uint32_t cur_cmd = 0; |
130 | while (cur_cmd < mh.ncmds && |
131 | (offset - start_of_load_commands) < mh.sizeofcmds) { |
132 | struct load_command lc; |
133 | uint32_t *lc_cmd = (uint32_t *)offset; |
134 | uint32_t *lc_cmdsize = (uint32_t *)offset + 1; |
135 | uint8_t *start_of_this_load_cmd = offset; |
136 | |
137 | if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) { |
138 | char segment_name[17]; |
139 | segment_name[0] = '\0'; |
140 | uint32_t nsects = 0; |
141 | uint64_t segment_offset = 0; |
142 | uint64_t segment_vmaddr = 0; |
143 | |
144 | if (*lc_cmd == LC_SEGMENT_64) { |
145 | struct segment_command_64 seg; |
146 | memcpy(&seg, offset, sizeof(struct segment_command_64)); |
147 | memcpy(dest: &segment_name, src: &seg.segname, n: 16); |
148 | segment_name[16] = '\0'; |
149 | nsects = seg.nsects; |
150 | segment_offset = seg.fileoff; |
151 | segment_vmaddr = seg.vmaddr; |
152 | offset += sizeof(struct segment_command_64); |
153 | if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) { |
154 | printf(format: "Segment '%s' is encrypted.\n" , segment_name); |
155 | } |
156 | } |
157 | |
158 | if (*lc_cmd == LC_SEGMENT) { |
159 | struct segment_command seg; |
160 | memcpy(&seg, offset, sizeof(struct segment_command)); |
161 | memcpy(dest: &segment_name, src: &seg.segname, n: 16); |
162 | segment_name[16] = '\0'; |
163 | nsects = seg.nsects; |
164 | segment_offset = seg.fileoff; |
165 | segment_vmaddr = seg.vmaddr; |
166 | offset += sizeof(struct segment_command); |
167 | if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) { |
168 | printf(format: "Segment '%s' is encrypted.\n" , segment_name); |
169 | } |
170 | } |
171 | |
172 | if (nsects != 0 && strcmp(s1: segment_name, s2: "__TEXT" ) == 0) { |
173 | baton->text_segment_vmaddr = segment_vmaddr; |
174 | baton->text_segment_file_offset = segment_offset; |
175 | |
176 | uint32_t current_sect = 0; |
177 | while (current_sect < nsects && |
178 | (offset - start_of_this_load_cmd) < *lc_cmdsize) { |
179 | char sect_name[17]; |
180 | memcpy(dest: §_name, src: offset, n: 16); |
181 | sect_name[16] = '\0'; |
182 | if (strcmp(s1: sect_name, s2: "__unwind_info" ) == 0) { |
183 | if (is_64bit) { |
184 | struct section_64 sect; |
185 | memset(§, 0, sizeof(struct section_64)); |
186 | memcpy(§, offset, sizeof(struct section_64)); |
187 | baton->compact_unwind_start = |
188 | baton->mach_header_start + sect.offset; |
189 | } else { |
190 | struct section sect; |
191 | memset(§, 0, sizeof(struct section)); |
192 | memcpy(§, offset, sizeof(struct section)); |
193 | baton->compact_unwind_start = |
194 | baton->mach_header_start + sect.offset; |
195 | } |
196 | } |
197 | if (strcmp(s1: sect_name, s2: "__eh_frame" ) == 0) { |
198 | if (is_64bit) { |
199 | struct section_64 sect; |
200 | memset(§, 0, sizeof(struct section_64)); |
201 | memcpy(§, offset, sizeof(struct section_64)); |
202 | baton->eh_section_file_address = sect.addr; |
203 | } else { |
204 | struct section sect; |
205 | memset(§, 0, sizeof(struct section)); |
206 | memcpy(§, offset, sizeof(struct section)); |
207 | baton->eh_section_file_address = sect.addr; |
208 | } |
209 | } |
210 | if (strcmp(s1: sect_name, s2: "__text" ) == 0) { |
211 | if (is_64bit) { |
212 | struct section_64 sect; |
213 | memset(§, 0, sizeof(struct section_64)); |
214 | memcpy(§, offset, sizeof(struct section_64)); |
215 | baton->text_section_vmaddr = sect.addr; |
216 | baton->text_section_file_offset = sect.offset; |
217 | } else { |
218 | struct section sect; |
219 | memset(§, 0, sizeof(struct section)); |
220 | memcpy(§, offset, sizeof(struct section)); |
221 | baton->text_section_vmaddr = sect.addr; |
222 | } |
223 | } |
224 | if (is_64bit) { |
225 | offset += sizeof(struct section_64); |
226 | } else { |
227 | offset += sizeof(struct section); |
228 | } |
229 | } |
230 | } |
231 | |
232 | if (strcmp(s1: segment_name, s2: "__LINKEDIT" ) == 0) { |
233 | linkedit_segment_vmaddr = segment_vmaddr; |
234 | linkedit_segment_file_offset = segment_offset; |
235 | } |
236 | } |
237 | |
238 | if (*lc_cmd == LC_SYMTAB) { |
239 | memcpy(&symtab_cmd, offset, sizeof(struct symtab_command)); |
240 | } |
241 | |
242 | if (*lc_cmd == LC_DYSYMTAB) { |
243 | struct dysymtab_command dysymtab_cmd; |
244 | memcpy(&dysymtab_cmd, offset, sizeof(struct dysymtab_command)); |
245 | |
246 | int nlist_size = 12; |
247 | if (is_64bit) |
248 | nlist_size = 16; |
249 | |
250 | char *string_table = |
251 | (char *)(baton->mach_header_start + symtab_cmd.stroff); |
252 | uint8_t *local_syms = baton->mach_header_start + symtab_cmd.symoff + |
253 | (dysymtab_cmd.ilocalsym * nlist_size); |
254 | int local_syms_count = dysymtab_cmd.nlocalsym; |
255 | uint8_t *exported_syms = baton->mach_header_start + symtab_cmd.symoff + |
256 | (dysymtab_cmd.iextdefsym * nlist_size); |
257 | int exported_syms_count = dysymtab_cmd.nextdefsym; |
258 | |
259 | // We're only going to create records for a small number of these symbols |
260 | // but to |
261 | // simplify the memory management I'll allocate enough space to store all |
262 | // of them. |
263 | baton->symbols = (struct symbol *)malloc( |
264 | size: sizeof(struct symbol) * (local_syms_count + exported_syms_count)); |
265 | baton->symbols_count = 0; |
266 | |
267 | for (int i = 0; i < local_syms_count; i++) { |
268 | struct nlist_64 nlist; |
269 | memset(&nlist, 0, sizeof(struct nlist_64)); |
270 | if (is_64bit) { |
271 | memcpy(&nlist, local_syms + (i * nlist_size), |
272 | sizeof(struct nlist_64)); |
273 | } else { |
274 | struct nlist nlist_32; |
275 | memset(&nlist_32, 0, sizeof(struct nlist)); |
276 | memcpy(&nlist_32, local_syms + (i * nlist_size), |
277 | sizeof(struct nlist)); |
278 | nlist.n_un.n_strx = nlist_32.n_un.n_strx; |
279 | nlist.n_type = nlist_32.n_type; |
280 | nlist.n_sect = nlist_32.n_sect; |
281 | nlist.n_desc = nlist_32.n_desc; |
282 | nlist.n_value = nlist_32.n_value; |
283 | } |
284 | if ((nlist.n_type & N_STAB) == 0 && |
285 | ((nlist.n_type & N_EXT) == 1 || |
286 | ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) && |
287 | nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) { |
288 | baton->symbols[baton->symbols_count].file_address = nlist.n_value; |
289 | if (baton->cputype == CPU_TYPE_ARM) |
290 | baton->symbols[baton->symbols_count].file_address = |
291 | baton->symbols[baton->symbols_count].file_address & ~1; |
292 | baton->symbols[baton->symbols_count].name = |
293 | string_table + nlist.n_un.n_strx; |
294 | baton->symbols_count++; |
295 | } |
296 | } |
297 | |
298 | for (int i = 0; i < exported_syms_count; i++) { |
299 | struct nlist_64 nlist; |
300 | memset(&nlist, 0, sizeof(struct nlist_64)); |
301 | if (is_64bit) { |
302 | memcpy(&nlist, exported_syms + (i * nlist_size), |
303 | sizeof(struct nlist_64)); |
304 | } else { |
305 | struct nlist nlist_32; |
306 | memcpy(&nlist_32, exported_syms + (i * nlist_size), |
307 | sizeof(struct nlist)); |
308 | nlist.n_un.n_strx = nlist_32.n_un.n_strx; |
309 | nlist.n_type = nlist_32.n_type; |
310 | nlist.n_sect = nlist_32.n_sect; |
311 | nlist.n_desc = nlist_32.n_desc; |
312 | nlist.n_value = nlist_32.n_value; |
313 | } |
314 | if ((nlist.n_type & N_STAB) == 0 && |
315 | ((nlist.n_type & N_EXT) == 1 || |
316 | ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) && |
317 | nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) { |
318 | baton->symbols[baton->symbols_count].file_address = nlist.n_value; |
319 | if (baton->cputype == CPU_TYPE_ARM) |
320 | baton->symbols[baton->symbols_count].file_address = |
321 | baton->symbols[baton->symbols_count].file_address & ~1; |
322 | baton->symbols[baton->symbols_count].name = |
323 | string_table + nlist.n_un.n_strx; |
324 | baton->symbols_count++; |
325 | } |
326 | } |
327 | |
328 | qsort(base: baton->symbols, nmemb: baton->symbols_count, size: sizeof(struct symbol), |
329 | compar: symbol_compare); |
330 | } |
331 | |
332 | if (*lc_cmd == LC_FUNCTION_STARTS) { |
333 | struct linkedit_data_command function_starts_cmd; |
334 | memcpy(&function_starts_cmd, offset, |
335 | sizeof(struct linkedit_data_command)); |
336 | |
337 | uint8_t *funcstarts_offset = |
338 | baton->mach_header_start + function_starts_cmd.dataoff; |
339 | uint8_t *function_end = funcstarts_offset + function_starts_cmd.datasize; |
340 | int count = 0; |
341 | |
342 | while (funcstarts_offset < function_end) { |
343 | if (read_leb128(offset: &funcstarts_offset) != 0) { |
344 | count++; |
345 | } |
346 | } |
347 | |
348 | baton->function_start_addresses = |
349 | (uint64_t *)malloc(size: sizeof(uint64_t) * count); |
350 | baton->function_start_addresses_count = count; |
351 | |
352 | funcstarts_offset = |
353 | baton->mach_header_start + function_starts_cmd.dataoff; |
354 | uint64_t current_pc = baton->text_segment_vmaddr; |
355 | int i = 0; |
356 | while (funcstarts_offset < function_end) { |
357 | uint64_t func_start = read_leb128(offset: &funcstarts_offset); |
358 | if (func_start != 0) { |
359 | current_pc += func_start; |
360 | baton->function_start_addresses[i++] = current_pc; |
361 | } |
362 | } |
363 | } |
364 | |
365 | offset = start_of_this_load_cmd + *lc_cmdsize; |
366 | cur_cmd++; |
367 | } |
368 | |
369 | // Augment the symbol table with the function starts table -- adding symbol |
370 | // entries |
371 | // for functions that were stripped. |
372 | |
373 | int unnamed_functions_to_add = 0; |
374 | for (int i = 0; i < baton->function_start_addresses_count; i++) { |
375 | struct symbol search_key; |
376 | search_key.file_address = baton->function_start_addresses[i]; |
377 | if (baton->cputype == CPU_TYPE_ARM) |
378 | search_key.file_address = search_key.file_address & ~1; |
379 | struct symbol *sym = |
380 | bsearch(key: &search_key, base: baton->symbols, nmemb: baton->symbols_count, |
381 | size: sizeof(struct symbol), compar: symbol_compare); |
382 | if (sym == NULL) |
383 | unnamed_functions_to_add++; |
384 | } |
385 | |
386 | baton->symbols = (struct symbol *)realloc( |
387 | ptr: baton->symbols, size: sizeof(struct symbol) * |
388 | (baton->symbols_count + unnamed_functions_to_add)); |
389 | |
390 | int current_unnamed_symbol = 1; |
391 | int number_symbols_added = 0; |
392 | for (int i = 0; i < baton->function_start_addresses_count; i++) { |
393 | struct symbol search_key; |
394 | search_key.file_address = baton->function_start_addresses[i]; |
395 | if (baton->cputype == CPU_TYPE_ARM) |
396 | search_key.file_address = search_key.file_address & ~1; |
397 | struct symbol *sym = |
398 | bsearch(key: &search_key, base: baton->symbols, nmemb: baton->symbols_count, |
399 | size: sizeof(struct symbol), compar: symbol_compare); |
400 | if (sym == NULL) { |
401 | char *name; |
402 | asprintf(ptr: &name, fmt: "unnamed function #%d" , current_unnamed_symbol++); |
403 | baton->symbols[baton->symbols_count + number_symbols_added].file_address = |
404 | baton->function_start_addresses[i]; |
405 | baton->symbols[baton->symbols_count + number_symbols_added].name = name; |
406 | number_symbols_added++; |
407 | } |
408 | } |
409 | baton->symbols_count += number_symbols_added; |
410 | qsort(base: baton->symbols, nmemb: baton->symbols_count, size: sizeof(struct symbol), |
411 | compar: symbol_compare); |
412 | |
413 | // printf ("function start addresses\n"); |
414 | // for (int i = 0; i < baton->function_start_addresses_count; i++) |
415 | // { |
416 | // printf ("0x%012llx\n", baton->function_start_addresses[i]); |
417 | // } |
418 | |
419 | // printf ("symbol table names & addresses\n"); |
420 | // for (int i = 0; i < baton->symbols_count; i++) |
421 | // { |
422 | // printf ("0x%012llx %s\n", baton->symbols[i].file_address, |
423 | // baton->symbols[i].name); |
424 | // } |
425 | } |
426 | |
427 | void print_encoding_x86_64(struct baton baton, uint8_t *function_start, |
428 | uint32_t encoding) { |
429 | int mode = encoding & UNWIND_X86_64_MODE_MASK; |
430 | switch (mode) { |
431 | case UNWIND_X86_64_MODE_RBP_FRAME: { |
432 | printf(format: "frame func: CFA is rbp+%d " , 16); |
433 | printf(format: " rip=[CFA-8] rbp=[CFA-16]" ); |
434 | uint32_t saved_registers_offset = |
435 | EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); |
436 | |
437 | uint32_t saved_registers_locations = |
438 | EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); |
439 | |
440 | saved_registers_offset += 2; |
441 | |
442 | for (int i = 0; i < 5; i++) { |
443 | switch (saved_registers_locations & 0x7) { |
444 | case UNWIND_X86_64_REG_NONE: |
445 | break; |
446 | case UNWIND_X86_64_REG_RBX: |
447 | printf(format: " rbx=[CFA-%d]" , saved_registers_offset * 8); |
448 | break; |
449 | case UNWIND_X86_64_REG_R12: |
450 | printf(format: " r12=[CFA-%d]" , saved_registers_offset * 8); |
451 | break; |
452 | case UNWIND_X86_64_REG_R13: |
453 | printf(format: " r13=[CFA-%d]" , saved_registers_offset * 8); |
454 | break; |
455 | case UNWIND_X86_64_REG_R14: |
456 | printf(format: " r14=[CFA-%d]" , saved_registers_offset * 8); |
457 | break; |
458 | case UNWIND_X86_64_REG_R15: |
459 | printf(format: " r15=[CFA-%d]" , saved_registers_offset * 8); |
460 | break; |
461 | } |
462 | saved_registers_offset--; |
463 | saved_registers_locations >>= 3; |
464 | } |
465 | } break; |
466 | |
467 | case UNWIND_X86_64_MODE_STACK_IND: |
468 | case UNWIND_X86_64_MODE_STACK_IMMD: { |
469 | uint32_t stack_size = |
470 | EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); |
471 | uint32_t register_count = |
472 | EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); |
473 | uint32_t permutation = |
474 | EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); |
475 | |
476 | if (mode == UNWIND_X86_64_MODE_STACK_IND && function_start) { |
477 | uint32_t stack_adjust = |
478 | EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); |
479 | |
480 | // offset into the function instructions; 0 == beginning of first |
481 | // instruction |
482 | uint32_t offset_to_subl_insn = |
483 | EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); |
484 | |
485 | stack_size = *((uint32_t *)(function_start + offset_to_subl_insn)); |
486 | |
487 | stack_size += stack_adjust * 8; |
488 | |
489 | printf(format: "large stack " ); |
490 | } |
491 | |
492 | if (mode == UNWIND_X86_64_MODE_STACK_IND) { |
493 | printf(format: "frameless function: stack size %d, register count %d " , |
494 | stack_size * 8, register_count); |
495 | } else { |
496 | printf(format: "frameless function: stack size %d, register count %d " , |
497 | stack_size, register_count); |
498 | } |
499 | |
500 | if (register_count == 0) { |
501 | printf(format: " no registers saved" ); |
502 | } else { |
503 | |
504 | // We need to include (up to) 6 registers in 10 bits. |
505 | // That would be 18 bits if we just used 3 bits per reg to indicate |
506 | // the order they're saved on the stack. |
507 | // |
508 | // This is done with Lehmer code permutation, e.g. see |
509 | // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms |
510 | int permunreg[6]; |
511 | |
512 | // This decodes the variable-base number in the 10 bits |
513 | // and gives us the Lehmer code sequence which can then |
514 | // be decoded. |
515 | |
516 | switch (register_count) { |
517 | case 6: |
518 | permunreg[0] = permutation / 120; // 120 == 5! |
519 | permutation -= (permunreg[0] * 120); |
520 | permunreg[1] = permutation / 24; // 24 == 4! |
521 | permutation -= (permunreg[1] * 24); |
522 | permunreg[2] = permutation / 6; // 6 == 3! |
523 | permutation -= (permunreg[2] * 6); |
524 | permunreg[3] = permutation / 2; // 2 == 2! |
525 | permutation -= (permunreg[3] * 2); |
526 | permunreg[4] = permutation; // 1 == 1! |
527 | permunreg[5] = 0; |
528 | break; |
529 | case 5: |
530 | permunreg[0] = permutation / 120; |
531 | permutation -= (permunreg[0] * 120); |
532 | permunreg[1] = permutation / 24; |
533 | permutation -= (permunreg[1] * 24); |
534 | permunreg[2] = permutation / 6; |
535 | permutation -= (permunreg[2] * 6); |
536 | permunreg[3] = permutation / 2; |
537 | permutation -= (permunreg[3] * 2); |
538 | permunreg[4] = permutation; |
539 | break; |
540 | case 4: |
541 | permunreg[0] = permutation / 60; |
542 | permutation -= (permunreg[0] * 60); |
543 | permunreg[1] = permutation / 12; |
544 | permutation -= (permunreg[1] * 12); |
545 | permunreg[2] = permutation / 3; |
546 | permutation -= (permunreg[2] * 3); |
547 | permunreg[3] = permutation; |
548 | break; |
549 | case 3: |
550 | permunreg[0] = permutation / 20; |
551 | permutation -= (permunreg[0] * 20); |
552 | permunreg[1] = permutation / 4; |
553 | permutation -= (permunreg[1] * 4); |
554 | permunreg[2] = permutation; |
555 | break; |
556 | case 2: |
557 | permunreg[0] = permutation / 5; |
558 | permutation -= (permunreg[0] * 5); |
559 | permunreg[1] = permutation; |
560 | break; |
561 | case 1: |
562 | permunreg[0] = permutation; |
563 | break; |
564 | } |
565 | |
566 | // Decode the Lehmer code for this permutation of |
567 | // the registers v. http://en.wikipedia.org/wiki/Lehmer_code |
568 | |
569 | int registers[6]; |
570 | bool used[7] = {false, false, false, false, false, false, false}; |
571 | for (int i = 0; i < register_count; i++) { |
572 | int renum = 0; |
573 | for (int j = 1; j < 7; j++) { |
574 | if (used[j] == false) { |
575 | if (renum == permunreg[i]) { |
576 | registers[i] = j; |
577 | used[j] = true; |
578 | break; |
579 | } |
580 | renum++; |
581 | } |
582 | } |
583 | } |
584 | |
585 | if (mode == UNWIND_X86_64_MODE_STACK_IND) { |
586 | printf(format: " CFA is rsp+%d " , stack_size); |
587 | } else { |
588 | printf(format: " CFA is rsp+%d " , stack_size * 8); |
589 | } |
590 | |
591 | uint32_t saved_registers_offset = 1; |
592 | printf(format: " rip=[CFA-%d]" , saved_registers_offset * 8); |
593 | saved_registers_offset++; |
594 | |
595 | for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { |
596 | switch (registers[i]) { |
597 | case UNWIND_X86_64_REG_NONE: |
598 | break; |
599 | case UNWIND_X86_64_REG_RBX: |
600 | printf(format: " rbx=[CFA-%d]" , saved_registers_offset * 8); |
601 | saved_registers_offset++; |
602 | break; |
603 | case UNWIND_X86_64_REG_R12: |
604 | printf(format: " r12=[CFA-%d]" , saved_registers_offset * 8); |
605 | saved_registers_offset++; |
606 | break; |
607 | case UNWIND_X86_64_REG_R13: |
608 | printf(format: " r13=[CFA-%d]" , saved_registers_offset * 8); |
609 | saved_registers_offset++; |
610 | break; |
611 | case UNWIND_X86_64_REG_R14: |
612 | printf(format: " r14=[CFA-%d]" , saved_registers_offset * 8); |
613 | saved_registers_offset++; |
614 | break; |
615 | case UNWIND_X86_64_REG_R15: |
616 | printf(format: " r15=[CFA-%d]" , saved_registers_offset * 8); |
617 | saved_registers_offset++; |
618 | break; |
619 | case UNWIND_X86_64_REG_RBP: |
620 | printf(format: " rbp=[CFA-%d]" , saved_registers_offset * 8); |
621 | saved_registers_offset++; |
622 | break; |
623 | } |
624 | } |
625 | } |
626 | |
627 | } break; |
628 | |
629 | case UNWIND_X86_64_MODE_DWARF: { |
630 | uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; |
631 | printf( |
632 | format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 |
633 | ")" , |
634 | dwarf_offset, dwarf_offset + baton.eh_section_file_address); |
635 | } break; |
636 | |
637 | case 0: { |
638 | printf(format: " no unwind information" ); |
639 | } break; |
640 | } |
641 | } |
642 | |
643 | void print_encoding_i386(struct baton baton, uint8_t *function_start, |
644 | uint32_t encoding) { |
645 | int mode = encoding & UNWIND_X86_MODE_MASK; |
646 | switch (mode) { |
647 | case UNWIND_X86_MODE_EBP_FRAME: { |
648 | printf(format: "frame func: CFA is ebp+%d " , 8); |
649 | printf(format: " eip=[CFA-4] ebp=[CFA-8]" ); |
650 | uint32_t saved_registers_offset = |
651 | EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET); |
652 | |
653 | uint32_t saved_registers_locations = |
654 | EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS); |
655 | |
656 | saved_registers_offset += 2; |
657 | |
658 | for (int i = 0; i < 5; i++) { |
659 | switch (saved_registers_locations & 0x7) { |
660 | case UNWIND_X86_REG_NONE: |
661 | break; |
662 | case UNWIND_X86_REG_EBX: |
663 | printf(format: " ebx=[CFA-%d]" , saved_registers_offset * 4); |
664 | break; |
665 | case UNWIND_X86_REG_ECX: |
666 | printf(format: " ecx=[CFA-%d]" , saved_registers_offset * 4); |
667 | break; |
668 | case UNWIND_X86_REG_EDX: |
669 | printf(format: " edx=[CFA-%d]" , saved_registers_offset * 4); |
670 | break; |
671 | case UNWIND_X86_REG_EDI: |
672 | printf(format: " edi=[CFA-%d]" , saved_registers_offset * 4); |
673 | break; |
674 | case UNWIND_X86_REG_ESI: |
675 | printf(format: " esi=[CFA-%d]" , saved_registers_offset * 4); |
676 | break; |
677 | } |
678 | saved_registers_offset--; |
679 | saved_registers_locations >>= 3; |
680 | } |
681 | } break; |
682 | |
683 | case UNWIND_X86_MODE_STACK_IND: |
684 | case UNWIND_X86_MODE_STACK_IMMD: { |
685 | uint32_t stack_size = |
686 | EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); |
687 | uint32_t register_count = |
688 | EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); |
689 | uint32_t permutation = |
690 | EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); |
691 | |
692 | if (mode == UNWIND_X86_MODE_STACK_IND && function_start) { |
693 | uint32_t stack_adjust = |
694 | EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); |
695 | |
696 | // offset into the function instructions; 0 == beginning of first |
697 | // instruction |
698 | uint32_t offset_to_subl_insn = |
699 | EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); |
700 | |
701 | stack_size = *((uint32_t *)(function_start + offset_to_subl_insn)); |
702 | |
703 | stack_size += stack_adjust * 4; |
704 | |
705 | printf(format: "large stack " ); |
706 | } |
707 | |
708 | if (mode == UNWIND_X86_MODE_STACK_IND) { |
709 | printf(format: "frameless function: stack size %d, register count %d " , |
710 | stack_size, register_count); |
711 | } else { |
712 | printf(format: "frameless function: stack size %d, register count %d " , |
713 | stack_size * 4, register_count); |
714 | } |
715 | |
716 | if (register_count == 0) { |
717 | printf(format: " no registers saved" ); |
718 | } else { |
719 | |
720 | // We need to include (up to) 6 registers in 10 bits. |
721 | // That would be 18 bits if we just used 3 bits per reg to indicate |
722 | // the order they're saved on the stack. |
723 | // |
724 | // This is done with Lehmer code permutation, e.g. see |
725 | // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms |
726 | int permunreg[6]; |
727 | |
728 | // This decodes the variable-base number in the 10 bits |
729 | // and gives us the Lehmer code sequence which can then |
730 | // be decoded. |
731 | |
732 | switch (register_count) { |
733 | case 6: |
734 | permunreg[0] = permutation / 120; // 120 == 5! |
735 | permutation -= (permunreg[0] * 120); |
736 | permunreg[1] = permutation / 24; // 24 == 4! |
737 | permutation -= (permunreg[1] * 24); |
738 | permunreg[2] = permutation / 6; // 6 == 3! |
739 | permutation -= (permunreg[2] * 6); |
740 | permunreg[3] = permutation / 2; // 2 == 2! |
741 | permutation -= (permunreg[3] * 2); |
742 | permunreg[4] = permutation; // 1 == 1! |
743 | permunreg[5] = 0; |
744 | break; |
745 | case 5: |
746 | permunreg[0] = permutation / 120; |
747 | permutation -= (permunreg[0] * 120); |
748 | permunreg[1] = permutation / 24; |
749 | permutation -= (permunreg[1] * 24); |
750 | permunreg[2] = permutation / 6; |
751 | permutation -= (permunreg[2] * 6); |
752 | permunreg[3] = permutation / 2; |
753 | permutation -= (permunreg[3] * 2); |
754 | permunreg[4] = permutation; |
755 | break; |
756 | case 4: |
757 | permunreg[0] = permutation / 60; |
758 | permutation -= (permunreg[0] * 60); |
759 | permunreg[1] = permutation / 12; |
760 | permutation -= (permunreg[1] * 12); |
761 | permunreg[2] = permutation / 3; |
762 | permutation -= (permunreg[2] * 3); |
763 | permunreg[3] = permutation; |
764 | break; |
765 | case 3: |
766 | permunreg[0] = permutation / 20; |
767 | permutation -= (permunreg[0] * 20); |
768 | permunreg[1] = permutation / 4; |
769 | permutation -= (permunreg[1] * 4); |
770 | permunreg[2] = permutation; |
771 | break; |
772 | case 2: |
773 | permunreg[0] = permutation / 5; |
774 | permutation -= (permunreg[0] * 5); |
775 | permunreg[1] = permutation; |
776 | break; |
777 | case 1: |
778 | permunreg[0] = permutation; |
779 | break; |
780 | } |
781 | |
782 | // Decode the Lehmer code for this permutation of |
783 | // the registers v. http://en.wikipedia.org/wiki/Lehmer_code |
784 | |
785 | int registers[6]; |
786 | bool used[7] = {false, false, false, false, false, false, false}; |
787 | for (int i = 0; i < register_count; i++) { |
788 | int renum = 0; |
789 | for (int j = 1; j < 7; j++) { |
790 | if (used[j] == false) { |
791 | if (renum == permunreg[i]) { |
792 | registers[i] = j; |
793 | used[j] = true; |
794 | break; |
795 | } |
796 | renum++; |
797 | } |
798 | } |
799 | } |
800 | |
801 | if (mode == UNWIND_X86_MODE_STACK_IND) { |
802 | printf(format: " CFA is esp+%d " , stack_size); |
803 | } else { |
804 | printf(format: " CFA is esp+%d " , stack_size * 4); |
805 | } |
806 | |
807 | uint32_t saved_registers_offset = 1; |
808 | printf(format: " eip=[CFA-%d]" , saved_registers_offset * 4); |
809 | saved_registers_offset++; |
810 | |
811 | for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { |
812 | switch (registers[i]) { |
813 | case UNWIND_X86_REG_NONE: |
814 | break; |
815 | case UNWIND_X86_REG_EBX: |
816 | printf(format: " ebx=[CFA-%d]" , saved_registers_offset * 4); |
817 | saved_registers_offset++; |
818 | break; |
819 | case UNWIND_X86_REG_ECX: |
820 | printf(format: " ecx=[CFA-%d]" , saved_registers_offset * 4); |
821 | saved_registers_offset++; |
822 | break; |
823 | case UNWIND_X86_REG_EDX: |
824 | printf(format: " edx=[CFA-%d]" , saved_registers_offset * 4); |
825 | saved_registers_offset++; |
826 | break; |
827 | case UNWIND_X86_REG_EDI: |
828 | printf(format: " edi=[CFA-%d]" , saved_registers_offset * 4); |
829 | saved_registers_offset++; |
830 | break; |
831 | case UNWIND_X86_REG_ESI: |
832 | printf(format: " esi=[CFA-%d]" , saved_registers_offset * 4); |
833 | saved_registers_offset++; |
834 | break; |
835 | case UNWIND_X86_REG_EBP: |
836 | printf(format: " ebp=[CFA-%d]" , saved_registers_offset * 4); |
837 | saved_registers_offset++; |
838 | break; |
839 | } |
840 | } |
841 | } |
842 | |
843 | } break; |
844 | |
845 | case UNWIND_X86_MODE_DWARF: { |
846 | uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; |
847 | printf( |
848 | format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 |
849 | ")" , |
850 | dwarf_offset, dwarf_offset + baton.eh_section_file_address); |
851 | } break; |
852 | |
853 | case 0: { |
854 | printf(format: " no unwind information" ); |
855 | } break; |
856 | } |
857 | } |
858 | |
859 | void print_encoding_arm64(struct baton baton, uint8_t *function_start, |
860 | uint32_t encoding) { |
861 | const int wordsize = 8; |
862 | int mode = encoding & UNWIND_ARM64_MODE_MASK; |
863 | switch (mode) { |
864 | case UNWIND_ARM64_MODE_FRAME: { |
865 | printf(format: "frame func: CFA is fp+%d " , 16); |
866 | printf(format: " pc=[CFA-8] fp=[CFA-16]" ); |
867 | int reg_pairs_saved_count = 1; |
868 | uint32_t saved_register_bits = encoding & 0xfff; |
869 | if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) { |
870 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
871 | cfa_offset -= wordsize; |
872 | printf(format: " x19=[CFA%d]" , cfa_offset); |
873 | cfa_offset -= wordsize; |
874 | printf(format: " x20=[CFA%d]" , cfa_offset); |
875 | reg_pairs_saved_count++; |
876 | } |
877 | if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) { |
878 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
879 | cfa_offset -= wordsize; |
880 | printf(format: " x21=[CFA%d]" , cfa_offset); |
881 | cfa_offset -= wordsize; |
882 | printf(format: " x22=[CFA%d]" , cfa_offset); |
883 | reg_pairs_saved_count++; |
884 | } |
885 | if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) { |
886 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
887 | cfa_offset -= wordsize; |
888 | printf(format: " x23=[CFA%d]" , cfa_offset); |
889 | cfa_offset -= wordsize; |
890 | printf(format: " x24=[CFA%d]" , cfa_offset); |
891 | reg_pairs_saved_count++; |
892 | } |
893 | if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) { |
894 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
895 | cfa_offset -= wordsize; |
896 | printf(format: " x25=[CFA%d]" , cfa_offset); |
897 | cfa_offset -= wordsize; |
898 | printf(format: " x26=[CFA%d]" , cfa_offset); |
899 | reg_pairs_saved_count++; |
900 | } |
901 | if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) { |
902 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
903 | cfa_offset -= wordsize; |
904 | printf(format: " x27=[CFA%d]" , cfa_offset); |
905 | cfa_offset -= wordsize; |
906 | printf(format: " x28=[CFA%d]" , cfa_offset); |
907 | reg_pairs_saved_count++; |
908 | } |
909 | if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) { |
910 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
911 | cfa_offset -= wordsize; |
912 | printf(format: " d8=[CFA%d]" , cfa_offset); |
913 | cfa_offset -= wordsize; |
914 | printf(format: " d9=[CFA%d]" , cfa_offset); |
915 | reg_pairs_saved_count++; |
916 | } |
917 | if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) { |
918 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
919 | cfa_offset -= wordsize; |
920 | printf(format: " d10=[CFA%d]" , cfa_offset); |
921 | cfa_offset -= wordsize; |
922 | printf(format: " d11=[CFA%d]" , cfa_offset); |
923 | reg_pairs_saved_count++; |
924 | } |
925 | if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) { |
926 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
927 | cfa_offset -= wordsize; |
928 | printf(format: " d12=[CFA%d]" , cfa_offset); |
929 | cfa_offset -= wordsize; |
930 | printf(format: " d13=[CFA%d]" , cfa_offset); |
931 | reg_pairs_saved_count++; |
932 | } |
933 | if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) { |
934 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
935 | cfa_offset -= wordsize; |
936 | printf(format: " d14=[CFA%d]" , cfa_offset); |
937 | cfa_offset -= wordsize; |
938 | printf(format: " d15=[CFA%d]" , cfa_offset); |
939 | reg_pairs_saved_count++; |
940 | } |
941 | |
942 | } break; |
943 | |
944 | case UNWIND_ARM64_MODE_FRAMELESS: { |
945 | uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK; |
946 | printf(format: "frameless function: stack size %d " , stack_size * 16); |
947 | |
948 | } break; |
949 | |
950 | case UNWIND_ARM64_MODE_DWARF: { |
951 | uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET; |
952 | printf( |
953 | format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 |
954 | ")" , |
955 | dwarf_offset, dwarf_offset + baton.eh_section_file_address); |
956 | } break; |
957 | |
958 | case 0: { |
959 | printf(format: " no unwind information" ); |
960 | } break; |
961 | } |
962 | } |
963 | |
964 | void print_encoding_armv7(struct baton baton, uint8_t *function_start, |
965 | uint32_t encoding) { |
966 | const int wordsize = 4; |
967 | int mode = encoding & UNWIND_ARM_MODE_MASK; |
968 | switch (mode) { |
969 | case UNWIND_ARM_MODE_FRAME_D: |
970 | case UNWIND_ARM_MODE_FRAME: { |
971 | int stack_adjust = |
972 | EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize; |
973 | |
974 | printf(format: "frame func: CFA is fp+%d " , (2 * wordsize) + stack_adjust); |
975 | int cfa_offset = -stack_adjust; |
976 | |
977 | cfa_offset -= wordsize; |
978 | printf(format: " pc=[CFA%d]" , cfa_offset); |
979 | cfa_offset -= wordsize; |
980 | printf(format: " fp=[CFA%d]" , cfa_offset); |
981 | |
982 | uint32_t saved_register_bits = encoding & 0xff; |
983 | if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) { |
984 | cfa_offset -= wordsize; |
985 | printf(format: " r6=[CFA%d]" , cfa_offset); |
986 | } |
987 | if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) { |
988 | cfa_offset -= wordsize; |
989 | printf(format: " r5=[CFA%d]" , cfa_offset); |
990 | } |
991 | if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) { |
992 | cfa_offset -= wordsize; |
993 | printf(format: " r4=[CFA%d]" , cfa_offset); |
994 | } |
995 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) { |
996 | cfa_offset -= wordsize; |
997 | printf(format: " r12=[CFA%d]" , cfa_offset); |
998 | } |
999 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) { |
1000 | cfa_offset -= wordsize; |
1001 | printf(format: " r11=[CFA%d]" , cfa_offset); |
1002 | } |
1003 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) { |
1004 | cfa_offset -= wordsize; |
1005 | printf(format: " r10=[CFA%d]" , cfa_offset); |
1006 | } |
1007 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) { |
1008 | cfa_offset -= wordsize; |
1009 | printf(format: " r9=[CFA%d]" , cfa_offset); |
1010 | } |
1011 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) { |
1012 | cfa_offset -= wordsize; |
1013 | printf(format: " r8=[CFA%d]" , cfa_offset); |
1014 | } |
1015 | |
1016 | if (mode == UNWIND_ARM_MODE_FRAME_D) { |
1017 | uint32_t d_reg_bits = |
1018 | EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK); |
1019 | switch (d_reg_bits) { |
1020 | case 0: |
1021 | // vpush {d8} |
1022 | cfa_offset -= 8; |
1023 | printf(format: " d8=[CFA%d]" , cfa_offset); |
1024 | break; |
1025 | case 1: |
1026 | // vpush {d10} |
1027 | // vpush {d8} |
1028 | cfa_offset -= 8; |
1029 | printf(format: " d10=[CFA%d]" , cfa_offset); |
1030 | cfa_offset -= 8; |
1031 | printf(format: " d8=[CFA%d]" , cfa_offset); |
1032 | break; |
1033 | case 2: |
1034 | // vpush {d12} |
1035 | // vpush {d10} |
1036 | // vpush {d8} |
1037 | cfa_offset -= 8; |
1038 | printf(format: " d12=[CFA%d]" , cfa_offset); |
1039 | cfa_offset -= 8; |
1040 | printf(format: " d10=[CFA%d]" , cfa_offset); |
1041 | cfa_offset -= 8; |
1042 | printf(format: " d8=[CFA%d]" , cfa_offset); |
1043 | break; |
1044 | case 3: |
1045 | // vpush {d14} |
1046 | // vpush {d12} |
1047 | // vpush {d10} |
1048 | // vpush {d8} |
1049 | cfa_offset -= 8; |
1050 | printf(format: " d14=[CFA%d]" , cfa_offset); |
1051 | cfa_offset -= 8; |
1052 | printf(format: " d12=[CFA%d]" , cfa_offset); |
1053 | cfa_offset -= 8; |
1054 | printf(format: " d10=[CFA%d]" , cfa_offset); |
1055 | cfa_offset -= 8; |
1056 | printf(format: " d8=[CFA%d]" , cfa_offset); |
1057 | break; |
1058 | case 4: |
1059 | // vpush {d14} |
1060 | // vpush {d12} |
1061 | // sp = (sp - 24) & (-16); |
1062 | // vst {d8, d9, d10} |
1063 | printf(format: " d14, d12, d10, d9, d8" ); |
1064 | break; |
1065 | case 5: |
1066 | // vpush {d14} |
1067 | // sp = (sp - 40) & (-16); |
1068 | // vst {d8, d9, d10, d11} |
1069 | // vst {d12} |
1070 | printf(format: " d14, d11, d10, d9, d8, d12" ); |
1071 | break; |
1072 | case 6: |
1073 | // sp = (sp - 56) & (-16); |
1074 | // vst {d8, d9, d10, d11} |
1075 | // vst {d12, d13, d14} |
1076 | printf(format: " d11, d10, d9, d8, d14, d13, d12" ); |
1077 | break; |
1078 | case 7: |
1079 | // sp = (sp - 64) & (-16); |
1080 | // vst {d8, d9, d10, d11} |
1081 | // vst {d12, d13, d14, d15} |
1082 | printf(format: " d11, d10, d9, d8, d15, d14, d13, d12" ); |
1083 | break; |
1084 | } |
1085 | } |
1086 | } break; |
1087 | |
1088 | case UNWIND_ARM_MODE_DWARF: { |
1089 | uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET; |
1090 | printf( |
1091 | format: "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 |
1092 | ")" , |
1093 | dwarf_offset, dwarf_offset + baton.eh_section_file_address); |
1094 | } break; |
1095 | |
1096 | case 0: { |
1097 | printf(format: " no unwind information" ); |
1098 | } break; |
1099 | } |
1100 | } |
1101 | |
1102 | void print_encoding(struct baton baton, uint8_t *function_start, |
1103 | uint32_t encoding) { |
1104 | |
1105 | if (baton.cputype == CPU_TYPE_X86_64) { |
1106 | print_encoding_x86_64(baton, function_start, encoding); |
1107 | } else if (baton.cputype == CPU_TYPE_I386) { |
1108 | print_encoding_i386(baton, function_start, encoding); |
1109 | } else if (baton.cputype == CPU_TYPE_ARM64 || baton.cputype == CPU_TYPE_ARM64_32) { |
1110 | print_encoding_arm64(baton, function_start, encoding); |
1111 | } else if (baton.cputype == CPU_TYPE_ARM) { |
1112 | print_encoding_armv7(baton, function_start, encoding); |
1113 | } else { |
1114 | printf(format: " -- unsupported encoding arch -- " ); |
1115 | } |
1116 | } |
1117 | |
1118 | void print_function_encoding(struct baton baton, uint32_t idx, |
1119 | uint32_t encoding, uint32_t entry_encoding_index, |
1120 | uint32_t entry_func_offset) { |
1121 | |
1122 | char *entry_encoding_index_str = "" ; |
1123 | if (entry_encoding_index != (uint32_t)-1) { |
1124 | asprintf(ptr: &entry_encoding_index_str, fmt: ", encoding #%d" , entry_encoding_index); |
1125 | } else { |
1126 | asprintf(ptr: &entry_encoding_index_str, fmt: "" ); |
1127 | } |
1128 | |
1129 | uint64_t file_address = baton.first_level_index_entry.functionOffset + |
1130 | entry_func_offset + baton.text_segment_vmaddr; |
1131 | |
1132 | if (baton.cputype == CPU_TYPE_ARM) |
1133 | file_address = file_address & ~1; |
1134 | |
1135 | printf( |
1136 | format: " func [%d] offset %d (file addr 0x%" PRIx64 ")%s, encoding is 0x%x" , |
1137 | idx, entry_func_offset, file_address, entry_encoding_index_str, encoding); |
1138 | |
1139 | struct symbol *symbol = NULL; |
1140 | for (int i = 0; i < baton.symbols_count; i++) { |
1141 | if (i == baton.symbols_count - 1 && |
1142 | baton.symbols[i].file_address <= file_address) { |
1143 | symbol = &(baton.symbols[i]); |
1144 | break; |
1145 | } else { |
1146 | if (baton.symbols[i].file_address <= file_address && |
1147 | baton.symbols[i + 1].file_address > file_address) { |
1148 | symbol = &(baton.symbols[i]); |
1149 | break; |
1150 | } |
1151 | } |
1152 | } |
1153 | |
1154 | printf(format: "\n " ); |
1155 | if (symbol) { |
1156 | int offset = file_address - symbol->file_address; |
1157 | |
1158 | // FIXME this is a poor heuristic - if we're greater than 16 bytes past the |
1159 | // start of the function, this is the unwind info for a stripped function. |
1160 | // In reality the compact unwind entry may not line up exactly with the |
1161 | // function bounds. |
1162 | if (offset >= 0) { |
1163 | printf(format: "name: %s" , symbol->name); |
1164 | if (offset > 0) { |
1165 | printf(format: " + %d" , offset); |
1166 | } |
1167 | } |
1168 | printf(format: "\n " ); |
1169 | } |
1170 | |
1171 | print_encoding(baton, function_start: baton.mach_header_start + |
1172 | baton.first_level_index_entry.functionOffset + |
1173 | baton.text_section_file_offset + entry_func_offset, |
1174 | encoding); |
1175 | |
1176 | bool has_lsda = encoding & UNWIND_HAS_LSDA; |
1177 | |
1178 | if (has_lsda) { |
1179 | uint32_t func_offset = |
1180 | entry_func_offset + baton.first_level_index_entry.functionOffset; |
1181 | |
1182 | int lsda_entry_number = -1; |
1183 | |
1184 | uint32_t low = 0; |
1185 | uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) / |
1186 | sizeof(struct unwind_info_section_header_lsda_index_entry); |
1187 | |
1188 | while (low < high) { |
1189 | uint32_t mid = (low + high) / 2; |
1190 | |
1191 | uint8_t *mid_lsda_entry_addr = |
1192 | (baton.lsda_array_start + |
1193 | (mid * sizeof(struct unwind_info_section_header_lsda_index_entry))); |
1194 | struct unwind_info_section_header_lsda_index_entry mid_lsda_entry; |
1195 | memcpy(&mid_lsda_entry, mid_lsda_entry_addr, |
1196 | sizeof(struct unwind_info_section_header_lsda_index_entry)); |
1197 | if (mid_lsda_entry.functionOffset == func_offset) { |
1198 | lsda_entry_number = |
1199 | (mid_lsda_entry_addr - baton.lsda_array_start) / |
1200 | sizeof(struct unwind_info_section_header_lsda_index_entry); |
1201 | break; |
1202 | } else if (mid_lsda_entry.functionOffset < func_offset) { |
1203 | low = mid + 1; |
1204 | } else { |
1205 | high = mid; |
1206 | } |
1207 | } |
1208 | |
1209 | if (lsda_entry_number != -1) { |
1210 | printf(format: ", LSDA entry #%d" , lsda_entry_number); |
1211 | } else { |
1212 | printf(format: ", LSDA entry not found" ); |
1213 | } |
1214 | } |
1215 | |
1216 | uint32_t pers_idx = EXTRACT_BITS(encoding, UNWIND_PERSONALITY_MASK); |
1217 | if (pers_idx != 0) { |
1218 | pers_idx--; // Change 1-based to 0-based index |
1219 | printf(format: ", personality entry #%d" , pers_idx); |
1220 | } |
1221 | |
1222 | printf(format: "\n" ); |
1223 | } |
1224 | |
1225 | void print_second_level_index_regular(struct baton baton) { |
1226 | uint8_t *page_entries = |
1227 | baton.compact_unwind_start + |
1228 | baton.first_level_index_entry.secondLevelPagesSectionOffset + |
1229 | baton.regular_second_level_page_header.entryPageOffset; |
1230 | uint32_t entries_count = baton.regular_second_level_page_header.entryCount; |
1231 | |
1232 | uint8_t *offset = page_entries; |
1233 | |
1234 | uint32_t idx = 0; |
1235 | while (idx < entries_count) { |
1236 | uint32_t func_offset = *((uint32_t *)(offset)); |
1237 | uint32_t encoding = *((uint32_t *)(offset + 4)); |
1238 | |
1239 | // UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the |
1240 | // functionOffset from the containing index table already. |
1241 | // UNWIND_SECOND_LEVEL_COMPRESSED |
1242 | // entries only have the offset from the containing index table |
1243 | // functionOffset. |
1244 | // So strip off the containing index table functionOffset value here so they |
1245 | // can |
1246 | // be treated the same at the lower layers. |
1247 | |
1248 | print_function_encoding(baton, idx, encoding, entry_encoding_index: (uint32_t)-1, |
1249 | entry_func_offset: func_offset - |
1250 | baton.first_level_index_entry.functionOffset); |
1251 | idx++; |
1252 | offset += 8; |
1253 | } |
1254 | } |
1255 | |
1256 | void print_second_level_index_compressed(struct baton baton) { |
1257 | uint8_t *this_index = |
1258 | baton.compact_unwind_start + |
1259 | baton.first_level_index_entry.secondLevelPagesSectionOffset; |
1260 | uint8_t *start_of_entries = |
1261 | this_index + baton.compressed_second_level_page_header.entryPageOffset; |
1262 | uint8_t *offset = start_of_entries; |
1263 | for (uint16_t idx = 0; |
1264 | idx < baton.compressed_second_level_page_header.entryCount; idx++) { |
1265 | uint32_t entry = *((uint32_t *)offset); |
1266 | offset += 4; |
1267 | uint32_t encoding; |
1268 | |
1269 | uint32_t entry_encoding_index = |
1270 | UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry); |
1271 | uint32_t entry_func_offset = |
1272 | UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry); |
1273 | |
1274 | if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) { |
1275 | // encoding is in common table in section header |
1276 | encoding = |
1277 | *((uint32_t *)(baton.compact_unwind_start + |
1278 | baton.unwind_header.commonEncodingsArraySectionOffset + |
1279 | (entry_encoding_index * sizeof(uint32_t)))); |
1280 | } else { |
1281 | // encoding is in page specific table |
1282 | uint32_t page_encoding_index = |
1283 | entry_encoding_index - baton.unwind_header.commonEncodingsArrayCount; |
1284 | encoding = *((uint32_t *)(this_index + |
1285 | baton.compressed_second_level_page_header |
1286 | .encodingsPageOffset + |
1287 | (page_encoding_index * sizeof(uint32_t)))); |
1288 | } |
1289 | |
1290 | print_function_encoding(baton, idx, encoding, entry_encoding_index, |
1291 | entry_func_offset); |
1292 | } |
1293 | } |
1294 | |
1295 | void print_second_level_index(struct baton baton) { |
1296 | uint8_t *index_start = |
1297 | baton.compact_unwind_start + |
1298 | baton.first_level_index_entry.secondLevelPagesSectionOffset; |
1299 | |
1300 | if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_REGULAR) { |
1301 | struct unwind_info_regular_second_level_page_header ; |
1302 | memcpy(&header, index_start, |
1303 | sizeof(struct unwind_info_regular_second_level_page_header)); |
1304 | printf( |
1305 | format: " UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n" , |
1306 | baton.current_index_table_number, header.entryPageOffset, |
1307 | header.entryCount); |
1308 | baton.regular_second_level_page_header = header; |
1309 | print_second_level_index_regular(baton); |
1310 | } |
1311 | |
1312 | if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_COMPRESSED) { |
1313 | struct unwind_info_compressed_second_level_page_header ; |
1314 | memcpy(&header, index_start, |
1315 | sizeof(struct unwind_info_compressed_second_level_page_header)); |
1316 | printf(format: " UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, " |
1317 | "entryCount %d, encodingsPageOffset %d, encodingsCount %d\n" , |
1318 | baton.current_index_table_number, header.entryPageOffset, |
1319 | header.entryCount, header.encodingsPageOffset, |
1320 | header.encodingsCount); |
1321 | baton.compressed_second_level_page_header = header; |
1322 | print_second_level_index_compressed(baton); |
1323 | } |
1324 | } |
1325 | |
1326 | void print_index_sections(struct baton baton) { |
1327 | uint8_t *index_section_offset = |
1328 | baton.compact_unwind_start + baton.unwind_header.indexSectionOffset; |
1329 | uint32_t index_count = baton.unwind_header.indexCount; |
1330 | |
1331 | uint32_t cur_idx = 0; |
1332 | |
1333 | uint8_t *offset = index_section_offset; |
1334 | while (cur_idx < index_count) { |
1335 | baton.current_index_table_number = cur_idx; |
1336 | struct unwind_info_section_header_index_entry index_entry; |
1337 | memcpy(&index_entry, offset, |
1338 | sizeof(struct unwind_info_section_header_index_entry)); |
1339 | printf(format: "index section #%d: functionOffset %d, " |
1340 | "secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n" , |
1341 | cur_idx, index_entry.functionOffset, |
1342 | index_entry.secondLevelPagesSectionOffset, |
1343 | index_entry.lsdaIndexArraySectionOffset); |
1344 | |
1345 | // secondLevelPagesSectionOffset == 0 means this is a sentinel entry |
1346 | if (index_entry.secondLevelPagesSectionOffset != 0) { |
1347 | struct unwind_info_section_header_index_entry next_index_entry; |
1348 | memcpy(&next_index_entry, |
1349 | offset + sizeof(struct unwind_info_section_header_index_entry), |
1350 | sizeof(struct unwind_info_section_header_index_entry)); |
1351 | |
1352 | baton.lsda_array_start = |
1353 | baton.compact_unwind_start + index_entry.lsdaIndexArraySectionOffset; |
1354 | baton.lsda_array_end = baton.compact_unwind_start + |
1355 | next_index_entry.lsdaIndexArraySectionOffset; |
1356 | |
1357 | uint8_t *lsda_entry_offset = baton.lsda_array_start; |
1358 | uint32_t lsda_count = 0; |
1359 | while (lsda_entry_offset < baton.lsda_array_end) { |
1360 | struct lsda_entry; |
1361 | memcpy(&lsda_entry, lsda_entry_offset, |
1362 | sizeof(struct unwind_info_section_header_lsda_index_entry)); |
1363 | uint64_t function_file_address = |
1364 | baton.first_level_index_entry.functionOffset + |
1365 | lsda_entry.functionOffset + baton.text_segment_vmaddr; |
1366 | uint64_t lsda_file_address = |
1367 | lsda_entry.lsdaOffset + baton.text_segment_vmaddr; |
1368 | printf(format: " LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx64 |
1369 | "), lsdaOffset %d (file address 0x%" PRIx64 ")\n" , |
1370 | lsda_count, lsda_entry.functionOffset, |
1371 | lsda_entry.functionOffset - index_entry.functionOffset, |
1372 | function_file_address, lsda_entry.lsdaOffset, lsda_file_address); |
1373 | lsda_count++; |
1374 | lsda_entry_offset += |
1375 | sizeof(struct unwind_info_section_header_lsda_index_entry); |
1376 | } |
1377 | |
1378 | printf(format: "\n" ); |
1379 | |
1380 | baton.first_level_index_entry = index_entry; |
1381 | print_second_level_index(baton); |
1382 | } |
1383 | |
1384 | printf(format: "\n" ); |
1385 | |
1386 | cur_idx++; |
1387 | offset += sizeof(struct unwind_info_section_header_index_entry); |
1388 | } |
1389 | } |
1390 | |
1391 | int main(int argc, char **argv) { |
1392 | struct stat st; |
1393 | char *file = argv[0]; |
1394 | if (argc > 1) |
1395 | file = argv[1]; |
1396 | int fd = open(file: file, O_RDONLY); |
1397 | if (fd == -1) { |
1398 | printf(format: "Failed to open '%s'\n" , file); |
1399 | exit(status: 1); |
1400 | } |
1401 | fstat(fd: fd, buf: &st); |
1402 | uint8_t *file_mem = |
1403 | (uint8_t *)mmap(addr: 0, len: st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd: fd, offset: 0); |
1404 | if (file_mem == MAP_FAILED) { |
1405 | printf(format: "Failed to mmap() '%s'\n" , file); |
1406 | } |
1407 | |
1408 | FILE *f = fopen(filename: "a.out" , modes: "r" ); |
1409 | |
1410 | struct baton baton; |
1411 | baton.mach_header_start = file_mem; |
1412 | baton.symbols = NULL; |
1413 | baton.symbols_count = 0; |
1414 | baton.function_start_addresses = NULL; |
1415 | baton.function_start_addresses_count = 0; |
1416 | |
1417 | scan_macho_load_commands(baton: &baton); |
1418 | |
1419 | if (baton.compact_unwind_start == NULL) { |
1420 | printf(format: "could not find __TEXT,__unwind_info section\n" ); |
1421 | exit(status: 1); |
1422 | } |
1423 | |
1424 | struct unwind_info_section_header ; |
1425 | memcpy(&header, baton.compact_unwind_start, |
1426 | sizeof(struct unwind_info_section_header)); |
1427 | printf(format: "Header:\n" ); |
1428 | printf(format: " version %u\n" , header.version); |
1429 | printf(format: " commonEncodingsArraySectionOffset is %d\n" , |
1430 | header.commonEncodingsArraySectionOffset); |
1431 | printf(format: " commonEncodingsArrayCount is %d\n" , |
1432 | header.commonEncodingsArrayCount); |
1433 | printf(format: " personalityArraySectionOffset is %d\n" , |
1434 | header.personalityArraySectionOffset); |
1435 | printf(format: " personalityArrayCount is %d\n" , header.personalityArrayCount); |
1436 | printf(format: " indexSectionOffset is %d\n" , header.indexSectionOffset); |
1437 | printf(format: " indexCount is %d\n" , header.indexCount); |
1438 | |
1439 | uint8_t *common_encodings = |
1440 | baton.compact_unwind_start + header.commonEncodingsArraySectionOffset; |
1441 | uint32_t encoding_idx = 0; |
1442 | while (encoding_idx < header.commonEncodingsArrayCount) { |
1443 | uint32_t encoding = *((uint32_t *)common_encodings); |
1444 | printf(format: " Common Encoding [%d]: 0x%x " , encoding_idx, encoding); |
1445 | print_encoding(baton, NULL, encoding); |
1446 | printf(format: "\n" ); |
1447 | common_encodings += sizeof(uint32_t); |
1448 | encoding_idx++; |
1449 | } |
1450 | |
1451 | uint8_t *pers_arr = |
1452 | baton.compact_unwind_start + header.personalityArraySectionOffset; |
1453 | uint32_t pers_idx = 0; |
1454 | while (pers_idx < header.personalityArrayCount) { |
1455 | int32_t pers_delta = *((int32_t *)(baton.compact_unwind_start + |
1456 | header.personalityArraySectionOffset + |
1457 | (pers_idx * sizeof(uint32_t)))); |
1458 | printf(format: " Personality [%d]: personality function ptr @ offset %d (file " |
1459 | "address 0x%" PRIx64 ")\n" , |
1460 | pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta); |
1461 | pers_idx++; |
1462 | pers_arr += sizeof(uint32_t); |
1463 | } |
1464 | |
1465 | printf(format: "\n" ); |
1466 | |
1467 | baton.unwind_header = header; |
1468 | |
1469 | print_index_sections(baton); |
1470 | |
1471 | return 0; |
1472 | } |
1473 | |