1299 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1299 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
| // macho.c:
 | |
| #include <dirent.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/types.h>
 | |
| 
 | |
| #ifdef HAVE_MACH_O_DYLD_H
 | |
| #include <mach-o/dyld.h>
 | |
| #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 <errno.h>
 | |
| #include <sys/mman.h>
 | |
| #include <sys/types.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #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 <errno.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/mman.h>
 | |
| #include <sys/types.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #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;
 | |
| }
 |