// macho.c: #include #include #include #include #ifdef HAVE_MACH_O_DYLD_H #include #endif struct macho_header_32 { uint32_t magic; uint32_t cputype; uint32_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; }; struct macho_header_64 { uint32_t magic; uint32_t cputype; uint32_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; uint32_t reserved; }; struct macho_header_fat { uint32_t magic; uint32_t nfat_arch; }; #define MACH_O_MH_MAGIC_32 0xfeedface #define MACH_O_MH_MAGIC_64 0xfeedfacf #define MACH_O_MH_MAGIC_FAT 0xcafebabe #define MACH_O_MH_CIGAM_FAT 0xbebafeca #define MACH_O_MH_MAGIC_FAT_64 0xcafebabf #define MACH_O_MH_CIGAM_FAT_64 0xbfbafeca #define MACH_O_MH_EXECUTE 0x02 #define MACH_O_MH_DYLIB 0x06 #define MACH_O_MH_DSYM 0x0a struct macho_fat_arch { uint32_t cputype; uint32_t cpusubtype; uint32_t offset; uint32_t size; uint32_t align; }; struct macho_fat_arch_64 { uint32_t cputype; uint32_t cpusubtype; uint64_t offset; uint64_t size; uint32_t align; uint32_t reserved; }; #define MACH_O_CPU_ARCH_ABI64 0x01000000 #define MACH_O_CPU_TYPE_X86 7 #define MACH_O_CPU_TYPE_ARM 12 #define MACH_O_CPU_TYPE_PPC 18 #define MACH_O_CPU_TYPE_X86_64 (MACH_O_CPU_TYPE_X86 | MACH_O_CPU_ARCH_ABI64) #define MACH_O_CPU_TYPE_ARM64 (MACH_O_CPU_TYPE_ARM | MACH_O_CPU_ARCH_ABI64) #define MACH_O_CPU_TYPE_PPC64 (MACH_O_CPU_TYPE_PPC | MACH_O_CPU_ARCH_ABI64) struct macho_load_command { uint32_t cmd; uint32_t cmdsize; }; #define MACH_O_LC_SEGMENT 0x01 #define MACH_O_LC_SYMTAB 0x02 #define MACH_O_LC_SEGMENT_64 0x19 #define MACH_O_LC_UUID 0x1b #define MACH_O_NAMELEN (16) struct macho_segment_command { uint32_t cmd; uint32_t cmdsize; char segname[MACH_O_NAMELEN]; uint32_t vmaddr; uint32_t vmsize; uint32_t fileoff; uint32_t filesize; uint32_t maxprot; uint32_t initprot; uint32_t nsects; uint32_t flags; }; struct macho_segment_64_command { uint32_t cmd; uint32_t cmdsize; char segname[MACH_O_NAMELEN]; uint64_t vmaddr; uint64_t vmsize; uint64_t fileoff; uint64_t filesize; uint32_t maxprot; uint32_t initprot; uint32_t nsects; uint32_t flags; }; struct macho_symtab_command { uint32_t cmd; uint32_t cmdsize; uint32_t symoff; uint32_t nsyms; uint32_t stroff; uint32_t strsize; }; #define MACH_O_UUID_LEN (16) struct macho_uuid_command { uint32_t cmd; uint32_t cmdsize; unsigned char uuid[MACH_O_UUID_LEN]; }; struct macho_section { char sectname[MACH_O_NAMELEN]; char segment[MACH_O_NAMELEN]; uint32_t addr; uint32_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; }; struct macho_section_64 { char sectname[MACH_O_NAMELEN]; char segment[MACH_O_NAMELEN]; uint64_t addr; uint64_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; uint32_t reserved3; }; struct macho_nlist { uint32_t n_strx; uint8_t n_type; uint8_t n_sect; uint16_t n_desc; uint32_t n_value; }; struct macho_nlist_64 { uint32_t n_strx; uint8_t n_type; uint8_t n_sect; uint16_t n_desc; uint64_t n_value; }; #define MACH_O_N_EXT 0x01 #define MACH_O_N_ABS 0x02 #define MACH_O_N_SECT 0x0e #define MACH_O_N_TYPE 0x0e #define MACH_O_N_STAB 0xe0 struct macho_symbol { const char *name; uintptr_t address; }; struct macho_syminfo_data { struct macho_syminfo_data *next; struct macho_symbol *symbols; size_t count; }; static const char *const dwarf_section_names[DEBUG_MAX] = { "__debug_info", "__debug_line", "__debug_abbrev", "__debug_ranges", "__debug_str", "", "__debug_str_offs", "", "__debug_rnglists"}; static int macho_add(struct backtrace_state *, const char *, int, off_t, const unsigned char *, uintptr_t, int, backtrace_error_callback, void *, fileline *, int *); static int macho_nodebug(struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t pc ATTRIBUTE_UNUSED, backtrace_full_callback callback ATTRIBUTE_UNUSED, backtrace_error_callback error_callback, void *data) { error_callback(data, "no debug info in Mach-O executable", -1); return 0; } static void macho_nosyms(struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t addr ATTRIBUTE_UNUSED, backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, backtrace_error_callback error_callback, void *data) { error_callback(data, "no symbol table in Mach-O executable", -1); } static int macho_add_dwarf_section(struct backtrace_state *state, int descriptor, const char *sectname, uint32_t offset, uint64_t size, backtrace_error_callback error_callback, void *data, struct dwarf_sections *dwarf_sections) { int i; for (i = 0; i < (int)DEBUG_MAX; ++i) { if (dwarf_section_names[i][0] != '\0' && strncmp(sectname, dwarf_section_names[i], MACH_O_NAMELEN) == 0) { struct backtrace_view section_view; if (!backtrace_get_view(state, descriptor, offset, size, error_callback, data, §ion_view)) return 0; dwarf_sections->data[i] = (const unsigned char *)section_view.data; dwarf_sections->size[i] = size; break; } } return 1; } static int macho_add_dwarf_segment(struct backtrace_state *state, int descriptor, off_t offset, unsigned int cmd, const char *psecs, size_t sizesecs, unsigned int nsects, backtrace_error_callback error_callback, void *data, struct dwarf_sections *dwarf_sections) { size_t sec_header_size; size_t secoffset; unsigned int i; switch (cmd) { case MACH_O_LC_SEGMENT: sec_header_size = sizeof(struct macho_section); break; case MACH_O_LC_SEGMENT_64: sec_header_size = sizeof(struct macho_section_64); break; default: abort(); } secoffset = 0; for (i = 0; i < nsects; ++i) { if (secoffset + sec_header_size > sizesecs) { error_callback(data, "section overflow withing segment", 0); return 0; } switch (cmd) { case MACH_O_LC_SEGMENT: { struct macho_section section; memcpy(§ion, psecs + secoffset, sizeof section); macho_add_dwarf_section(state, descriptor, section.sectname, offset + section.offset, section.size, error_callback, data, dwarf_sections); } break; case MACH_O_LC_SEGMENT_64: { struct macho_section_64 section; memcpy(§ion, psecs + secoffset, sizeof section); macho_add_dwarf_section(state, descriptor, section.sectname, offset + section.offset, section.size, error_callback, data, dwarf_sections); } break; default: abort(); } secoffset += sec_header_size; } return 1; } static int macho_symbol_compare(const void *v1, const void *v2) { const struct macho_symbol *m1 = (const struct macho_symbol *)v1; const struct macho_symbol *m2 = (const struct macho_symbol *)v2; if (m1->address < m2->address) return -1; else if (m1->address > m2->address) return 1; else return 0; } static int macho_symbol_search(const void *vkey, const void *ventry) { const uintptr_t *key = (const uintptr_t *)vkey; const struct macho_symbol *entry = (const struct macho_symbol *)ventry; uintptr_t addr; addr = *key; if (addr < entry->address) return -1; else if (entry->name[0] == '\0' && entry->address == ~(uintptr_t)0) return -1; else if ((entry + 1)->name[0] == '\0' && (entry + 1)->address == ~(uintptr_t)0) return -1; else if (addr >= (entry + 1)->address) return 1; else return 0; } static int macho_defined_symbol(uint8_t type) { if ((type & MACH_O_N_STAB) != 0) return 0; if ((type & MACH_O_N_EXT) != 0) return 0; switch (type & MACH_O_N_TYPE) { case MACH_O_N_ABS: return 1; case MACH_O_N_SECT: return 1; default: return 0; } } static int macho_add_symtab(struct backtrace_state *state, int descriptor, uintptr_t base_address, int is_64, off_t symoff, unsigned int nsyms, off_t stroff, unsigned int strsize, backtrace_error_callback error_callback, void *data) { size_t symsize; struct backtrace_view sym_view; int sym_view_valid; struct backtrace_view str_view; int str_view_valid; size_t ndefs; size_t symtaboff; unsigned int i; size_t macho_symbol_size; struct macho_symbol *macho_symbols; unsigned int j; struct macho_syminfo_data *sdata; sym_view_valid = 0; str_view_valid = 0; macho_symbol_size = 0; macho_symbols = NULL; if (is_64) symsize = sizeof(struct macho_nlist_64); else symsize = sizeof(struct macho_nlist); if (!backtrace_get_view(state, descriptor, symoff, nsyms * symsize, error_callback, data, &sym_view)) goto fail; sym_view_valid = 1; if (!backtrace_get_view(state, descriptor, stroff, strsize, error_callback, data, &str_view)) return 0; str_view_valid = 1; ndefs = 0; symtaboff = 0; for (i = 0; i < nsyms; ++i, symtaboff += symsize) { if (is_64) { struct macho_nlist_64 nlist; memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); if (macho_defined_symbol(nlist.n_type)) ++ndefs; } else { struct macho_nlist nlist; memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); if (macho_defined_symbol(nlist.n_type)) ++ndefs; } } macho_symbol_size = (ndefs + 1) * sizeof(struct macho_symbol); macho_symbols = ((struct macho_symbol *)backtrace_alloc( state, macho_symbol_size, error_callback, data)); if (macho_symbols == NULL) goto fail; j = 0; symtaboff = 0; for (i = 0; i < nsyms; ++i, symtaboff += symsize) { uint32_t strx; uint64_t value; const char *name; strx = 0; value = 0; if (is_64) { struct macho_nlist_64 nlist; memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); if (!macho_defined_symbol(nlist.n_type)) continue; strx = nlist.n_strx; value = nlist.n_value; } else { struct macho_nlist nlist; memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); if (!macho_defined_symbol(nlist.n_type)) continue; strx = nlist.n_strx; value = nlist.n_value; } if (strx >= strsize) { error_callback(data, "symbol string index out of range", 0); goto fail; } name = (const char *)str_view.data + strx; if (name[0] == '_') ++name; macho_symbols[j].name = name; macho_symbols[j].address = value + base_address; ++j; } sdata = ((struct macho_syminfo_data *)backtrace_alloc(state, sizeof *sdata, error_callback, data)); if (sdata == NULL) goto fail; backtrace_release_view(state, &sym_view, error_callback, data); sym_view_valid = 0; str_view_valid = 0; macho_symbols[j].name = ""; macho_symbols[j].address = ~(uintptr_t)0; backtrace_qsort(macho_symbols, ndefs + 1, sizeof(struct macho_symbol), macho_symbol_compare); sdata->next = NULL; sdata->symbols = macho_symbols; sdata->count = ndefs; if (!state->threaded) { struct macho_syminfo_data **pp; for (pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data; *pp != NULL; pp = &(*pp)->next) ; *pp = sdata; } else { while (1) { struct macho_syminfo_data **pp; pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data; while (1) { struct macho_syminfo_data *p; p = backtrace_atomic_load_pointer(pp); if (p == NULL) break; pp = &p->next; } if (__sync_bool_compare_and_swap(pp, NULL, sdata)) break; } } return 1; fail: if (macho_symbols != NULL) backtrace_free(state, macho_symbols, macho_symbol_size, error_callback, data); if (sym_view_valid) backtrace_release_view(state, &sym_view, error_callback, data); if (str_view_valid) backtrace_release_view(state, &str_view, error_callback, data); return 0; } static void macho_syminfo( struct backtrace_state *state, uintptr_t addr, backtrace_syminfo_callback callback, backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data) { struct macho_syminfo_data *sdata; struct macho_symbol *sym; sym = NULL; if (!state->threaded) { for (sdata = (struct macho_syminfo_data *)state->syminfo_data; sdata != NULL; sdata = sdata->next) { sym = ((struct macho_symbol *)bsearch(&addr, sdata->symbols, sdata->count, sizeof(struct macho_symbol), macho_symbol_search)); if (sym != NULL) break; } } else { struct macho_syminfo_data **pp; pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data; while (1) { sdata = backtrace_atomic_load_pointer(pp); if (sdata == NULL) break; sym = ((struct macho_symbol *)bsearch(&addr, sdata->symbols, sdata->count, sizeof(struct macho_symbol), macho_symbol_search)); if (sym != NULL) break; pp = &sdata->next; } } if (sym == NULL) callback(data, addr, NULL, 0, 0); else callback(data, addr, sym->name, sym->address, 0); } static int macho_add_fat(struct backtrace_state *state, const char *filename, int descriptor, int swapped, off_t offset, const unsigned char *match_uuid, uintptr_t base_address, int skip_symtab, uint32_t nfat_arch, int is_64, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym) { int arch_view_valid; unsigned int cputype; size_t arch_size; struct backtrace_view arch_view; unsigned int i; arch_view_valid = 0; #if defined(__x86_64__) cputype = MACH_O_CPU_TYPE_X86_64; #elif defined(__i386__) cputype = MACH_O_CPU_TYPE_X86; #elif defined(__aarch64__) cputype = MACH_O_CPU_TYPE_ARM64; #elif defined(__arm__) cputype = MACH_O_CPU_TYPE_ARM; #elif defined(__ppc__) cputype = MACH_O_CPU_TYPE_PPC; #elif defined(__ppc64__) cputype = MACH_O_CPU_TYPE_PPC64; #else error_callback(data, "unknown Mach-O architecture", 0); goto fail; #endif if (is_64) arch_size = sizeof(struct macho_fat_arch_64); else arch_size = sizeof(struct macho_fat_arch); if (!backtrace_get_view(state, descriptor, offset, nfat_arch * arch_size, error_callback, data, &arch_view)) goto fail; for (i = 0; i < nfat_arch; ++i) { uint32_t fcputype; uint64_t foffset; if (is_64) { struct macho_fat_arch_64 fat_arch_64; memcpy(&fat_arch_64, (const char *)arch_view.data + i * arch_size, arch_size); fcputype = fat_arch_64.cputype; foffset = fat_arch_64.offset; if (swapped) { fcputype = __builtin_bswap32(fcputype); foffset = __builtin_bswap64(foffset); } } else { struct macho_fat_arch fat_arch_32; memcpy(&fat_arch_32, (const char *)arch_view.data + i * arch_size, arch_size); fcputype = fat_arch_32.cputype; foffset = (uint64_t)fat_arch_32.offset; if (swapped) { fcputype = __builtin_bswap32(fcputype); foffset = (uint64_t)__builtin_bswap32((uint32_t)foffset); } } if (fcputype == cputype) { backtrace_release_view(state, &arch_view, error_callback, data); return macho_add(state, filename, descriptor, foffset, match_uuid, base_address, skip_symtab, error_callback, data, fileline_fn, found_sym); } } error_callback(data, "could not find executable in fat file", 0); fail: if (arch_view_valid) backtrace_release_view(state, &arch_view, error_callback, data); if (descriptor != -1) backtrace_close(descriptor, error_callback, data); return 0; } static int macho_add_dsym(struct backtrace_state *state, const char *filename, uintptr_t base_address, const unsigned char *uuid, backtrace_error_callback error_callback, void *data, fileline *fileline_fn) { const char *p; const char *dirname; char *diralc; size_t dirnamelen; const char *basename; size_t basenamelen; const char *dsymsuffixdir; size_t dsymsuffixdirlen; size_t dsymlen; char *dsym; char *ps; int d; int does_not_exist; int dummy_found_sym; diralc = NULL; dirnamelen = 0; dsym = NULL; dsymlen = 0; p = strrchr(filename, '/'); if (p == NULL) { dirname = "."; dirnamelen = 1; basename = filename; basenamelen = strlen(basename); diralc = NULL; } else { dirnamelen = p - filename; diralc = backtrace_alloc(state, dirnamelen + 1, error_callback, data); if (diralc == NULL) goto fail; memcpy(diralc, filename, dirnamelen); diralc[dirnamelen] = '\0'; dirname = diralc; basename = p + 1; basenamelen = strlen(basename); } dsymsuffixdir = ".dSYM/Contents/Resources/DWARF/"; dsymsuffixdirlen = strlen(dsymsuffixdir); dsymlen = (dirnamelen + 1 + basenamelen + dsymsuffixdirlen + basenamelen + 1); dsym = backtrace_alloc(state, dsymlen, error_callback, data); if (dsym == NULL) goto fail; ps = dsym; memcpy(ps, dirname, dirnamelen); ps += dirnamelen; *ps++ = '/'; memcpy(ps, basename, basenamelen); ps += basenamelen; memcpy(ps, dsymsuffixdir, dsymsuffixdirlen); ps += dsymsuffixdirlen; memcpy(ps, basename, basenamelen); ps += basenamelen; *ps = '\0'; if (diralc != NULL) { backtrace_free(state, diralc, dirnamelen + 1, error_callback, data); diralc = NULL; } d = backtrace_open(dsym, error_callback, data, &does_not_exist); if (d < 0) { backtrace_free(state, dsym, dsymlen, error_callback, data); return 1; } if (!macho_add(state, dsym, d, 0, uuid, base_address, 1, error_callback, data, fileline_fn, &dummy_found_sym)) goto fail; backtrace_free(state, dsym, dsymlen, error_callback, data); return 1; fail: if (dsym != NULL) backtrace_free(state, dsym, dsymlen, error_callback, data); if (diralc != NULL) backtrace_free(state, diralc, dirnamelen, error_callback, data); return 0; } static int macho_add(struct backtrace_state *state, const char *filename, int descriptor, off_t offset, const unsigned char *match_uuid, uintptr_t base_address, int skip_symtab, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym) { struct backtrace_view header_view; struct macho_header_32 header; off_t hdroffset; int is_64; struct backtrace_view cmds_view; int cmds_view_valid; struct dwarf_sections dwarf_sections; int have_dwarf; unsigned char uuid[MACH_O_UUID_LEN]; int have_uuid; size_t cmdoffset; unsigned int i; *found_sym = 0; cmds_view_valid = 0; if (!backtrace_get_view(state, descriptor, offset, sizeof(struct macho_header_32), error_callback, data, &header_view)) goto fail; memcpy(&header, header_view.data, sizeof header); backtrace_release_view(state, &header_view, error_callback, data); switch (header.magic) { case MACH_O_MH_MAGIC_32: is_64 = 0; hdroffset = offset + sizeof(struct macho_header_32); break; case MACH_O_MH_MAGIC_64: is_64 = 1; hdroffset = offset + sizeof(struct macho_header_64); break; case MACH_O_MH_MAGIC_FAT: case MACH_O_MH_MAGIC_FAT_64: { struct macho_header_fat fat_header; hdroffset = offset + sizeof(struct macho_header_fat); memcpy(&fat_header, &header, sizeof fat_header); return macho_add_fat(state, filename, descriptor, 0, hdroffset, match_uuid, base_address, skip_symtab, fat_header.nfat_arch, header.magic == MACH_O_MH_MAGIC_FAT_64, error_callback, data, fileline_fn, found_sym); } case MACH_O_MH_CIGAM_FAT: case MACH_O_MH_CIGAM_FAT_64: { struct macho_header_fat fat_header; uint32_t nfat_arch; hdroffset = offset + sizeof(struct macho_header_fat); memcpy(&fat_header, &header, sizeof fat_header); nfat_arch = __builtin_bswap32(fat_header.nfat_arch); return macho_add_fat(state, filename, descriptor, 1, hdroffset, match_uuid, base_address, skip_symtab, nfat_arch, header.magic == MACH_O_MH_CIGAM_FAT_64, error_callback, data, fileline_fn, found_sym); } default: error_callback(data, "executable file is not in Mach-O format", 0); goto fail; } switch (header.filetype) { case MACH_O_MH_EXECUTE: case MACH_O_MH_DYLIB: case MACH_O_MH_DSYM: break; default: error_callback(data, "executable file is not an executable", 0); goto fail; } if (!backtrace_get_view(state, descriptor, hdroffset, header.sizeofcmds, error_callback, data, &cmds_view)) goto fail; cmds_view_valid = 1; memset(&dwarf_sections, 0, sizeof dwarf_sections); have_dwarf = 0; memset(&uuid, 0, sizeof uuid); have_uuid = 0; cmdoffset = 0; for (i = 0; i < header.ncmds; ++i) { const char *pcmd; struct macho_load_command load_command; if (cmdoffset + sizeof load_command > header.sizeofcmds) break; pcmd = (const char *)cmds_view.data + cmdoffset; memcpy(&load_command, pcmd, sizeof load_command); switch (load_command.cmd) { case MACH_O_LC_SEGMENT: { struct macho_segment_command segcmd; memcpy(&segcmd, pcmd, sizeof segcmd); if (memcmp(segcmd.segname, "__DWARF\0\0\0\0\0\0\0\0\0", MACH_O_NAMELEN) == 0) { if (!macho_add_dwarf_segment( state, descriptor, offset, load_command.cmd, pcmd + sizeof segcmd, (load_command.cmdsize - sizeof segcmd), segcmd.nsects, error_callback, data, &dwarf_sections)) goto fail; have_dwarf = 1; } } break; case MACH_O_LC_SEGMENT_64: { struct macho_segment_64_command segcmd; memcpy(&segcmd, pcmd, sizeof segcmd); if (memcmp(segcmd.segname, "__DWARF\0\0\0\0\0\0\0\0\0", MACH_O_NAMELEN) == 0) { if (!macho_add_dwarf_segment( state, descriptor, offset, load_command.cmd, pcmd + sizeof segcmd, (load_command.cmdsize - sizeof segcmd), segcmd.nsects, error_callback, data, &dwarf_sections)) goto fail; have_dwarf = 1; } } break; case MACH_O_LC_SYMTAB: if (!skip_symtab) { struct macho_symtab_command symcmd; memcpy(&symcmd, pcmd, sizeof symcmd); if (!macho_add_symtab(state, descriptor, base_address, is_64, offset + symcmd.symoff, symcmd.nsyms, offset + symcmd.stroff, symcmd.strsize, error_callback, data)) goto fail; *found_sym = 1; } break; case MACH_O_LC_UUID: { struct macho_uuid_command uuidcmd; memcpy(&uuidcmd, pcmd, sizeof uuidcmd); memcpy(&uuid[0], &uuidcmd.uuid[0], MACH_O_UUID_LEN); have_uuid = 1; } break; default: break; } cmdoffset += load_command.cmdsize; } if (!backtrace_close(descriptor, error_callback, data)) goto fail; descriptor = -1; backtrace_release_view(state, &cmds_view, error_callback, data); cmds_view_valid = 0; if (match_uuid != NULL) { if (!have_uuid || memcmp(match_uuid, &uuid[0], MACH_O_UUID_LEN) != 0) return 1; } if (have_dwarf) { int is_big_endian; is_big_endian = 0; #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ is_big_endian = 1; #endif #endif if (!backtrace_dwarf_add(state, base_address, &dwarf_sections, is_big_endian, NULL, error_callback, data, fileline_fn, NULL)) goto fail; } if (!have_dwarf && have_uuid) { if (!macho_add_dsym(state, filename, base_address, &uuid[0], error_callback, data, fileline_fn)) goto fail; } return 1; fail: if (cmds_view_valid) backtrace_release_view(state, &cmds_view, error_callback, data); if (descriptor != -1) backtrace_close(descriptor, error_callback, data); return 0; } #ifdef HAVE_MACH_O_DYLD_H int backtrace_initialize(struct backtrace_state *state, const char *filename, int descriptor, backtrace_error_callback error_callback, void *data, fileline *fileline_fn) { uint32_t c; uint32_t i; int closed_descriptor; int found_sym; fileline macho_fileline_fn; closed_descriptor = 0; found_sym = 0; macho_fileline_fn = macho_nodebug; c = _dyld_image_count(); for (i = 0; i < c; ++i) { uintptr_t base_address; const char *name; int d; fileline mff; int mfs; name = _dyld_get_image_name(i); if (name == NULL) continue; if (strcmp(name, filename) == 0 && !closed_descriptor) { d = descriptor; closed_descriptor = 1; } else { int does_not_exist; d = backtrace_open(name, error_callback, data, &does_not_exist); if (d < 0) continue; } base_address = _dyld_get_image_vmaddr_slide(i); mff = macho_nodebug; if (!macho_add(state, name, d, 0, NULL, base_address, 0, error_callback, data, &mff, &mfs)) return 0; if (mff != macho_nodebug) macho_fileline_fn = mff; if (mfs) found_sym = 1; } if (!closed_descriptor) backtrace_close(descriptor, error_callback, data); if (!state->threaded) { if (found_sym) state->syminfo_fn = macho_syminfo; else if (state->syminfo_fn == NULL) state->syminfo_fn = macho_nosyms; } else { if (found_sym) backtrace_atomic_store_pointer(&state->syminfo_fn, macho_syminfo); else (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL, macho_nosyms); } if (!state->threaded) *fileline_fn = state->fileline_fn; else *fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn); if (*fileline_fn == NULL || *fileline_fn == macho_nodebug) *fileline_fn = macho_fileline_fn; return 1; } #else int backtrace_initialize(struct backtrace_state *state, const char *filename, int descriptor, backtrace_error_callback error_callback, void *data, fileline *fileline_fn) { fileline macho_fileline_fn; int found_sym; macho_fileline_fn = macho_nodebug; if (!macho_add(state, filename, descriptor, 0, NULL, 0, 0, error_callback, data, &macho_fileline_fn, &found_sym)) return 0; if (!state->threaded) { if (found_sym) state->syminfo_fn = macho_syminfo; else if (state->syminfo_fn == NULL) state->syminfo_fn = macho_nosyms; } else { if (found_sym) backtrace_atomic_store_pointer(&state->syminfo_fn, macho_syminfo); else (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL, macho_nosyms); } if (!state->threaded) *fileline_fn = state->fileline_fn; else *fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn); if (*fileline_fn == NULL || *fileline_fn == macho_nodebug) *fileline_fn = macho_fileline_fn; return 1; } #endif // mmapio.c: #include #include #include #include #ifndef HAVE_DECL_GETPAGESIZE extern int getpagesize(void); #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif int backtrace_get_view(struct backtrace_state *state ATTRIBUTE_UNUSED, int descriptor, off_t offset, uint64_t size, backtrace_error_callback error_callback, void *data, struct backtrace_view *view) { size_t pagesize; unsigned int inpage; off_t pageoff; void *map; if ((uint64_t)(size_t)size != size) { error_callback(data, "file size too large", 0); return 0; } pagesize = getpagesize(); inpage = offset % pagesize; pageoff = offset - inpage; size += inpage; size = (size + (pagesize - 1)) & ~(pagesize - 1); map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff); if (map == MAP_FAILED) { error_callback(data, "mmap", errno); return 0; } view->data = (char *)map + inpage; view->base = map; view->len = size; return 1; } void backtrace_release_view(struct backtrace_state *state ATTRIBUTE_UNUSED, struct backtrace_view *view, backtrace_error_callback error_callback, void *data) { union { const void *cv; void *v; } const_cast; const_cast.cv = view->base; if (munmap(const_cast.v, view->len) < 0) error_callback(data, "munmap", errno); } // mmap.c: #include #include #include #include #include #include #ifndef HAVE_DECL_GETPAGESIZE extern int getpagesize(void); #endif #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif struct backtrace_freelist_struct { struct backtrace_freelist_struct *next; size_t size; }; static void backtrace_free_locked(struct backtrace_state *state, void *addr, size_t size) { if (size >= sizeof(struct backtrace_freelist_struct)) { size_t c; struct backtrace_freelist_struct **ppsmall; struct backtrace_freelist_struct **pp; struct backtrace_freelist_struct *p; c = 0; ppsmall = NULL; for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) { if (ppsmall == NULL || (*pp)->size < (*ppsmall)->size) ppsmall = pp; ++c; } if (c >= 16) { if (size <= (*ppsmall)->size) return; *ppsmall = (*ppsmall)->next; } p = (struct backtrace_freelist_struct *)addr; p->next = state->freelist; p->size = size; state->freelist = p; } } void *backtrace_alloc(struct backtrace_state *state, size_t size, backtrace_error_callback error_callback, void *data) { void *ret; int locked; struct backtrace_freelist_struct **pp; size_t pagesize; size_t asksize; void *page; ret = NULL; if (!state->threaded) locked = 1; else locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0; if (locked) { for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) { if ((*pp)->size >= size) { struct backtrace_freelist_struct *p; p = *pp; *pp = p->next; size = (size + 7) & ~(size_t)7; if (size < p->size) backtrace_free_locked(state, (char *)p + size, p->size - size); ret = (void *)p; break; } } if (state->threaded) __sync_lock_release(&state->lock_alloc); } if (ret == NULL) { pagesize = getpagesize(); asksize = (size + pagesize - 1) & ~(pagesize - 1); page = mmap(NULL, asksize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (page == MAP_FAILED) { if (error_callback) error_callback(data, "mmap", errno); } else { size = (size + 7) & ~(size_t)7; if (size < asksize) backtrace_free(state, (char *)page + size, asksize - size, error_callback, data); ret = page; } } return ret; } void backtrace_free(struct backtrace_state *state, void *addr, size_t size, backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data ATTRIBUTE_UNUSED) { int locked; if (size >= 16 * 4096) { size_t pagesize; pagesize = getpagesize(); if (((uintptr_t)addr & (pagesize - 1)) == 0 && (size & (pagesize - 1)) == 0) { if (munmap(addr, size) == 0) return; } } if (!state->threaded) locked = 1; else locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0; if (locked) { backtrace_free_locked(state, addr, size); if (state->threaded) __sync_lock_release(&state->lock_alloc); } } void *backtrace_vector_grow(struct backtrace_state *state, size_t size, backtrace_error_callback error_callback, void *data, struct backtrace_vector *vec) { void *ret; if (size > vec->alc) { size_t pagesize; size_t alc; void *base; pagesize = getpagesize(); alc = vec->size + size; if (vec->size == 0) alc = 16 * size; else if (alc < pagesize) { alc *= 2; if (alc > pagesize) alc = pagesize; } else { alc *= 2; alc = (alc + pagesize - 1) & ~(pagesize - 1); } base = backtrace_alloc(state, alc, error_callback, data); if (base == NULL) return NULL; if (vec->base != NULL) { memcpy(base, vec->base, vec->size); backtrace_free(state, vec->base, vec->size + vec->alc, error_callback, data); } vec->base = base; vec->alc = alc - vec->size; } ret = (char *)vec->base + vec->size; vec->size += size; vec->alc -= size; return ret; } void *backtrace_vector_finish(struct backtrace_state *state ATTRIBUTE_UNUSED, struct backtrace_vector *vec, backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data ATTRIBUTE_UNUSED) { void *ret; ret = vec->base; vec->base = (char *)vec->base + vec->size; vec->size = 0; return ret; } int backtrace_vector_release(struct backtrace_state *state, struct backtrace_vector *vec, backtrace_error_callback error_callback, void *data) { size_t size; size_t alc; size_t aligned; size = vec->size; alc = vec->alc; aligned = (size + 7) & ~(size_t)7; alc -= aligned - size; backtrace_free(state, (char *)vec->base + aligned, alc, error_callback, data); vec->alc = 0; if (vec->size == 0) vec->base = NULL; return 1; }