12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361 |
- /* elf.c -- Get debug data from a Mach-O file for backtraces.
- Copyright (C) 2020-2024 Free Software Foundation, Inc.
- Written by Ian Lance Taylor, Google.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
- (1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- (2) Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- (3) The name of the author may not be used to
- endorse or promote products derived from this software without
- specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE. */
- #include "config.h"
- #include <sys/types.h>
- #include <dirent.h>
- #include <stdlib.h>
- #include <string.h>
- #ifdef HAVE_MACH_O_DYLD_H
- #include <mach-o/dyld.h>
- #endif
- #include "backtrace.h"
- #include "internal.h"
- /* Mach-O file header for a 32-bit executable. */
- struct macho_header_32
- {
- uint32_t magic; /* Magic number (MACH_O_MAGIC_32) */
- uint32_t cputype; /* CPU type */
- uint32_t cpusubtype; /* CPU subtype */
- uint32_t filetype; /* Type of file (object, executable) */
- uint32_t ncmds; /* Number of load commands */
- uint32_t sizeofcmds; /* Total size of load commands */
- uint32_t flags; /* Flags for special features */
- };
- /* Mach-O file header for a 64-bit executable. */
- struct macho_header_64
- {
- uint32_t magic; /* Magic number (MACH_O_MAGIC_64) */
- uint32_t cputype; /* CPU type */
- uint32_t cpusubtype; /* CPU subtype */
- uint32_t filetype; /* Type of file (object, executable) */
- uint32_t ncmds; /* Number of load commands */
- uint32_t sizeofcmds; /* Total size of load commands */
- uint32_t flags; /* Flags for special features */
- uint32_t reserved; /* Reserved */
- };
- /* Mach-O file header for a fat executable. */
- struct macho_header_fat
- {
- uint32_t magic; /* Magic number (MACH_O_MH_(MAGIC|CIGAM)_FAT(_64)?) */
- uint32_t nfat_arch; /* Number of components */
- };
- /* Values for the header magic field. */
- #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
- /* Value for the header filetype field. */
- #define MACH_O_MH_EXECUTE 0x02
- #define MACH_O_MH_DYLIB 0x06
- #define MACH_O_MH_DSYM 0x0a
- /* A component of a fat file. A fat file starts with a
- macho_header_fat followed by nfat_arch instances of this
- struct. */
- struct macho_fat_arch
- {
- uint32_t cputype; /* CPU type */
- uint32_t cpusubtype; /* CPU subtype */
- uint32_t offset; /* File offset of this entry */
- uint32_t size; /* Size of this entry */
- uint32_t align; /* Alignment of this entry */
- };
- /* A component of a 64-bit fat file. This is used if the magic field
- is MAGIC_FAT_64. This is only used when some file size or file
- offset is too large to represent in the 32-bit format. */
- struct macho_fat_arch_64
- {
- uint32_t cputype; /* CPU type */
- uint32_t cpusubtype; /* CPU subtype */
- uint64_t offset; /* File offset of this entry */
- uint64_t size; /* Size of this entry */
- uint32_t align; /* Alignment of this entry */
- uint32_t reserved; /* Reserved */
- };
- /* Values for the fat_arch cputype field (and the header cputype
- field). */
- #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)
- /* The header of a load command. */
- struct macho_load_command
- {
- uint32_t cmd; /* The type of load command */
- uint32_t cmdsize; /* Size in bytes of the entire command */
- };
- /* Values for the load_command cmd field. */
- #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
- /* The length of a section of segment name. */
- #define MACH_O_NAMELEN (16)
- /* LC_SEGMENT load command. */
- struct macho_segment_command
- {
- uint32_t cmd; /* The type of load command (LC_SEGMENT) */
- uint32_t cmdsize; /* Size in bytes of the entire command */
- char segname[MACH_O_NAMELEN]; /* Segment name */
- uint32_t vmaddr; /* Virtual memory address */
- uint32_t vmsize; /* Virtual memory size */
- uint32_t fileoff; /* Offset of data to be mapped */
- uint32_t filesize; /* Size of data in file */
- uint32_t maxprot; /* Maximum permitted virtual protection */
- uint32_t initprot; /* Initial virtual memory protection */
- uint32_t nsects; /* Number of sections in this segment */
- uint32_t flags; /* Flags */
- };
- /* LC_SEGMENT_64 load command. */
- struct macho_segment_64_command
- {
- uint32_t cmd; /* The type of load command (LC_SEGMENT) */
- uint32_t cmdsize; /* Size in bytes of the entire command */
- char segname[MACH_O_NAMELEN]; /* Segment name */
- uint64_t vmaddr; /* Virtual memory address */
- uint64_t vmsize; /* Virtual memory size */
- uint64_t fileoff; /* Offset of data to be mapped */
- uint64_t filesize; /* Size of data in file */
- uint32_t maxprot; /* Maximum permitted virtual protection */
- uint32_t initprot; /* Initial virtual memory protection */
- uint32_t nsects; /* Number of sections in this segment */
- uint32_t flags; /* Flags */
- };
- /* LC_SYMTAB load command. */
- struct macho_symtab_command
- {
- uint32_t cmd; /* The type of load command (LC_SEGMENT) */
- uint32_t cmdsize; /* Size in bytes of the entire command */
- uint32_t symoff; /* File offset of symbol table */
- uint32_t nsyms; /* Number of symbols */
- uint32_t stroff; /* File offset of string table */
- uint32_t strsize; /* String table size */
- };
- /* The length of a Mach-O uuid. */
- #define MACH_O_UUID_LEN (16)
- /* LC_UUID load command. */
- struct macho_uuid_command
- {
- uint32_t cmd; /* Type of load command (LC_UUID) */
- uint32_t cmdsize; /* Size in bytes of command */
- unsigned char uuid[MACH_O_UUID_LEN]; /* UUID */
- };
- /* 32-bit section header within a LC_SEGMENT segment. */
- struct macho_section
- {
- char sectname[MACH_O_NAMELEN]; /* Section name */
- char segment[MACH_O_NAMELEN]; /* Segment of this section */
- uint32_t addr; /* Address in memory */
- uint32_t size; /* Section size */
- uint32_t offset; /* File offset */
- uint32_t align; /* Log2 of section alignment */
- uint32_t reloff; /* File offset of relocations */
- uint32_t nreloc; /* Number of relocs for this section */
- uint32_t flags; /* Flags */
- uint32_t reserved1;
- uint32_t reserved2;
- };
- /* 64-bit section header within a LC_SEGMENT_64 segment. */
- struct macho_section_64
- {
- char sectname[MACH_O_NAMELEN]; /* Section name */
- char segment[MACH_O_NAMELEN]; /* Segment of this section */
- uint64_t addr; /* Address in memory */
- uint64_t size; /* Section size */
- uint32_t offset; /* File offset */
- uint32_t align; /* Log2 of section alignment */
- uint32_t reloff; /* File offset of section relocations */
- uint32_t nreloc; /* Number of relocs for this section */
- uint32_t flags; /* Flags */
- uint32_t reserved1;
- uint32_t reserved2;
- uint32_t reserved3;
- };
- /* 32-bit symbol data. */
- struct macho_nlist
- {
- uint32_t n_strx; /* Index of name in string table */
- uint8_t n_type; /* Type flag */
- uint8_t n_sect; /* Section number */
- uint16_t n_desc; /* Stabs description field */
- uint32_t n_value; /* Value */
- };
- /* 64-bit symbol data. */
- struct macho_nlist_64
- {
- uint32_t n_strx; /* Index of name in string table */
- uint8_t n_type; /* Type flag */
- uint8_t n_sect; /* Section number */
- uint16_t n_desc; /* Stabs description field */
- uint64_t n_value; /* Value */
- };
- /* Value found in nlist n_type field. */
- #define MACH_O_N_STAB 0xe0 /* Stabs debugging symbol */
- #define MACH_O_N_TYPE 0x0e /* Mask for type bits */
- /* Values found after masking with MACH_O_N_TYPE. */
- #define MACH_O_N_UNDF 0x00 /* Undefined symbol */
- #define MACH_O_N_ABS 0x02 /* Absolute symbol */
- #define MACH_O_N_SECT 0x0e /* Defined in section from n_sect field */
- /* Information we keep for a Mach-O symbol. */
- struct macho_symbol
- {
- const char *name; /* Symbol name */
- uintptr_t address; /* Symbol address */
- };
- /* Information to pass to macho_syminfo. */
- struct macho_syminfo_data
- {
- struct macho_syminfo_data *next; /* Next module */
- struct macho_symbol *symbols; /* Symbols sorted by address */
- size_t count; /* Number of symbols */
- };
- /* Names of sections, indexed by enum dwarf_section in internal.h. */
- static const char * const dwarf_section_names[DEBUG_MAX] =
- {
- "__debug_info",
- "__debug_line",
- "__debug_abbrev",
- "__debug_ranges",
- "__debug_str",
- "", /* DEBUG_ADDR */
- "__debug_str_offs",
- "", /* DEBUG_LINE_STR */
- "__debug_rnglists"
- };
- /* Forward declaration. */
- static int macho_add (struct backtrace_state *, const char *, int, off_t,
- const unsigned char *, struct libbacktrace_base_address,
- int, backtrace_error_callback, void *, fileline *,
- int *);
- /* A dummy callback function used when we can't find any debug info. */
- 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 (make sure to compile with -g; may need to run dsymutil)", -1);
- return 0;
- }
- /* A dummy callback function used when we can't find a symbol
- table. */
- 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);
- }
- /* Add a single DWARF section to DWARF_SECTIONS, if we need the
- section. Returns 1 on success, 0 on failure. */
- 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;
- /* FIXME: Perhaps it would be better to try to use a single
- view to read all the DWARF data, as we try to do for
- ELF. */
- 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;
- }
- /* Collect DWARF sections from a DWARF segment. Returns 1 on success,
- 0 on failure. */
- 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;
- }
- /* Compare struct macho_symbol for qsort. */
- 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;
- }
- /* Compare an address against a macho_symbol for bsearch. We allocate
- one extra entry in the array so that this can safely look at the
- next entry. */
- 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;
- }
- /* Return whether the symbol type field indicates a symbol table entry
- that we care about: a function or data symbol. */
- static int
- macho_defined_symbol (uint8_t type)
- {
- if ((type & MACH_O_N_STAB) != 0)
- return 0;
- switch (type & MACH_O_N_TYPE)
- {
- case MACH_O_N_UNDF:
- return 0;
- case MACH_O_N_ABS:
- return 1;
- case MACH_O_N_SECT:
- return 1;
- default:
- return 0;
- }
- }
- /* Add symbol table information for a Mach-O file. */
- static int
- macho_add_symtab (struct backtrace_state *state, int descriptor,
- struct libbacktrace_base_address 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;
- }
- }
- /* Add 1 to ndefs to make room for a sentinel. */
- 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 = libbacktrace_add_base (value, base_address);
- ++j;
- }
- sdata = ((struct macho_syminfo_data *)
- backtrace_alloc (state, sizeof *sdata, error_callback, data));
- if (sdata == NULL)
- goto fail;
- /* We need to keep the string table since it holds the names, but we
- can release the symbol table. */
- backtrace_release_view (state, &sym_view, error_callback, data);
- sym_view_valid = 0;
- str_view_valid = 0;
- /* Add a trailing sentinel symbol. */
- 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;
- }
- /* Return the symbol name and value for an ADDR. */
- 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);
- }
- /* Look through a fat file to find the relevant executable. Returns 1
- on success, 0 on failure (in both cases descriptor is closed). */
- static int
- macho_add_fat (struct backtrace_state *state, const char *filename,
- int descriptor, int swapped, off_t offset,
- const unsigned char *match_uuid,
- struct libbacktrace_base_address 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)
- {
- /* FIXME: What about cpusubtype? */
- 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;
- }
- /* Look for the dsym file for FILENAME. This is called if FILENAME
- does not have debug info or a symbol table. Returns 1 on success,
- 0 on failure. */
- static int
- macho_add_dsym (struct backtrace_state *state, const char *filename,
- struct libbacktrace_base_address 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)
- {
- /* The file does not exist, so we can't read the debug info.
- Just return success. */
- 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;
- }
- /* Add the backtrace data for a Macho-O file. Returns 1 on success, 0
- on failure (in both cases descriptor is closed).
- FILENAME: the name of the executable.
- DESCRIPTOR: an open descriptor for the executable, closed here.
- OFFSET: the offset within the file of this executable, for fat files.
- MATCH_UUID: if not NULL, UUID that must match.
- BASE_ADDRESS: the load address of the executable.
- SKIP_SYMTAB: if non-zero, ignore the symbol table; used for dSYM files.
- FILELINE_FN: set to the fileline function, by backtrace_dwarf_add.
- FOUND_SYM: set to non-zero if we found the symbol table.
- */
- static int
- macho_add (struct backtrace_state *state, const char *filename, int descriptor,
- off_t offset, const unsigned char *match_uuid,
- struct libbacktrace_base_address 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;
- /* The 32-bit and 64-bit file headers start out the same, so we can
- just always read the 32-bit version. A fat header is shorter but
- it will always be followed by data, so it's OK to read extra. */
- 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 we don't have a UUID, or it doesn't match, just ignore
- this file. */
- 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
- /* Initialize the backtrace data we need from a Mach-O executable
- using the dyld support functions. This closes descriptor. */
- 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)
- {
- struct libbacktrace_base_address 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.m = _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))
- continue;
- 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 /* !defined (HAVE_MACH_O_DYLD_H) */
- /* Initialize the backtrace data we need from a Mach-O executable
- without using the dyld support functions. This closes
- descriptor. */
- 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;
- struct libbacktrace_base_address zero_base_address;
- int found_sym;
- macho_fileline_fn = macho_nodebug;
- memset (&zero_base_address, 0, sizeof zero_base_address);
- if (!macho_add (state, filename, descriptor, 0, NULL, zero_base_address, 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 /* !defined (HAVE_MACH_O_DYLD_H) */
|