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