v / thirdparty / libbacktrace
Raw file | 1298 loc (1082 sloc) | 34.97 KB | Latest commit hash 76a735450
1// macho.c:
2#include <dirent.h>
3#include <stdlib.h>
4#include <string.h>
5#include <sys/types.h>
6
7#ifdef HAVE_MACH_O_DYLD_H
8#include <mach-o/dyld.h>
9#endif
10
11struct macho_header_32 {
12 uint32_t magic;
13 uint32_t cputype;
14 uint32_t cpusubtype;
15 uint32_t filetype;
16 uint32_t ncmds;
17 uint32_t sizeofcmds;
18 uint32_t flags;
19};
20
21struct macho_header_64 {
22 uint32_t magic;
23 uint32_t cputype;
24 uint32_t cpusubtype;
25 uint32_t filetype;
26 uint32_t ncmds;
27 uint32_t sizeofcmds;
28 uint32_t flags;
29 uint32_t reserved;
30};
31
32struct macho_header_fat {
33 uint32_t magic;
34 uint32_t nfat_arch;
35};
36
37#define MACH_O_MH_MAGIC_32 0xfeedface
38#define MACH_O_MH_MAGIC_64 0xfeedfacf
39#define MACH_O_MH_MAGIC_FAT 0xcafebabe
40#define MACH_O_MH_CIGAM_FAT 0xbebafeca
41#define MACH_O_MH_MAGIC_FAT_64 0xcafebabf
42#define MACH_O_MH_CIGAM_FAT_64 0xbfbafeca
43
44#define MACH_O_MH_EXECUTE 0x02
45#define MACH_O_MH_DYLIB 0x06
46#define MACH_O_MH_DSYM 0x0a
47
48struct macho_fat_arch {
49 uint32_t cputype;
50 uint32_t cpusubtype;
51 uint32_t offset;
52 uint32_t size;
53 uint32_t align;
54};
55
56struct macho_fat_arch_64 {
57 uint32_t cputype;
58 uint32_t cpusubtype;
59 uint64_t offset;
60 uint64_t size;
61 uint32_t align;
62 uint32_t reserved;
63};
64
65#define MACH_O_CPU_ARCH_ABI64 0x01000000
66
67#define MACH_O_CPU_TYPE_X86 7
68#define MACH_O_CPU_TYPE_ARM 12
69#define MACH_O_CPU_TYPE_PPC 18
70
71#define MACH_O_CPU_TYPE_X86_64 (MACH_O_CPU_TYPE_X86 | MACH_O_CPU_ARCH_ABI64)
72#define MACH_O_CPU_TYPE_ARM64 (MACH_O_CPU_TYPE_ARM | MACH_O_CPU_ARCH_ABI64)
73#define MACH_O_CPU_TYPE_PPC64 (MACH_O_CPU_TYPE_PPC | MACH_O_CPU_ARCH_ABI64)
74
75struct macho_load_command {
76 uint32_t cmd;
77 uint32_t cmdsize;
78};
79
80#define MACH_O_LC_SEGMENT 0x01
81#define MACH_O_LC_SYMTAB 0x02
82#define MACH_O_LC_SEGMENT_64 0x19
83#define MACH_O_LC_UUID 0x1b
84
85#define MACH_O_NAMELEN (16)
86
87struct macho_segment_command {
88 uint32_t cmd;
89 uint32_t cmdsize;
90 char segname[MACH_O_NAMELEN];
91 uint32_t vmaddr;
92 uint32_t vmsize;
93 uint32_t fileoff;
94 uint32_t filesize;
95 uint32_t maxprot;
96 uint32_t initprot;
97 uint32_t nsects;
98 uint32_t flags;
99};
100
101struct macho_segment_64_command {
102 uint32_t cmd;
103 uint32_t cmdsize;
104 char segname[MACH_O_NAMELEN];
105 uint64_t vmaddr;
106 uint64_t vmsize;
107 uint64_t fileoff;
108 uint64_t filesize;
109 uint32_t maxprot;
110 uint32_t initprot;
111 uint32_t nsects;
112 uint32_t flags;
113};
114
115struct macho_symtab_command {
116 uint32_t cmd;
117 uint32_t cmdsize;
118 uint32_t symoff;
119 uint32_t nsyms;
120 uint32_t stroff;
121 uint32_t strsize;
122};
123
124#define MACH_O_UUID_LEN (16)
125
126struct macho_uuid_command {
127 uint32_t cmd;
128 uint32_t cmdsize;
129 unsigned char uuid[MACH_O_UUID_LEN];
130};
131
132struct macho_section {
133 char sectname[MACH_O_NAMELEN];
134 char segment[MACH_O_NAMELEN];
135 uint32_t addr;
136 uint32_t size;
137 uint32_t offset;
138 uint32_t align;
139 uint32_t reloff;
140 uint32_t nreloc;
141 uint32_t flags;
142 uint32_t reserved1;
143 uint32_t reserved2;
144};
145
146struct macho_section_64 {
147 char sectname[MACH_O_NAMELEN];
148 char segment[MACH_O_NAMELEN];
149 uint64_t addr;
150 uint64_t size;
151 uint32_t offset;
152 uint32_t align;
153 uint32_t reloff;
154 uint32_t nreloc;
155 uint32_t flags;
156 uint32_t reserved1;
157 uint32_t reserved2;
158 uint32_t reserved3;
159};
160
161struct macho_nlist {
162 uint32_t n_strx;
163 uint8_t n_type;
164 uint8_t n_sect;
165 uint16_t n_desc;
166 uint32_t n_value;
167};
168
169struct macho_nlist_64 {
170 uint32_t n_strx;
171 uint8_t n_type;
172 uint8_t n_sect;
173 uint16_t n_desc;
174 uint64_t n_value;
175};
176
177#define MACH_O_N_EXT 0x01
178#define MACH_O_N_ABS 0x02
179#define MACH_O_N_SECT 0x0e
180#define MACH_O_N_TYPE 0x0e
181#define MACH_O_N_STAB 0xe0
182
183struct macho_symbol {
184 const char *name;
185 uintptr_t address;
186};
187
188struct macho_syminfo_data {
189 struct macho_syminfo_data *next;
190 struct macho_symbol *symbols;
191 size_t count;
192};
193
194static const char *const dwarf_section_names[DEBUG_MAX] = {
195 "__debug_info", "__debug_line",
196 "__debug_abbrev", "__debug_ranges",
197 "__debug_str", "",
198 "__debug_str_offs", "",
199 "__debug_rnglists"};
200
201static int macho_add(struct backtrace_state *, const char *, int, off_t,
202 const unsigned char *, uintptr_t, int,
203 backtrace_error_callback, void *, fileline *, int *);
204
205static int macho_nodebug(struct backtrace_state *state ATTRIBUTE_UNUSED,
206 uintptr_t pc ATTRIBUTE_UNUSED,
207 backtrace_full_callback callback ATTRIBUTE_UNUSED,
208 backtrace_error_callback error_callback, void *data) {
209 error_callback(data, "no debug info in Mach-O executable", -1);
210 return 0;
211}
212
213static void macho_nosyms(struct backtrace_state *state ATTRIBUTE_UNUSED,
214 uintptr_t addr ATTRIBUTE_UNUSED,
215 backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
216 backtrace_error_callback error_callback, void *data) {
217 error_callback(data, "no symbol table in Mach-O executable", -1);
218}
219
220static int macho_add_dwarf_section(struct backtrace_state *state,
221 int descriptor, const char *sectname,
222 uint32_t offset, uint64_t size,
223 backtrace_error_callback error_callback,
224 void *data,
225 struct dwarf_sections *dwarf_sections) {
226 int i;
227
228 for (i = 0; i < (int)DEBUG_MAX; ++i) {
229 if (dwarf_section_names[i][0] != '\0' &&
230 strncmp(sectname, dwarf_section_names[i], MACH_O_NAMELEN) == 0) {
231 struct backtrace_view section_view;
232
233 if (!backtrace_get_view(state, descriptor, offset, size, error_callback,
234 data, §ion_view))
235 return 0;
236 dwarf_sections->data[i] = (const unsigned char *)section_view.data;
237 dwarf_sections->size[i] = size;
238 break;
239 }
240 }
241 return 1;
242}
243
244static int macho_add_dwarf_segment(struct backtrace_state *state,
245 int descriptor, off_t offset,
246 unsigned int cmd, const char *psecs,
247 size_t sizesecs, unsigned int nsects,
248 backtrace_error_callback error_callback,
249 void *data,
250 struct dwarf_sections *dwarf_sections) {
251 size_t sec_header_size;
252 size_t secoffset;
253 unsigned int i;
254
255 switch (cmd) {
256 case MACH_O_LC_SEGMENT:
257 sec_header_size = sizeof(struct macho_section);
258 break;
259 case MACH_O_LC_SEGMENT_64:
260 sec_header_size = sizeof(struct macho_section_64);
261 break;
262 default:
263 abort();
264 }
265
266 secoffset = 0;
267 for (i = 0; i < nsects; ++i) {
268 if (secoffset + sec_header_size > sizesecs) {
269 error_callback(data, "section overflow withing segment", 0);
270 return 0;
271 }
272
273 switch (cmd) {
274 case MACH_O_LC_SEGMENT: {
275 struct macho_section section;
276
277 memcpy(§ion, psecs + secoffset, sizeof section);
278 macho_add_dwarf_section(state, descriptor, section.sectname,
279 offset + section.offset, section.size,
280 error_callback, data, dwarf_sections);
281 } break;
282
283 case MACH_O_LC_SEGMENT_64: {
284 struct macho_section_64 section;
285
286 memcpy(§ion, psecs + secoffset, sizeof section);
287 macho_add_dwarf_section(state, descriptor, section.sectname,
288 offset + section.offset, section.size,
289 error_callback, data, dwarf_sections);
290 } break;
291
292 default:
293 abort();
294 }
295
296 secoffset += sec_header_size;
297 }
298
299 return 1;
300}
301
302static int macho_symbol_compare(const void *v1, const void *v2) {
303 const struct macho_symbol *m1 = (const struct macho_symbol *)v1;
304 const struct macho_symbol *m2 = (const struct macho_symbol *)v2;
305
306 if (m1->address < m2->address)
307 return -1;
308 else if (m1->address > m2->address)
309 return 1;
310 else
311 return 0;
312}
313
314static int macho_symbol_search(const void *vkey, const void *ventry) {
315 const uintptr_t *key = (const uintptr_t *)vkey;
316 const struct macho_symbol *entry = (const struct macho_symbol *)ventry;
317 uintptr_t addr;
318
319 addr = *key;
320 if (addr < entry->address)
321 return -1;
322 else if (entry->name[0] == '\0' && entry->address == ~(uintptr_t)0)
323 return -1;
324 else if ((entry + 1)->name[0] == '\0' &&
325 (entry + 1)->address == ~(uintptr_t)0)
326 return -1;
327 else if (addr >= (entry + 1)->address)
328 return 1;
329 else
330 return 0;
331}
332
333static int macho_defined_symbol(uint8_t type) {
334 if ((type & MACH_O_N_STAB) != 0) return 0;
335 if ((type & MACH_O_N_EXT) != 0) return 0;
336 switch (type & MACH_O_N_TYPE) {
337 case MACH_O_N_ABS:
338 return 1;
339 case MACH_O_N_SECT:
340 return 1;
341 default:
342 return 0;
343 }
344}
345
346static int macho_add_symtab(struct backtrace_state *state, int descriptor,
347 uintptr_t base_address, int is_64, off_t symoff,
348 unsigned int nsyms, off_t stroff,
349 unsigned int strsize,
350 backtrace_error_callback error_callback,
351 void *data) {
352 size_t symsize;
353 struct backtrace_view sym_view;
354 int sym_view_valid;
355 struct backtrace_view str_view;
356 int str_view_valid;
357 size_t ndefs;
358 size_t symtaboff;
359 unsigned int i;
360 size_t macho_symbol_size;
361 struct macho_symbol *macho_symbols;
362 unsigned int j;
363 struct macho_syminfo_data *sdata;
364
365 sym_view_valid = 0;
366 str_view_valid = 0;
367 macho_symbol_size = 0;
368 macho_symbols = NULL;
369
370 if (is_64)
371 symsize = sizeof(struct macho_nlist_64);
372 else
373 symsize = sizeof(struct macho_nlist);
374
375 if (!backtrace_get_view(state, descriptor, symoff, nsyms * symsize,
376 error_callback, data, &sym_view))
377 goto fail;
378 sym_view_valid = 1;
379
380 if (!backtrace_get_view(state, descriptor, stroff, strsize, error_callback,
381 data, &str_view))
382 return 0;
383 str_view_valid = 1;
384
385 ndefs = 0;
386 symtaboff = 0;
387 for (i = 0; i < nsyms; ++i, symtaboff += symsize) {
388 if (is_64) {
389 struct macho_nlist_64 nlist;
390
391 memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist);
392 if (macho_defined_symbol(nlist.n_type)) ++ndefs;
393 } else {
394 struct macho_nlist nlist;
395
396 memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist);
397 if (macho_defined_symbol(nlist.n_type)) ++ndefs;
398 }
399 }
400
401 macho_symbol_size = (ndefs + 1) * sizeof(struct macho_symbol);
402 macho_symbols = ((struct macho_symbol *)backtrace_alloc(
403 state, macho_symbol_size, error_callback, data));
404 if (macho_symbols == NULL) goto fail;
405
406 j = 0;
407 symtaboff = 0;
408 for (i = 0; i < nsyms; ++i, symtaboff += symsize) {
409 uint32_t strx;
410 uint64_t value;
411 const char *name;
412
413 strx = 0;
414 value = 0;
415 if (is_64) {
416 struct macho_nlist_64 nlist;
417
418 memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist);
419 if (!macho_defined_symbol(nlist.n_type)) continue;
420
421 strx = nlist.n_strx;
422 value = nlist.n_value;
423 } else {
424 struct macho_nlist nlist;
425
426 memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist);
427 if (!macho_defined_symbol(nlist.n_type)) continue;
428
429 strx = nlist.n_strx;
430 value = nlist.n_value;
431 }
432
433 if (strx >= strsize) {
434 error_callback(data, "symbol string index out of range", 0);
435 goto fail;
436 }
437
438 name = (const char *)str_view.data + strx;
439 if (name[0] == '_') ++name;
440 macho_symbols[j].name = name;
441 macho_symbols[j].address = value + base_address;
442 ++j;
443 }
444
445 sdata = ((struct macho_syminfo_data *)backtrace_alloc(state, sizeof *sdata,
446 error_callback, data));
447 if (sdata == NULL) goto fail;
448
449 backtrace_release_view(state, &sym_view, error_callback, data);
450 sym_view_valid = 0;
451 str_view_valid = 0;
452
453 macho_symbols[j].name = "";
454 macho_symbols[j].address = ~(uintptr_t)0;
455
456 backtrace_qsort(macho_symbols, ndefs + 1, sizeof(struct macho_symbol),
457 macho_symbol_compare);
458
459 sdata->next = NULL;
460 sdata->symbols = macho_symbols;
461 sdata->count = ndefs;
462
463 if (!state->threaded) {
464 struct macho_syminfo_data **pp;
465
466 for (pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data;
467 *pp != NULL; pp = &(*pp)->next)
468 ;
469 *pp = sdata;
470 } else {
471 while (1) {
472 struct macho_syminfo_data **pp;
473
474 pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data;
475
476 while (1) {
477 struct macho_syminfo_data *p;
478
479 p = backtrace_atomic_load_pointer(pp);
480
481 if (p == NULL) break;
482
483 pp = &p->next;
484 }
485
486 if (__sync_bool_compare_and_swap(pp, NULL, sdata)) break;
487 }
488 }
489
490 return 1;
491
492fail:
493 if (macho_symbols != NULL)
494 backtrace_free(state, macho_symbols, macho_symbol_size, error_callback,
495 data);
496 if (sym_view_valid)
497 backtrace_release_view(state, &sym_view, error_callback, data);
498 if (str_view_valid)
499 backtrace_release_view(state, &str_view, error_callback, data);
500 return 0;
501}
502
503static void macho_syminfo(
504 struct backtrace_state *state, uintptr_t addr,
505 backtrace_syminfo_callback callback,
506 backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data) {
507 struct macho_syminfo_data *sdata;
508 struct macho_symbol *sym;
509
510 sym = NULL;
511 if (!state->threaded) {
512 for (sdata = (struct macho_syminfo_data *)state->syminfo_data;
513 sdata != NULL; sdata = sdata->next) {
514 sym = ((struct macho_symbol *)bsearch(&addr, sdata->symbols, sdata->count,
515 sizeof(struct macho_symbol),
516 macho_symbol_search));
517 if (sym != NULL) break;
518 }
519 } else {
520 struct macho_syminfo_data **pp;
521
522 pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data;
523 while (1) {
524 sdata = backtrace_atomic_load_pointer(pp);
525 if (sdata == NULL) break;
526
527 sym = ((struct macho_symbol *)bsearch(&addr, sdata->symbols, sdata->count,
528 sizeof(struct macho_symbol),
529 macho_symbol_search));
530 if (sym != NULL) break;
531
532 pp = &sdata->next;
533 }
534 }
535
536 if (sym == NULL)
537 callback(data, addr, NULL, 0, 0);
538 else
539 callback(data, addr, sym->name, sym->address, 0);
540}
541
542static int macho_add_fat(struct backtrace_state *state, const char *filename,
543 int descriptor, int swapped, off_t offset,
544 const unsigned char *match_uuid,
545 uintptr_t base_address, int skip_symtab,
546 uint32_t nfat_arch, int is_64,
547 backtrace_error_callback error_callback, void *data,
548 fileline *fileline_fn, int *found_sym) {
549 int arch_view_valid;
550 unsigned int cputype;
551 size_t arch_size;
552 struct backtrace_view arch_view;
553 unsigned int i;
554
555 arch_view_valid = 0;
556
557#if defined(__x86_64__)
558 cputype = MACH_O_CPU_TYPE_X86_64;
559#elif defined(__i386__)
560 cputype = MACH_O_CPU_TYPE_X86;
561#elif defined(__aarch64__)
562 cputype = MACH_O_CPU_TYPE_ARM64;
563#elif defined(__arm__)
564 cputype = MACH_O_CPU_TYPE_ARM;
565#elif defined(__ppc__)
566 cputype = MACH_O_CPU_TYPE_PPC;
567#elif defined(__ppc64__)
568 cputype = MACH_O_CPU_TYPE_PPC64;
569#else
570 error_callback(data, "unknown Mach-O architecture", 0);
571 goto fail;
572#endif
573
574 if (is_64)
575 arch_size = sizeof(struct macho_fat_arch_64);
576 else
577 arch_size = sizeof(struct macho_fat_arch);
578
579 if (!backtrace_get_view(state, descriptor, offset, nfat_arch * arch_size,
580 error_callback, data, &arch_view))
581 goto fail;
582
583 for (i = 0; i < nfat_arch; ++i) {
584 uint32_t fcputype;
585 uint64_t foffset;
586
587 if (is_64) {
588 struct macho_fat_arch_64 fat_arch_64;
589
590 memcpy(&fat_arch_64, (const char *)arch_view.data + i * arch_size,
591 arch_size);
592 fcputype = fat_arch_64.cputype;
593 foffset = fat_arch_64.offset;
594 if (swapped) {
595 fcputype = __builtin_bswap32(fcputype);
596 foffset = __builtin_bswap64(foffset);
597 }
598 } else {
599 struct macho_fat_arch fat_arch_32;
600
601 memcpy(&fat_arch_32, (const char *)arch_view.data + i * arch_size,
602 arch_size);
603 fcputype = fat_arch_32.cputype;
604 foffset = (uint64_t)fat_arch_32.offset;
605 if (swapped) {
606 fcputype = __builtin_bswap32(fcputype);
607 foffset = (uint64_t)__builtin_bswap32((uint32_t)foffset);
608 }
609 }
610
611 if (fcputype == cputype) {
612 backtrace_release_view(state, &arch_view, error_callback, data);
613 return macho_add(state, filename, descriptor, foffset, match_uuid,
614 base_address, skip_symtab, error_callback, data,
615 fileline_fn, found_sym);
616 }
617 }
618
619 error_callback(data, "could not find executable in fat file", 0);
620
621fail:
622 if (arch_view_valid)
623 backtrace_release_view(state, &arch_view, error_callback, data);
624 if (descriptor != -1) backtrace_close(descriptor, error_callback, data);
625 return 0;
626}
627
628static int macho_add_dsym(struct backtrace_state *state, const char *filename,
629 uintptr_t base_address, const unsigned char *uuid,
630 backtrace_error_callback error_callback, void *data,
631 fileline *fileline_fn) {
632 const char *p;
633 const char *dirname;
634 char *diralc;
635 size_t dirnamelen;
636 const char *basename;
637 size_t basenamelen;
638 const char *dsymsuffixdir;
639 size_t dsymsuffixdirlen;
640 size_t dsymlen;
641 char *dsym;
642 char *ps;
643 int d;
644 int does_not_exist;
645 int dummy_found_sym;
646
647 diralc = NULL;
648 dirnamelen = 0;
649 dsym = NULL;
650 dsymlen = 0;
651
652 p = strrchr(filename, '/');
653 if (p == NULL) {
654 dirname = ".";
655 dirnamelen = 1;
656 basename = filename;
657 basenamelen = strlen(basename);
658 diralc = NULL;
659 } else {
660 dirnamelen = p - filename;
661 diralc = backtrace_alloc(state, dirnamelen + 1, error_callback, data);
662 if (diralc == NULL) goto fail;
663 memcpy(diralc, filename, dirnamelen);
664 diralc[dirnamelen] = '\0';
665 dirname = diralc;
666 basename = p + 1;
667 basenamelen = strlen(basename);
668 }
669
670 dsymsuffixdir = ".dSYM/Contents/Resources/DWARF/";
671 dsymsuffixdirlen = strlen(dsymsuffixdir);
672
673 dsymlen = (dirnamelen + 1 + basenamelen + dsymsuffixdirlen + basenamelen + 1);
674 dsym = backtrace_alloc(state, dsymlen, error_callback, data);
675 if (dsym == NULL) goto fail;
676
677 ps = dsym;
678 memcpy(ps, dirname, dirnamelen);
679 ps += dirnamelen;
680 *ps++ = '/';
681 memcpy(ps, basename, basenamelen);
682 ps += basenamelen;
683 memcpy(ps, dsymsuffixdir, dsymsuffixdirlen);
684 ps += dsymsuffixdirlen;
685 memcpy(ps, basename, basenamelen);
686 ps += basenamelen;
687 *ps = '\0';
688
689 if (diralc != NULL) {
690 backtrace_free(state, diralc, dirnamelen + 1, error_callback, data);
691 diralc = NULL;
692 }
693
694 d = backtrace_open(dsym, error_callback, data, &does_not_exist);
695 if (d < 0) {
696 backtrace_free(state, dsym, dsymlen, error_callback, data);
697 return 1;
698 }
699
700 if (!macho_add(state, dsym, d, 0, uuid, base_address, 1, error_callback, data,
701 fileline_fn, &dummy_found_sym))
702 goto fail;
703
704 backtrace_free(state, dsym, dsymlen, error_callback, data);
705
706 return 1;
707
708fail:
709 if (dsym != NULL) backtrace_free(state, dsym, dsymlen, error_callback, data);
710 if (diralc != NULL)
711 backtrace_free(state, diralc, dirnamelen, error_callback, data);
712 return 0;
713}
714
715static int macho_add(struct backtrace_state *state, const char *filename,
716 int descriptor, off_t offset,
717 const unsigned char *match_uuid, uintptr_t base_address,
718 int skip_symtab, backtrace_error_callback error_callback,
719 void *data, fileline *fileline_fn, int *found_sym) {
720 struct backtrace_view header_view;
721 struct macho_header_32 header;
722 off_t hdroffset;
723 int is_64;
724 struct backtrace_view cmds_view;
725 int cmds_view_valid;
726 struct dwarf_sections dwarf_sections;
727 int have_dwarf;
728 unsigned char uuid[MACH_O_UUID_LEN];
729 int have_uuid;
730 size_t cmdoffset;
731 unsigned int i;
732
733 *found_sym = 0;
734
735 cmds_view_valid = 0;
736
737 if (!backtrace_get_view(state, descriptor, offset,
738 sizeof(struct macho_header_32), error_callback, data,
739 &header_view))
740 goto fail;
741
742 memcpy(&header, header_view.data, sizeof header);
743
744 backtrace_release_view(state, &header_view, error_callback, data);
745
746 switch (header.magic) {
747 case MACH_O_MH_MAGIC_32:
748 is_64 = 0;
749 hdroffset = offset + sizeof(struct macho_header_32);
750 break;
751 case MACH_O_MH_MAGIC_64:
752 is_64 = 1;
753 hdroffset = offset + sizeof(struct macho_header_64);
754 break;
755 case MACH_O_MH_MAGIC_FAT:
756 case MACH_O_MH_MAGIC_FAT_64: {
757 struct macho_header_fat fat_header;
758
759 hdroffset = offset + sizeof(struct macho_header_fat);
760 memcpy(&fat_header, &header, sizeof fat_header);
761 return macho_add_fat(state, filename, descriptor, 0, hdroffset,
762 match_uuid, base_address, skip_symtab,
763 fat_header.nfat_arch,
764 header.magic == MACH_O_MH_MAGIC_FAT_64,
765 error_callback, data, fileline_fn, found_sym);
766 }
767 case MACH_O_MH_CIGAM_FAT:
768 case MACH_O_MH_CIGAM_FAT_64: {
769 struct macho_header_fat fat_header;
770 uint32_t nfat_arch;
771
772 hdroffset = offset + sizeof(struct macho_header_fat);
773 memcpy(&fat_header, &header, sizeof fat_header);
774 nfat_arch = __builtin_bswap32(fat_header.nfat_arch);
775 return macho_add_fat(state, filename, descriptor, 1, hdroffset,
776 match_uuid, base_address, skip_symtab, nfat_arch,
777 header.magic == MACH_O_MH_CIGAM_FAT_64,
778 error_callback, data, fileline_fn, found_sym);
779 }
780 default:
781 error_callback(data, "executable file is not in Mach-O format", 0);
782 goto fail;
783 }
784
785 switch (header.filetype) {
786 case MACH_O_MH_EXECUTE:
787 case MACH_O_MH_DYLIB:
788 case MACH_O_MH_DSYM:
789 break;
790 default:
791 error_callback(data, "executable file is not an executable", 0);
792 goto fail;
793 }
794
795 if (!backtrace_get_view(state, descriptor, hdroffset, header.sizeofcmds,
796 error_callback, data, &cmds_view))
797 goto fail;
798 cmds_view_valid = 1;
799
800 memset(&dwarf_sections, 0, sizeof dwarf_sections);
801 have_dwarf = 0;
802 memset(&uuid, 0, sizeof uuid);
803 have_uuid = 0;
804
805 cmdoffset = 0;
806 for (i = 0; i < header.ncmds; ++i) {
807 const char *pcmd;
808 struct macho_load_command load_command;
809
810 if (cmdoffset + sizeof load_command > header.sizeofcmds) break;
811
812 pcmd = (const char *)cmds_view.data + cmdoffset;
813 memcpy(&load_command, pcmd, sizeof load_command);
814
815 switch (load_command.cmd) {
816 case MACH_O_LC_SEGMENT: {
817 struct macho_segment_command segcmd;
818
819 memcpy(&segcmd, pcmd, sizeof segcmd);
820 if (memcmp(segcmd.segname, "__DWARF\0\0\0\0\0\0\0\0\0",
821 MACH_O_NAMELEN) == 0) {
822 if (!macho_add_dwarf_segment(
823 state, descriptor, offset, load_command.cmd,
824 pcmd + sizeof segcmd, (load_command.cmdsize - sizeof segcmd),
825 segcmd.nsects, error_callback, data, &dwarf_sections))
826 goto fail;
827 have_dwarf = 1;
828 }
829 } break;
830
831 case MACH_O_LC_SEGMENT_64: {
832 struct macho_segment_64_command segcmd;
833
834 memcpy(&segcmd, pcmd, sizeof segcmd);
835 if (memcmp(segcmd.segname, "__DWARF\0\0\0\0\0\0\0\0\0",
836 MACH_O_NAMELEN) == 0) {
837 if (!macho_add_dwarf_segment(
838 state, descriptor, offset, load_command.cmd,
839 pcmd + sizeof segcmd, (load_command.cmdsize - sizeof segcmd),
840 segcmd.nsects, error_callback, data, &dwarf_sections))
841 goto fail;
842 have_dwarf = 1;
843 }
844 } break;
845
846 case MACH_O_LC_SYMTAB:
847 if (!skip_symtab) {
848 struct macho_symtab_command symcmd;
849
850 memcpy(&symcmd, pcmd, sizeof symcmd);
851 if (!macho_add_symtab(state, descriptor, base_address, is_64,
852 offset + symcmd.symoff, symcmd.nsyms,
853 offset + symcmd.stroff, symcmd.strsize,
854 error_callback, data))
855 goto fail;
856
857 *found_sym = 1;
858 }
859 break;
860
861 case MACH_O_LC_UUID: {
862 struct macho_uuid_command uuidcmd;
863
864 memcpy(&uuidcmd, pcmd, sizeof uuidcmd);
865 memcpy(&uuid[0], &uuidcmd.uuid[0], MACH_O_UUID_LEN);
866 have_uuid = 1;
867 } break;
868
869 default:
870 break;
871 }
872
873 cmdoffset += load_command.cmdsize;
874 }
875
876 if (!backtrace_close(descriptor, error_callback, data)) goto fail;
877 descriptor = -1;
878
879 backtrace_release_view(state, &cmds_view, error_callback, data);
880 cmds_view_valid = 0;
881
882 if (match_uuid != NULL) {
883 if (!have_uuid || memcmp(match_uuid, &uuid[0], MACH_O_UUID_LEN) != 0)
884 return 1;
885 }
886
887 if (have_dwarf) {
888 int is_big_endian;
889
890 is_big_endian = 0;
891#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
892#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
893 is_big_endian = 1;
894#endif
895#endif
896
897 if (!backtrace_dwarf_add(state, base_address, &dwarf_sections,
898 is_big_endian, NULL, error_callback, data,
899 fileline_fn, NULL))
900 goto fail;
901 }
902
903 if (!have_dwarf && have_uuid) {
904 if (!macho_add_dsym(state, filename, base_address, &uuid[0], error_callback,
905 data, fileline_fn))
906 goto fail;
907 }
908
909 return 1;
910
911fail:
912 if (cmds_view_valid)
913 backtrace_release_view(state, &cmds_view, error_callback, data);
914 if (descriptor != -1) backtrace_close(descriptor, error_callback, data);
915 return 0;
916}
917
918#ifdef HAVE_MACH_O_DYLD_H
919
920int backtrace_initialize(struct backtrace_state *state, const char *filename,
921 int descriptor,
922 backtrace_error_callback error_callback, void *data,
923 fileline *fileline_fn) {
924 uint32_t c;
925 uint32_t i;
926 int closed_descriptor;
927 int found_sym;
928 fileline macho_fileline_fn;
929
930 closed_descriptor = 0;
931 found_sym = 0;
932 macho_fileline_fn = macho_nodebug;
933
934 c = _dyld_image_count();
935 for (i = 0; i < c; ++i) {
936 uintptr_t base_address;
937 const char *name;
938 int d;
939 fileline mff;
940 int mfs;
941
942 name = _dyld_get_image_name(i);
943 if (name == NULL) continue;
944
945 if (strcmp(name, filename) == 0 && !closed_descriptor) {
946 d = descriptor;
947 closed_descriptor = 1;
948 } else {
949 int does_not_exist;
950
951 d = backtrace_open(name, error_callback, data, &does_not_exist);
952 if (d < 0) continue;
953 }
954
955 base_address = _dyld_get_image_vmaddr_slide(i);
956
957 mff = macho_nodebug;
958 if (!macho_add(state, name, d, 0, NULL, base_address, 0, error_callback,
959 data, &mff, &mfs))
960 return 0;
961
962 if (mff != macho_nodebug) macho_fileline_fn = mff;
963 if (mfs) found_sym = 1;
964 }
965
966 if (!closed_descriptor) backtrace_close(descriptor, error_callback, data);
967
968 if (!state->threaded) {
969 if (found_sym)
970 state->syminfo_fn = macho_syminfo;
971 else if (state->syminfo_fn == NULL)
972 state->syminfo_fn = macho_nosyms;
973 } else {
974 if (found_sym)
975 backtrace_atomic_store_pointer(&state->syminfo_fn, macho_syminfo);
976 else
977 (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL,
978 macho_nosyms);
979 }
980
981 if (!state->threaded)
982 *fileline_fn = state->fileline_fn;
983 else
984 *fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn);
985
986 if (*fileline_fn == NULL || *fileline_fn == macho_nodebug)
987 *fileline_fn = macho_fileline_fn;
988
989 return 1;
990}
991
992#else
993
994int backtrace_initialize(struct backtrace_state *state, const char *filename,
995 int descriptor,
996 backtrace_error_callback error_callback, void *data,
997 fileline *fileline_fn) {
998 fileline macho_fileline_fn;
999 int found_sym;
1000
1001 macho_fileline_fn = macho_nodebug;
1002 if (!macho_add(state, filename, descriptor, 0, NULL, 0, 0, error_callback,
1003 data, &macho_fileline_fn, &found_sym))
1004 return 0;
1005
1006 if (!state->threaded) {
1007 if (found_sym)
1008 state->syminfo_fn = macho_syminfo;
1009 else if (state->syminfo_fn == NULL)
1010 state->syminfo_fn = macho_nosyms;
1011 } else {
1012 if (found_sym)
1013 backtrace_atomic_store_pointer(&state->syminfo_fn, macho_syminfo);
1014 else
1015 (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL,
1016 macho_nosyms);
1017 }
1018
1019 if (!state->threaded)
1020 *fileline_fn = state->fileline_fn;
1021 else
1022 *fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn);
1023
1024 if (*fileline_fn == NULL || *fileline_fn == macho_nodebug)
1025 *fileline_fn = macho_fileline_fn;
1026
1027 return 1;
1028}
1029
1030#endif
1031
1032// mmapio.c:
1033#include <errno.h>
1034#include <sys/mman.h>
1035#include <sys/types.h>
1036#include <unistd.h>
1037
1038#ifndef HAVE_DECL_GETPAGESIZE
1039extern int getpagesize(void);
1040#endif
1041
1042#ifndef MAP_FAILED
1043#define MAP_FAILED ((void *)-1)
1044#endif
1045
1046int backtrace_get_view(struct backtrace_state *state ATTRIBUTE_UNUSED,
1047 int descriptor, off_t offset, uint64_t size,
1048 backtrace_error_callback error_callback, void *data,
1049 struct backtrace_view *view) {
1050 size_t pagesize;
1051 unsigned int inpage;
1052 off_t pageoff;
1053 void *map;
1054
1055 if ((uint64_t)(size_t)size != size) {
1056 error_callback(data, "file size too large", 0);
1057 return 0;
1058 }
1059
1060 pagesize = getpagesize();
1061 inpage = offset % pagesize;
1062 pageoff = offset - inpage;
1063
1064 size += inpage;
1065 size = (size + (pagesize - 1)) & ~(pagesize - 1);
1066
1067 map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff);
1068 if (map == MAP_FAILED) {
1069 error_callback(data, "mmap", errno);
1070 return 0;
1071 }
1072
1073 view->data = (char *)map + inpage;
1074 view->base = map;
1075 view->len = size;
1076
1077 return 1;
1078}
1079
1080void backtrace_release_view(struct backtrace_state *state ATTRIBUTE_UNUSED,
1081 struct backtrace_view *view,
1082 backtrace_error_callback error_callback,
1083 void *data) {
1084 union {
1085 const void *cv;
1086 void *v;
1087 } const_cast;
1088
1089 const_cast.cv = view->base;
1090 if (munmap(const_cast.v, view->len) < 0)
1091 error_callback(data, "munmap", errno);
1092}
1093
1094// mmap.c:
1095#include <errno.h>
1096#include <stdlib.h>
1097#include <string.h>
1098#include <sys/mman.h>
1099#include <sys/types.h>
1100#include <unistd.h>
1101
1102#ifndef HAVE_DECL_GETPAGESIZE
1103extern int getpagesize(void);
1104#endif
1105
1106#ifndef MAP_ANONYMOUS
1107#define MAP_ANONYMOUS MAP_ANON
1108#endif
1109
1110#ifndef MAP_FAILED
1111#define MAP_FAILED ((void *)-1)
1112#endif
1113
1114struct backtrace_freelist_struct {
1115 struct backtrace_freelist_struct *next;
1116
1117 size_t size;
1118};
1119
1120static void backtrace_free_locked(struct backtrace_state *state, void *addr,
1121 size_t size) {
1122 if (size >= sizeof(struct backtrace_freelist_struct)) {
1123 size_t c;
1124 struct backtrace_freelist_struct **ppsmall;
1125 struct backtrace_freelist_struct **pp;
1126 struct backtrace_freelist_struct *p;
1127
1128 c = 0;
1129 ppsmall = NULL;
1130 for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) {
1131 if (ppsmall == NULL || (*pp)->size < (*ppsmall)->size) ppsmall = pp;
1132 ++c;
1133 }
1134 if (c >= 16) {
1135 if (size <= (*ppsmall)->size) return;
1136 *ppsmall = (*ppsmall)->next;
1137 }
1138
1139 p = (struct backtrace_freelist_struct *)addr;
1140 p->next = state->freelist;
1141 p->size = size;
1142 state->freelist = p;
1143 }
1144}
1145
1146void *backtrace_alloc(struct backtrace_state *state, size_t size,
1147 backtrace_error_callback error_callback, void *data) {
1148 void *ret;
1149 int locked;
1150 struct backtrace_freelist_struct **pp;
1151 size_t pagesize;
1152 size_t asksize;
1153 void *page;
1154
1155 ret = NULL;
1156
1157 if (!state->threaded)
1158 locked = 1;
1159 else
1160 locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0;
1161
1162 if (locked) {
1163 for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) {
1164 if ((*pp)->size >= size) {
1165 struct backtrace_freelist_struct *p;
1166
1167 p = *pp;
1168 *pp = p->next;
1169
1170 size = (size + 7) & ~(size_t)7;
1171 if (size < p->size)
1172 backtrace_free_locked(state, (char *)p + size, p->size - size);
1173
1174 ret = (void *)p;
1175
1176 break;
1177 }
1178 }
1179
1180 if (state->threaded) __sync_lock_release(&state->lock_alloc);
1181 }
1182
1183 if (ret == NULL) {
1184 pagesize = getpagesize();
1185 asksize = (size + pagesize - 1) & ~(pagesize - 1);
1186 page = mmap(NULL, asksize, PROT_READ | PROT_WRITE,
1187 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1188 if (page == MAP_FAILED) {
1189 if (error_callback) error_callback(data, "mmap", errno);
1190 } else {
1191 size = (size + 7) & ~(size_t)7;
1192 if (size < asksize)
1193 backtrace_free(state, (char *)page + size, asksize - size,
1194 error_callback, data);
1195
1196 ret = page;
1197 }
1198 }
1199
1200 return ret;
1201}
1202
1203void backtrace_free(struct backtrace_state *state, void *addr, size_t size,
1204 backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
1205 void *data ATTRIBUTE_UNUSED) {
1206 int locked;
1207
1208 if (size >= 16 * 4096) {
1209 size_t pagesize;
1210
1211 pagesize = getpagesize();
1212 if (((uintptr_t)addr & (pagesize - 1)) == 0 &&
1213 (size & (pagesize - 1)) == 0) {
1214 if (munmap(addr, size) == 0) return;
1215 }
1216 }
1217
1218 if (!state->threaded)
1219 locked = 1;
1220 else
1221 locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0;
1222
1223 if (locked) {
1224 backtrace_free_locked(state, addr, size);
1225
1226 if (state->threaded) __sync_lock_release(&state->lock_alloc);
1227 }
1228}
1229
1230void *backtrace_vector_grow(struct backtrace_state *state, size_t size,
1231 backtrace_error_callback error_callback, void *data,
1232 struct backtrace_vector *vec) {
1233 void *ret;
1234
1235 if (size > vec->alc) {
1236 size_t pagesize;
1237 size_t alc;
1238 void *base;
1239
1240 pagesize = getpagesize();
1241 alc = vec->size + size;
1242 if (vec->size == 0)
1243 alc = 16 * size;
1244 else if (alc < pagesize) {
1245 alc *= 2;
1246 if (alc > pagesize) alc = pagesize;
1247 } else {
1248 alc *= 2;
1249 alc = (alc + pagesize - 1) & ~(pagesize - 1);
1250 }
1251 base = backtrace_alloc(state, alc, error_callback, data);
1252 if (base == NULL) return NULL;
1253 if (vec->base != NULL) {
1254 memcpy(base, vec->base, vec->size);
1255 backtrace_free(state, vec->base, vec->size + vec->alc, error_callback,
1256 data);
1257 }
1258 vec->base = base;
1259 vec->alc = alc - vec->size;
1260 }
1261
1262 ret = (char *)vec->base + vec->size;
1263 vec->size += size;
1264 vec->alc -= size;
1265 return ret;
1266}
1267
1268void *backtrace_vector_finish(struct backtrace_state *state ATTRIBUTE_UNUSED,
1269 struct backtrace_vector *vec,
1270 backtrace_error_callback error_callback
1271 ATTRIBUTE_UNUSED,
1272 void *data ATTRIBUTE_UNUSED) {
1273 void *ret;
1274
1275 ret = vec->base;
1276 vec->base = (char *)vec->base + vec->size;
1277 vec->size = 0;
1278 return ret;
1279}
1280
1281int backtrace_vector_release(struct backtrace_state *state,
1282 struct backtrace_vector *vec,
1283 backtrace_error_callback error_callback,
1284 void *data) {
1285 size_t size;
1286 size_t alc;
1287 size_t aligned;
1288
1289 size = vec->size;
1290 alc = vec->alc;
1291 aligned = (size + 7) & ~(size_t)7;
1292 alc -= aligned - size;
1293
1294 backtrace_free(state, (char *)vec->base + aligned, alc, error_callback, data);
1295 vec->alc = 0;
1296 if (vec->size == 0) vec->base = NULL;
1297 return 1;
1298}