152
|
1 /* elf.c -- Get debug data from a Mach-O file for backtraces.
|
|
2 Copyright (C) 2020 Free Software Foundation, Inc.
|
|
3 Written by Ian Lance Taylor, Google.
|
|
4
|
|
5 Redistribution and use in source and binary forms, with or without
|
|
6 modification, are permitted provided that the following conditions are
|
|
7 met:
|
|
8
|
|
9 (1) Redistributions of source code must retain the above copyright
|
|
10 notice, this list of conditions and the following disclaimer.
|
|
11
|
|
12 (2) Redistributions in binary form must reproduce the above copyright
|
|
13 notice, this list of conditions and the following disclaimer in
|
|
14 the documentation and/or other materials provided with the
|
|
15 distribution.
|
|
16
|
|
17 (3) The name of the author may not be used to
|
|
18 endorse or promote products derived from this software without
|
|
19 specific prior written permission.
|
|
20
|
|
21 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
22 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
24 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
25 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
29 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
30 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
31 POSSIBILITY OF SUCH DAMAGE. */
|
|
32
|
|
33 #include "config.h"
|
|
34
|
|
35 #include <sys/types.h>
|
|
36 #include <dirent.h>
|
|
37 #include <stdlib.h>
|
|
38 #include <string.h>
|
|
39
|
|
40 #ifdef HAVE_MACH_O_DYLD_H
|
|
41 #include <mach-o/dyld.h>
|
|
42 #endif
|
|
43
|
|
44 #include "backtrace.h"
|
|
45 #include "internal.h"
|
|
46
|
|
47 /* Mach-O file header for a 32-bit executable. */
|
|
48
|
|
49 struct macho_header_32
|
|
50 {
|
|
51 uint32_t magic; /* Magic number (MACH_O_MAGIC_32) */
|
|
52 uint32_t cputype; /* CPU type */
|
|
53 uint32_t cpusubtype; /* CPU subtype */
|
|
54 uint32_t filetype; /* Type of file (object, executable) */
|
|
55 uint32_t ncmds; /* Number of load commands */
|
|
56 uint32_t sizeofcmds; /* Total size of load commands */
|
|
57 uint32_t flags; /* Flags for special features */
|
|
58 };
|
|
59
|
|
60 /* Mach-O file header for a 64-bit executable. */
|
|
61
|
|
62 struct macho_header_64
|
|
63 {
|
|
64 uint32_t magic; /* Magic number (MACH_O_MAGIC_64) */
|
|
65 uint32_t cputype; /* CPU type */
|
|
66 uint32_t cpusubtype; /* CPU subtype */
|
|
67 uint32_t filetype; /* Type of file (object, executable) */
|
|
68 uint32_t ncmds; /* Number of load commands */
|
|
69 uint32_t sizeofcmds; /* Total size of load commands */
|
|
70 uint32_t flags; /* Flags for special features */
|
|
71 uint32_t reserved; /* Reserved */
|
|
72 };
|
|
73
|
|
74 /* Mach-O file header for a fat executable. */
|
|
75
|
|
76 struct macho_header_fat
|
|
77 {
|
|
78 uint32_t magic; /* Magic number (MACH_O_MH_MAGIC_FAT) */
|
|
79 uint32_t nfat_arch; /* Number of components */
|
|
80 };
|
|
81
|
|
82 /* Values for the header magic field. */
|
|
83
|
|
84 #define MACH_O_MH_MAGIC_32 0xfeedface
|
|
85 #define MACH_O_MH_MAGIC_64 0xfeedfacf
|
|
86 #define MACH_O_MH_MAGIC_FAT 0xcafebabe
|
|
87 #define MACH_O_MH_CIGAM_FAT 0xbebafeca
|
|
88
|
|
89 /* Value for the header filetype field. */
|
|
90
|
|
91 #define MACH_O_MH_EXECUTE 0x02
|
|
92 #define MACH_O_MH_DYLIB 0x06
|
|
93 #define MACH_O_MH_DSYM 0x0a
|
|
94
|
|
95 /* A component of a fat file. A fat file starts with a
|
|
96 macho_header_fat followed by nfat_arch instances of this
|
|
97 struct. */
|
|
98
|
|
99 struct macho_fat_arch
|
|
100 {
|
|
101 uint32_t cputype; /* CPU type */
|
|
102 uint32_t cpusubtype; /* CPU subtype */
|
|
103 uint32_t offset; /* File offset of this entry */
|
|
104 uint32_t size; /* Size of this entry */
|
|
105 uint32_t align; /* Alignment of this entry */
|
|
106 };
|
|
107
|
|
108 /* Values for the fat_arch cputype field (and the header cputype
|
|
109 field). */
|
|
110
|
|
111 #define MACH_O_CPU_ARCH_ABI64 0x01000000
|
|
112
|
|
113 #define MACH_O_CPU_TYPE_X86 7
|
|
114 #define MACH_O_CPU_TYPE_ARM 12
|
|
115
|
|
116 #define MACH_O_CPU_TYPE_X86_64 (MACH_O_CPU_TYPE_X86 | MACH_O_CPU_ARCH_ABI64)
|
|
117 #define MACH_O_CPU_TYPE_ARM64 (MACH_O_CPU_TYPE_ARM | MACH_O_CPU_ARCH_ABI64)
|
|
118
|
|
119 /* The header of a load command. */
|
|
120
|
|
121 struct macho_load_command
|
|
122 {
|
|
123 uint32_t cmd; /* The type of load command */
|
|
124 uint32_t cmdsize; /* Size in bytes of the entire command */
|
|
125 };
|
|
126
|
|
127 /* Values for the load_command cmd field. */
|
|
128
|
|
129 #define MACH_O_LC_SEGMENT 0x01
|
|
130 #define MACH_O_LC_SYMTAB 0x02
|
|
131 #define MACH_O_LC_SEGMENT_64 0x19
|
|
132 #define MACH_O_LC_UUID 0x1b
|
|
133
|
|
134 /* The length of a section of segment name. */
|
|
135
|
|
136 #define MACH_O_NAMELEN (16)
|
|
137
|
|
138 /* LC_SEGMENT load command. */
|
|
139
|
|
140 struct macho_segment_command
|
|
141 {
|
|
142 uint32_t cmd; /* The type of load command (LC_SEGMENT) */
|
|
143 uint32_t cmdsize; /* Size in bytes of the entire command */
|
|
144 char segname[MACH_O_NAMELEN]; /* Segment name */
|
|
145 uint32_t vmaddr; /* Virtual memory address */
|
|
146 uint32_t vmsize; /* Virtual memory size */
|
|
147 uint32_t fileoff; /* Offset of data to be mapped */
|
|
148 uint32_t filesize; /* Size of data in file */
|
|
149 uint32_t maxprot; /* Maximum permitted virtual protection */
|
|
150 uint32_t initprot; /* Initial virtual memory protection */
|
|
151 uint32_t nsects; /* Number of sections in this segment */
|
|
152 uint32_t flags; /* Flags */
|
|
153 };
|
|
154
|
|
155 /* LC_SEGMENT_64 load command. */
|
|
156
|
|
157 struct macho_segment_64_command
|
|
158 {
|
|
159 uint32_t cmd; /* The type of load command (LC_SEGMENT) */
|
|
160 uint32_t cmdsize; /* Size in bytes of the entire command */
|
|
161 char segname[MACH_O_NAMELEN]; /* Segment name */
|
|
162 uint64_t vmaddr; /* Virtual memory address */
|
|
163 uint64_t vmsize; /* Virtual memory size */
|
|
164 uint64_t fileoff; /* Offset of data to be mapped */
|
|
165 uint64_t filesize; /* Size of data in file */
|
|
166 uint32_t maxprot; /* Maximum permitted virtual protection */
|
|
167 uint32_t initprot; /* Initial virtual memory protection */
|
|
168 uint32_t nsects; /* Number of sections in this segment */
|
|
169 uint32_t flags; /* Flags */
|
|
170 };
|
|
171
|
|
172 /* LC_SYMTAB load command. */
|
|
173
|
|
174 struct macho_symtab_command
|
|
175 {
|
|
176 uint32_t cmd; /* The type of load command (LC_SEGMENT) */
|
|
177 uint32_t cmdsize; /* Size in bytes of the entire command */
|
|
178 uint32_t symoff; /* File offset of symbol table */
|
|
179 uint32_t nsyms; /* Number of symbols */
|
|
180 uint32_t stroff; /* File offset of string table */
|
|
181 uint32_t strsize; /* String table size */
|
|
182 };
|
|
183
|
|
184 /* The length of a Mach-O uuid. */
|
|
185
|
|
186 #define MACH_O_UUID_LEN (16)
|
|
187
|
|
188 /* LC_UUID load command. */
|
|
189
|
|
190 struct macho_uuid_command
|
|
191 {
|
|
192 uint32_t cmd; /* Type of load command (LC_UUID) */
|
|
193 uint32_t cmdsize; /* Size in bytes of command */
|
|
194 unsigned char uuid[MACH_O_UUID_LEN]; /* UUID */
|
|
195 };
|
|
196
|
|
197 /* 32-bit section header within a LC_SEGMENT segment. */
|
|
198
|
|
199 struct macho_section
|
|
200 {
|
|
201 char sectname[MACH_O_NAMELEN]; /* Section name */
|
|
202 char segment[MACH_O_NAMELEN]; /* Segment of this section */
|
|
203 uint32_t addr; /* Address in memory */
|
|
204 uint32_t size; /* Section size */
|
|
205 uint32_t offset; /* File offset */
|
|
206 uint32_t align; /* Log2 of section alignment */
|
|
207 uint32_t reloff; /* File offset of relocations */
|
|
208 uint32_t nreloc; /* Number of relocs for this section */
|
|
209 uint32_t flags; /* Flags */
|
|
210 uint32_t reserved1;
|
|
211 uint32_t reserved2;
|
|
212 };
|
|
213
|
|
214 /* 64-bit section header within a LC_SEGMENT_64 segment. */
|
|
215
|
|
216 struct macho_section_64
|
|
217 {
|
|
218 char sectname[MACH_O_NAMELEN]; /* Section name */
|
|
219 char segment[MACH_O_NAMELEN]; /* Segment of this section */
|
|
220 uint64_t addr; /* Address in memory */
|
|
221 uint64_t size; /* Section size */
|
|
222 uint32_t offset; /* File offset */
|
|
223 uint32_t align; /* Log2 of section alignment */
|
|
224 uint32_t reloff; /* File offset of section relocations */
|
|
225 uint32_t nreloc; /* Number of relocs for this section */
|
|
226 uint32_t flags; /* Flags */
|
|
227 uint32_t reserved1;
|
|
228 uint32_t reserved2;
|
|
229 uint32_t reserved3;
|
|
230 };
|
|
231
|
|
232 /* 32-bit symbol data. */
|
|
233
|
|
234 struct macho_nlist
|
|
235 {
|
|
236 uint32_t n_strx; /* Index of name in string table */
|
|
237 uint8_t n_type; /* Type flag */
|
|
238 uint8_t n_sect; /* Section number */
|
|
239 uint16_t n_desc; /* Stabs description field */
|
|
240 uint32_t n_value; /* Value */
|
|
241 };
|
|
242
|
|
243 /* 64-bit symbol data. */
|
|
244
|
|
245 struct macho_nlist_64
|
|
246 {
|
|
247 uint32_t n_strx; /* Index of name in string table */
|
|
248 uint8_t n_type; /* Type flag */
|
|
249 uint8_t n_sect; /* Section number */
|
|
250 uint16_t n_desc; /* Stabs description field */
|
|
251 uint64_t n_value; /* Value */
|
|
252 };
|
|
253
|
|
254 /* Value found in nlist n_type field. */
|
|
255
|
|
256 #define MACH_O_N_EXT 0x01 /* Extern symbol */
|
|
257 #define MACH_O_N_ABS 0x02 /* Absolute symbol */
|
|
258 #define MACH_O_N_SECT 0x0e /* Defined in section */
|
|
259
|
|
260 #define MACH_O_N_TYPE 0x0e /* Mask for type bits */
|
|
261 #define MACH_O_N_STAB 0xe0 /* Stabs debugging symbol */
|
|
262
|
|
263 /* Information we keep for a Mach-O symbol. */
|
|
264
|
|
265 struct macho_symbol
|
|
266 {
|
|
267 const char *name; /* Symbol name */
|
|
268 uintptr_t address; /* Symbol address */
|
|
269 };
|
|
270
|
|
271 /* Information to pass to macho_syminfo. */
|
|
272
|
|
273 struct macho_syminfo_data
|
|
274 {
|
|
275 struct macho_syminfo_data *next; /* Next module */
|
|
276 struct macho_symbol *symbols; /* Symbols sorted by address */
|
|
277 size_t count; /* Number of symbols */
|
|
278 };
|
|
279
|
|
280 /* Names of sections, indexed by enum dwarf_section in internal.h. */
|
|
281
|
|
282 static const char * const dwarf_section_names[DEBUG_MAX] =
|
|
283 {
|
|
284 "__debug_info",
|
|
285 "__debug_line",
|
|
286 "__debug_abbrev",
|
|
287 "__debug_ranges",
|
|
288 "__debug_str",
|
|
289 "", /* DEBUG_ADDR */
|
|
290 "__debug_str_offs",
|
|
291 "", /* DEBUG_LINE_STR */
|
|
292 "__debug_rnglists"
|
|
293 };
|
|
294
|
|
295 /* Forward declaration. */
|
|
296
|
|
297 static int macho_add (struct backtrace_state *, const char *, int, off_t,
|
|
298 const unsigned char *, uintptr_t, int,
|
|
299 backtrace_error_callback, void *, fileline *, int *);
|
|
300
|
|
301 /* A dummy callback function used when we can't find any debug info. */
|
|
302
|
|
303 static int
|
|
304 macho_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
|
305 uintptr_t pc ATTRIBUTE_UNUSED,
|
|
306 backtrace_full_callback callback ATTRIBUTE_UNUSED,
|
|
307 backtrace_error_callback error_callback, void *data)
|
|
308 {
|
|
309 error_callback (data, "no debug info in Mach-O executable", -1);
|
|
310 return 0;
|
|
311 }
|
|
312
|
|
313 /* A dummy callback function used when we can't find a symbol
|
|
314 table. */
|
|
315
|
|
316 static void
|
|
317 macho_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
|
318 uintptr_t addr ATTRIBUTE_UNUSED,
|
|
319 backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
|
|
320 backtrace_error_callback error_callback, void *data)
|
|
321 {
|
|
322 error_callback (data, "no symbol table in Mach-O executable", -1);
|
|
323 }
|
|
324
|
|
325 /* Add a single DWARF section to DWARF_SECTIONS, if we need the
|
|
326 section. Returns 1 on success, 0 on failure. */
|
|
327
|
|
328 static int
|
|
329 macho_add_dwarf_section (struct backtrace_state *state, int descriptor,
|
|
330 const char *sectname, uint32_t offset, uint64_t size,
|
|
331 backtrace_error_callback error_callback, void *data,
|
|
332 struct dwarf_sections *dwarf_sections)
|
|
333 {
|
|
334 int i;
|
|
335
|
|
336 for (i = 0; i < (int) DEBUG_MAX; ++i)
|
|
337 {
|
|
338 if (dwarf_section_names[i][0] != '\0'
|
|
339 && strncmp (sectname, dwarf_section_names[i], MACH_O_NAMELEN) == 0)
|
|
340 {
|
|
341 struct backtrace_view section_view;
|
|
342
|
|
343 /* FIXME: Perhaps it would be better to try to use a single
|
|
344 view to read all the DWARF data, as we try to do for
|
|
345 ELF. */
|
|
346
|
|
347 if (!backtrace_get_view (state, descriptor, offset, size,
|
|
348 error_callback, data, §ion_view))
|
|
349 return 0;
|
|
350 dwarf_sections->data[i] = (const unsigned char *) section_view.data;
|
|
351 dwarf_sections->size[i] = size;
|
|
352 break;
|
|
353 }
|
|
354 }
|
|
355 return 1;
|
|
356 }
|
|
357
|
|
358 /* Collect DWARF sections from a DWARF segment. Returns 1 on success,
|
|
359 0 on failure. */
|
|
360
|
|
361 static int
|
|
362 macho_add_dwarf_segment (struct backtrace_state *state, int descriptor,
|
|
363 off_t offset, unsigned int cmd, const char *psecs,
|
|
364 size_t sizesecs, unsigned int nsects,
|
|
365 backtrace_error_callback error_callback, void *data,
|
|
366 struct dwarf_sections *dwarf_sections)
|
|
367 {
|
|
368 size_t sec_header_size;
|
|
369 size_t secoffset;
|
|
370 unsigned int i;
|
|
371
|
|
372 switch (cmd)
|
|
373 {
|
|
374 case MACH_O_LC_SEGMENT:
|
|
375 sec_header_size = sizeof (struct macho_section);
|
|
376 break;
|
|
377 case MACH_O_LC_SEGMENT_64:
|
|
378 sec_header_size = sizeof (struct macho_section_64);
|
|
379 break;
|
|
380 default:
|
|
381 abort ();
|
|
382 }
|
|
383
|
|
384 secoffset = 0;
|
|
385 for (i = 0; i < nsects; ++i)
|
|
386 {
|
|
387 if (secoffset + sec_header_size > sizesecs)
|
|
388 {
|
|
389 error_callback (data, "section overflow withing segment", 0);
|
|
390 return 0;
|
|
391 }
|
|
392
|
|
393 switch (cmd)
|
|
394 {
|
|
395 case MACH_O_LC_SEGMENT:
|
|
396 {
|
|
397 struct macho_section section;
|
|
398
|
|
399 memcpy (§ion, psecs + secoffset, sizeof section);
|
|
400 macho_add_dwarf_section (state, descriptor, section.sectname,
|
|
401 offset + section.offset, section.size,
|
|
402 error_callback, data, dwarf_sections);
|
|
403 }
|
|
404 break;
|
|
405
|
|
406 case MACH_O_LC_SEGMENT_64:
|
|
407 {
|
|
408 struct macho_section_64 section;
|
|
409
|
|
410 memcpy (§ion, psecs + secoffset, sizeof section);
|
|
411 macho_add_dwarf_section (state, descriptor, section.sectname,
|
|
412 offset + section.offset, section.size,
|
|
413 error_callback, data, dwarf_sections);
|
|
414 }
|
|
415 break;
|
|
416
|
|
417 default:
|
|
418 abort ();
|
|
419 }
|
|
420
|
|
421 secoffset += sec_header_size;
|
|
422 }
|
|
423
|
|
424 return 1;
|
|
425 }
|
|
426
|
|
427 /* Compare struct macho_symbol for qsort. */
|
|
428
|
|
429 static int
|
|
430 macho_symbol_compare (const void *v1, const void *v2)
|
|
431 {
|
|
432 const struct macho_symbol *m1 = (const struct macho_symbol *) v1;
|
|
433 const struct macho_symbol *m2 = (const struct macho_symbol *) v2;
|
|
434
|
|
435 if (m1->address < m2->address)
|
|
436 return -1;
|
|
437 else if (m1->address > m2->address)
|
|
438 return 1;
|
|
439 else
|
|
440 return 0;
|
|
441 }
|
|
442
|
|
443 /* Compare an address against a macho_symbol for bsearch. We allocate
|
|
444 one extra entry in the array so that this can safely look at the
|
|
445 next entry. */
|
|
446
|
|
447 static int
|
|
448 macho_symbol_search (const void *vkey, const void *ventry)
|
|
449 {
|
|
450 const uintptr_t *key = (const uintptr_t *) vkey;
|
|
451 const struct macho_symbol *entry = (const struct macho_symbol *) ventry;
|
|
452 uintptr_t addr;
|
|
453
|
|
454 addr = *key;
|
|
455 if (addr < entry->address)
|
|
456 return -1;
|
|
457 else if (entry->name[0] == '\0'
|
|
458 && entry->address == ~(uintptr_t) 0)
|
|
459 return -1;
|
|
460 else if ((entry + 1)->name[0] == '\0'
|
|
461 && (entry + 1)->address == ~(uintptr_t) 0)
|
|
462 return -1;
|
|
463 else if (addr >= (entry + 1)->address)
|
|
464 return 1;
|
|
465 else
|
|
466 return 0;
|
|
467 }
|
|
468
|
|
469 /* Return whether the symbol type field indicates a symbol table entry
|
|
470 that we care about: a function or data symbol. */
|
|
471
|
|
472 static int
|
|
473 macho_defined_symbol (uint8_t type)
|
|
474 {
|
|
475 if ((type & MACH_O_N_STAB) != 0)
|
|
476 return 0;
|
|
477 if ((type & MACH_O_N_EXT) != 0)
|
|
478 return 0;
|
|
479 switch (type & MACH_O_N_TYPE)
|
|
480 {
|
|
481 case MACH_O_N_ABS:
|
|
482 return 1;
|
|
483 case MACH_O_N_SECT:
|
|
484 return 1;
|
|
485 default:
|
|
486 return 0;
|
|
487 }
|
|
488 }
|
|
489
|
|
490 /* Add symbol table information for a Mach-O file. */
|
|
491
|
|
492 static int
|
|
493 macho_add_symtab (struct backtrace_state *state, int descriptor,
|
|
494 uintptr_t base_address, int is_64,
|
|
495 off_t symoff, unsigned int nsyms, off_t stroff,
|
|
496 unsigned int strsize,
|
|
497 backtrace_error_callback error_callback, void *data)
|
|
498 {
|
|
499 size_t symsize;
|
|
500 struct backtrace_view sym_view;
|
|
501 int sym_view_valid;
|
|
502 struct backtrace_view str_view;
|
|
503 int str_view_valid;
|
|
504 size_t ndefs;
|
|
505 size_t symtaboff;
|
|
506 unsigned int i;
|
|
507 size_t macho_symbol_size;
|
|
508 struct macho_symbol *macho_symbols;
|
|
509 unsigned int j;
|
|
510 struct macho_syminfo_data *sdata;
|
|
511
|
|
512 sym_view_valid = 0;
|
|
513 str_view_valid = 0;
|
|
514 macho_symbol_size = 0;
|
|
515 macho_symbols = NULL;
|
|
516
|
|
517 if (is_64)
|
|
518 symsize = sizeof (struct macho_nlist_64);
|
|
519 else
|
|
520 symsize = sizeof (struct macho_nlist);
|
|
521
|
|
522 if (!backtrace_get_view (state, descriptor, symoff, nsyms * symsize,
|
|
523 error_callback, data, &sym_view))
|
|
524 goto fail;
|
|
525 sym_view_valid = 1;
|
|
526
|
|
527 if (!backtrace_get_view (state, descriptor, stroff, strsize,
|
|
528 error_callback, data, &str_view))
|
|
529 return 0;
|
|
530 str_view_valid = 1;
|
|
531
|
|
532 ndefs = 0;
|
|
533 symtaboff = 0;
|
|
534 for (i = 0; i < nsyms; ++i, symtaboff += symsize)
|
|
535 {
|
|
536 if (is_64)
|
|
537 {
|
|
538 struct macho_nlist_64 nlist;
|
|
539
|
|
540 memcpy (&nlist, (const char *) sym_view.data + symtaboff,
|
|
541 sizeof nlist);
|
|
542 if (macho_defined_symbol (nlist.n_type))
|
|
543 ++ndefs;
|
|
544 }
|
|
545 else
|
|
546 {
|
|
547 struct macho_nlist nlist;
|
|
548
|
|
549 memcpy (&nlist, (const char *) sym_view.data + symtaboff,
|
|
550 sizeof nlist);
|
|
551 if (macho_defined_symbol (nlist.n_type))
|
|
552 ++ndefs;
|
|
553 }
|
|
554 }
|
|
555
|
|
556 /* Add 1 to ndefs to make room for a sentinel. */
|
|
557 macho_symbol_size = (ndefs + 1) * sizeof (struct macho_symbol);
|
|
558 macho_symbols = ((struct macho_symbol *)
|
|
559 backtrace_alloc (state, macho_symbol_size, error_callback,
|
|
560 data));
|
|
561 if (macho_symbols == NULL)
|
|
562 goto fail;
|
|
563
|
|
564 j = 0;
|
|
565 symtaboff = 0;
|
|
566 for (i = 0; i < nsyms; ++i, symtaboff += symsize)
|
|
567 {
|
|
568 uint32_t strx;
|
|
569 uint64_t value;
|
|
570 const char *name;
|
|
571
|
|
572 strx = 0;
|
|
573 value = 0;
|
|
574 if (is_64)
|
|
575 {
|
|
576 struct macho_nlist_64 nlist;
|
|
577
|
|
578 memcpy (&nlist, (const char *) sym_view.data + symtaboff,
|
|
579 sizeof nlist);
|
|
580 if (!macho_defined_symbol (nlist.n_type))
|
|
581 continue;
|
|
582
|
|
583 strx = nlist.n_strx;
|
|
584 value = nlist.n_value;
|
|
585 }
|
|
586 else
|
|
587 {
|
|
588 struct macho_nlist nlist;
|
|
589
|
|
590 memcpy (&nlist, (const char *) sym_view.data + symtaboff,
|
|
591 sizeof nlist);
|
|
592 if (!macho_defined_symbol (nlist.n_type))
|
|
593 continue;
|
|
594
|
|
595 strx = nlist.n_strx;
|
|
596 value = nlist.n_value;
|
|
597 }
|
|
598
|
|
599 if (strx >= strsize)
|
|
600 {
|
|
601 error_callback (data, "symbol string index out of range", 0);
|
|
602 goto fail;
|
|
603 }
|
|
604
|
|
605 name = (const char *) str_view.data + strx;
|
|
606 if (name[0] == '_')
|
|
607 ++name;
|
|
608 macho_symbols[j].name = name;
|
|
609 macho_symbols[j].address = value + base_address;
|
|
610 ++j;
|
|
611 }
|
|
612
|
|
613 sdata = ((struct macho_syminfo_data *)
|
|
614 backtrace_alloc (state, sizeof *sdata, error_callback, data));
|
|
615 if (sdata == NULL)
|
|
616 goto fail;
|
|
617
|
|
618 /* We need to keep the string table since it holds the names, but we
|
|
619 can release the symbol table. */
|
|
620
|
|
621 backtrace_release_view (state, &sym_view, error_callback, data);
|
|
622 sym_view_valid = 0;
|
|
623 str_view_valid = 0;
|
|
624
|
|
625 /* Add a trailing sentinel symbol. */
|
|
626 macho_symbols[j].name = "";
|
|
627 macho_symbols[j].address = ~(uintptr_t) 0;
|
|
628
|
|
629 backtrace_qsort (macho_symbols, ndefs + 1, sizeof (struct macho_symbol),
|
|
630 macho_symbol_compare);
|
|
631
|
|
632 sdata->next = NULL;
|
|
633 sdata->symbols = macho_symbols;
|
|
634 sdata->count = ndefs;
|
|
635
|
|
636 if (!state->threaded)
|
|
637 {
|
|
638 struct macho_syminfo_data **pp;
|
|
639
|
|
640 for (pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data;
|
|
641 *pp != NULL;
|
|
642 pp = &(*pp)->next)
|
|
643 ;
|
|
644 *pp = sdata;
|
|
645 }
|
|
646 else
|
|
647 {
|
|
648 while (1)
|
|
649 {
|
|
650 struct macho_syminfo_data **pp;
|
|
651
|
|
652 pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data;
|
|
653
|
|
654 while (1)
|
|
655 {
|
|
656 struct macho_syminfo_data *p;
|
|
657
|
|
658 p = backtrace_atomic_load_pointer (pp);
|
|
659
|
|
660 if (p == NULL)
|
|
661 break;
|
|
662
|
|
663 pp = &p->next;
|
|
664 }
|
|
665
|
|
666 if (__sync_bool_compare_and_swap (pp, NULL, sdata))
|
|
667 break;
|
|
668 }
|
|
669 }
|
|
670
|
|
671 return 1;
|
|
672
|
|
673 fail:
|
|
674 if (macho_symbols != NULL)
|
|
675 backtrace_free (state, macho_symbols, macho_symbol_size,
|
|
676 error_callback, data);
|
|
677 if (sym_view_valid)
|
|
678 backtrace_release_view (state, &sym_view, error_callback, data);
|
|
679 if (str_view_valid)
|
|
680 backtrace_release_view (state, &str_view, error_callback, data);
|
|
681 return 0;
|
|
682 }
|
|
683
|
|
684 /* Return the symbol name and value for an ADDR. */
|
|
685
|
|
686 static void
|
|
687 macho_syminfo (struct backtrace_state *state, uintptr_t addr,
|
|
688 backtrace_syminfo_callback callback,
|
|
689 backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
|
|
690 void *data)
|
|
691 {
|
|
692 struct macho_syminfo_data *sdata;
|
|
693 struct macho_symbol *sym;
|
|
694
|
|
695 sym = NULL;
|
|
696 if (!state->threaded)
|
|
697 {
|
|
698 for (sdata = (struct macho_syminfo_data *) state->syminfo_data;
|
|
699 sdata != NULL;
|
|
700 sdata = sdata->next)
|
|
701 {
|
|
702 sym = ((struct macho_symbol *)
|
|
703 bsearch (&addr, sdata->symbols, sdata->count,
|
|
704 sizeof (struct macho_symbol), macho_symbol_search));
|
|
705 if (sym != NULL)
|
|
706 break;
|
|
707 }
|
|
708 }
|
|
709 else
|
|
710 {
|
|
711 struct macho_syminfo_data **pp;
|
|
712
|
|
713 pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data;
|
|
714 while (1)
|
|
715 {
|
|
716 sdata = backtrace_atomic_load_pointer (pp);
|
|
717 if (sdata == NULL)
|
|
718 break;
|
|
719
|
|
720 sym = ((struct macho_symbol *)
|
|
721 bsearch (&addr, sdata->symbols, sdata->count,
|
|
722 sizeof (struct macho_symbol), macho_symbol_search));
|
|
723 if (sym != NULL)
|
|
724 break;
|
|
725
|
|
726 pp = &sdata->next;
|
|
727 }
|
|
728 }
|
|
729
|
|
730 if (sym == NULL)
|
|
731 callback (data, addr, NULL, 0, 0);
|
|
732 else
|
|
733 callback (data, addr, sym->name, sym->address, 0);
|
|
734 }
|
|
735
|
|
736 /* Look through a fat file to find the relevant executable. Returns 1
|
|
737 on success, 0 on failure (in both cases descriptor is closed). */
|
|
738
|
|
739 static int
|
|
740 macho_add_fat (struct backtrace_state *state, const char *filename,
|
|
741 int descriptor, int swapped, off_t offset,
|
|
742 const unsigned char *match_uuid, uintptr_t base_address,
|
|
743 int skip_symtab, uint32_t nfat_arch,
|
|
744 backtrace_error_callback error_callback, void *data,
|
|
745 fileline *fileline_fn, int *found_sym)
|
|
746 {
|
|
747 int arch_view_valid;
|
|
748 unsigned int cputype;
|
|
749 struct backtrace_view arch_view;
|
|
750 size_t archoffset;
|
|
751 unsigned int i;
|
|
752
|
|
753 arch_view_valid = 0;
|
|
754
|
|
755 #if defined (__x86_64__)
|
|
756 cputype = MACH_O_CPU_TYPE_X86_64;
|
|
757 #elif defined (__i386__)
|
|
758 cputype = MACH_O_CPU_TYPE_X86;
|
|
759 #elif defined (__aarch64__)
|
|
760 cputype = MACH_O_CPU_TYPE_ARM64;
|
|
761 #elif defined (__arm__)
|
|
762 cputype = MACH_O_CPU_TYPE_ARM;
|
|
763 #else
|
|
764 error_callback (data, "unknown Mach-O architecture", 0);
|
|
765 goto fail;
|
|
766 #endif
|
|
767
|
|
768 if (!backtrace_get_view (state, descriptor, offset,
|
|
769 nfat_arch * sizeof (struct macho_fat_arch),
|
|
770 error_callback, data, &arch_view))
|
|
771 goto fail;
|
|
772
|
|
773 archoffset = 0;
|
|
774 for (i = 0; i < nfat_arch; ++i)
|
|
775 {
|
|
776 struct macho_fat_arch fat_arch;
|
|
777 uint32_t fcputype;
|
|
778
|
|
779 memcpy (&fat_arch,
|
|
780 ((const char *) arch_view.data
|
|
781 + i * sizeof (struct macho_fat_arch)),
|
|
782 sizeof fat_arch);
|
|
783
|
|
784 fcputype = fat_arch.cputype;
|
|
785 if (swapped)
|
|
786 fcputype = __builtin_bswap32 (fcputype);
|
|
787
|
|
788 if (fcputype == cputype)
|
|
789 {
|
|
790 uint32_t foffset;
|
|
791
|
|
792 /* FIXME: What about cpusubtype? */
|
|
793 foffset = fat_arch.offset;
|
|
794 if (swapped)
|
|
795 foffset = __builtin_bswap32 (foffset);
|
|
796 backtrace_release_view (state, &arch_view, error_callback, data);
|
|
797 return macho_add (state, filename, descriptor, foffset, match_uuid,
|
|
798 base_address, skip_symtab, error_callback, data,
|
|
799 fileline_fn, found_sym);
|
|
800 }
|
|
801
|
|
802 archoffset += sizeof (struct macho_fat_arch);
|
|
803 }
|
|
804
|
|
805 error_callback (data, "could not find executable in fat file", 0);
|
|
806
|
|
807 fail:
|
|
808 if (arch_view_valid)
|
|
809 backtrace_release_view (state, &arch_view, error_callback, data);
|
|
810 if (descriptor != -1)
|
|
811 backtrace_close (descriptor, error_callback, data);
|
|
812 return 0;
|
|
813 }
|
|
814
|
|
815 /* Look for the dsym file for FILENAME. This is called if FILENAME
|
|
816 does not have debug info or a symbol table. Returns 1 on success,
|
|
817 0 on failure. */
|
|
818
|
|
819 static int
|
|
820 macho_add_dsym (struct backtrace_state *state, const char *filename,
|
|
821 uintptr_t base_address, const unsigned char *uuid,
|
|
822 backtrace_error_callback error_callback, void *data,
|
|
823 fileline* fileline_fn)
|
|
824 {
|
|
825 const char *p;
|
|
826 const char *dirname;
|
|
827 char *diralc;
|
|
828 size_t dirnamelen;
|
|
829 const char *basename;
|
|
830 size_t basenamelen;
|
|
831 const char *dsymsuffixdir;
|
|
832 size_t dsymsuffixdirlen;
|
|
833 size_t dsymlen;
|
|
834 char *dsym;
|
|
835 char *ps;
|
|
836 int d;
|
|
837 int does_not_exist;
|
|
838 int dummy_found_sym;
|
|
839
|
|
840 diralc = NULL;
|
|
841 dirnamelen = 0;
|
|
842 dsym = NULL;
|
|
843 dsymlen = 0;
|
|
844
|
|
845 p = strrchr (filename, '/');
|
|
846 if (p == NULL)
|
|
847 {
|
|
848 dirname = ".";
|
|
849 dirnamelen = 1;
|
|
850 basename = filename;
|
|
851 basenamelen = strlen (basename);
|
|
852 diralc = NULL;
|
|
853 }
|
|
854 else
|
|
855 {
|
|
856 dirnamelen = p - filename;
|
|
857 diralc = backtrace_alloc (state, dirnamelen + 1, error_callback, data);
|
|
858 if (diralc == NULL)
|
|
859 goto fail;
|
|
860 memcpy (diralc, filename, dirnamelen);
|
|
861 diralc[dirnamelen] = '\0';
|
|
862 dirname = diralc;
|
|
863 basename = p + 1;
|
|
864 basenamelen = strlen (basename);
|
|
865 }
|
|
866
|
|
867 dsymsuffixdir = ".dSYM/Contents/Resources/DWARF/";
|
|
868 dsymsuffixdirlen = strlen (dsymsuffixdir);
|
|
869
|
|
870 dsymlen = (dirnamelen
|
|
871 + basenamelen
|
|
872 + dsymsuffixdirlen
|
|
873 + basenamelen
|
|
874 + 1);
|
|
875 dsym = backtrace_alloc (state, dsymlen, error_callback, data);
|
|
876 if (dsym == NULL)
|
|
877 goto fail;
|
|
878
|
|
879 ps = dsym;
|
|
880 memcpy (ps, dirname, dirnamelen);
|
|
881 ps += dirnamelen;
|
|
882 *ps++ = '/';
|
|
883 memcpy (ps, basename, basenamelen);
|
|
884 ps += basenamelen;
|
|
885 memcpy (ps, dsymsuffixdir, dsymsuffixdirlen);
|
|
886 ps += dsymsuffixdirlen;
|
|
887 memcpy (ps, basename, basenamelen);
|
|
888 ps += basenamelen;
|
|
889 *ps = '\0';
|
|
890
|
|
891 if (diralc != NULL)
|
|
892 {
|
|
893 backtrace_free (state, diralc, dirnamelen, error_callback, data);
|
|
894 diralc = NULL;
|
|
895 }
|
|
896
|
|
897 d = backtrace_open (dsym, error_callback, data, &does_not_exist);
|
|
898 if (d < 0)
|
|
899 {
|
|
900 /* The file does not exist, so we can't read the debug info.
|
|
901 Just return success. */
|
|
902 backtrace_free (state, dsym, dsymlen, error_callback, data);
|
|
903 return 1;
|
|
904 }
|
|
905
|
|
906 if (!macho_add (state, dsym, d, 0, uuid, base_address, 1,
|
|
907 error_callback, data, fileline_fn, &dummy_found_sym))
|
|
908 goto fail;
|
|
909
|
|
910 backtrace_free (state, dsym, dsymlen, error_callback, data);
|
|
911
|
|
912 return 1;
|
|
913
|
|
914 fail:
|
|
915 if (dsym != NULL)
|
|
916 backtrace_free (state, dsym, dsymlen, error_callback, data);
|
|
917 if (diralc != NULL)
|
|
918 backtrace_free (state, diralc, dirnamelen, error_callback, data);
|
|
919 return 0;
|
|
920 }
|
|
921
|
|
922 /* Add the backtrace data for a Macho-O file. Returns 1 on success, 0
|
|
923 on failure (in both cases descriptor is closed).
|
|
924
|
|
925 FILENAME: the name of the executable.
|
|
926 DESCRIPTOR: an open descriptor for the executable, closed here.
|
|
927 OFFSET: the offset within the file of this executable, for fat files.
|
|
928 MATCH_UUID: if not NULL, UUID that must match.
|
|
929 BASE_ADDRESS: the load address of the executable.
|
|
930 SKIP_SYMTAB: if non-zero, ignore the symbol table; used for dSYM files.
|
|
931 FILELINE_FN: set to the fileline function, by backtrace_dwarf_add.
|
|
932 FOUND_SYM: set to non-zero if we found the symbol table.
|
|
933 */
|
|
934
|
|
935 static int
|
|
936 macho_add (struct backtrace_state *state, const char *filename, int descriptor,
|
|
937 off_t offset, const unsigned char *match_uuid,
|
|
938 uintptr_t base_address, int skip_symtab,
|
|
939 backtrace_error_callback error_callback, void *data,
|
|
940 fileline *fileline_fn, int *found_sym)
|
|
941 {
|
|
942 struct backtrace_view header_view;
|
|
943 struct macho_header_32 header;
|
|
944 off_t hdroffset;
|
|
945 int is_64;
|
|
946 struct backtrace_view cmds_view;
|
|
947 int cmds_view_valid;
|
|
948 struct dwarf_sections dwarf_sections;
|
|
949 int have_dwarf;
|
|
950 unsigned char uuid[MACH_O_UUID_LEN];
|
|
951 int have_uuid;
|
|
952 size_t cmdoffset;
|
|
953 unsigned int i;
|
|
954
|
|
955 *found_sym = 0;
|
|
956
|
|
957 cmds_view_valid = 0;
|
|
958
|
|
959 /* The 32-bit and 64-bit file headers start out the same, so we can
|
|
960 just always read the 32-bit version. A fat header is shorter but
|
|
961 it will always be followed by data, so it's OK to read extra. */
|
|
962
|
|
963 if (!backtrace_get_view (state, descriptor, offset,
|
|
964 sizeof (struct macho_header_32),
|
|
965 error_callback, data, &header_view))
|
|
966 goto fail;
|
|
967
|
|
968 memcpy (&header, header_view.data, sizeof header);
|
|
969
|
|
970 backtrace_release_view (state, &header_view, error_callback, data);
|
|
971
|
|
972 switch (header.magic)
|
|
973 {
|
|
974 case MACH_O_MH_MAGIC_32:
|
|
975 is_64 = 0;
|
|
976 hdroffset = offset + sizeof (struct macho_header_32);
|
|
977 break;
|
|
978 case MACH_O_MH_MAGIC_64:
|
|
979 is_64 = 1;
|
|
980 hdroffset = offset + sizeof (struct macho_header_64);
|
|
981 break;
|
|
982 case MACH_O_MH_MAGIC_FAT:
|
|
983 {
|
|
984 struct macho_header_fat fat_header;
|
|
985
|
|
986 hdroffset = offset + sizeof (struct macho_header_fat);
|
|
987 memcpy (&fat_header, &header, sizeof fat_header);
|
|
988 return macho_add_fat (state, filename, descriptor, 0, hdroffset,
|
|
989 match_uuid, base_address, skip_symtab,
|
|
990 fat_header.nfat_arch, error_callback, data,
|
|
991 fileline_fn, found_sym);
|
|
992 }
|
|
993 case MACH_O_MH_CIGAM_FAT:
|
|
994 {
|
|
995 struct macho_header_fat fat_header;
|
|
996 uint32_t nfat_arch;
|
|
997
|
|
998 hdroffset = offset + sizeof (struct macho_header_fat);
|
|
999 memcpy (&fat_header, &header, sizeof fat_header);
|
|
1000 nfat_arch = __builtin_bswap32 (fat_header.nfat_arch);
|
|
1001 return macho_add_fat (state, filename, descriptor, 1, hdroffset,
|
|
1002 match_uuid, base_address, skip_symtab,
|
|
1003 nfat_arch, error_callback, data,
|
|
1004 fileline_fn, found_sym);
|
|
1005 }
|
|
1006 default:
|
|
1007 error_callback (data, "executable file is not in Mach-O format", 0);
|
|
1008 goto fail;
|
|
1009 }
|
|
1010
|
|
1011 switch (header.filetype)
|
|
1012 {
|
|
1013 case MACH_O_MH_EXECUTE:
|
|
1014 case MACH_O_MH_DYLIB:
|
|
1015 case MACH_O_MH_DSYM:
|
|
1016 break;
|
|
1017 default:
|
|
1018 error_callback (data, "executable file is not an executable", 0);
|
|
1019 goto fail;
|
|
1020 }
|
|
1021
|
|
1022 if (!backtrace_get_view (state, descriptor, hdroffset, header.sizeofcmds,
|
|
1023 error_callback, data, &cmds_view))
|
|
1024 goto fail;
|
|
1025 cmds_view_valid = 1;
|
|
1026
|
|
1027 memset (&dwarf_sections, 0, sizeof dwarf_sections);
|
|
1028 have_dwarf = 0;
|
|
1029 memset (&uuid, 0, sizeof uuid);
|
|
1030 have_uuid = 0;
|
|
1031
|
|
1032 cmdoffset = 0;
|
|
1033 for (i = 0; i < header.ncmds; ++i)
|
|
1034 {
|
|
1035 const char *pcmd;
|
|
1036 struct macho_load_command load_command;
|
|
1037
|
|
1038 if (cmdoffset + sizeof load_command > header.sizeofcmds)
|
|
1039 break;
|
|
1040
|
|
1041 pcmd = (const char *) cmds_view.data + cmdoffset;
|
|
1042 memcpy (&load_command, pcmd, sizeof load_command);
|
|
1043
|
|
1044 switch (load_command.cmd)
|
|
1045 {
|
|
1046 case MACH_O_LC_SEGMENT:
|
|
1047 {
|
|
1048 struct macho_segment_command segcmd;
|
|
1049
|
|
1050 memcpy (&segcmd, pcmd, sizeof segcmd);
|
|
1051 if (memcmp (segcmd.segname,
|
|
1052 "__DWARF\0\0\0\0\0\0\0\0\0",
|
|
1053 MACH_O_NAMELEN) == 0)
|
|
1054 {
|
|
1055 if (!macho_add_dwarf_segment (state, descriptor, offset,
|
|
1056 load_command.cmd,
|
|
1057 pcmd + sizeof segcmd,
|
|
1058 (load_command.cmdsize
|
|
1059 - sizeof segcmd),
|
|
1060 segcmd.nsects, error_callback,
|
|
1061 data, &dwarf_sections))
|
|
1062 goto fail;
|
|
1063 have_dwarf = 1;
|
|
1064 }
|
|
1065 }
|
|
1066 break;
|
|
1067
|
|
1068 case MACH_O_LC_SEGMENT_64:
|
|
1069 {
|
|
1070 struct macho_segment_64_command segcmd;
|
|
1071
|
|
1072 memcpy (&segcmd, pcmd, sizeof segcmd);
|
|
1073 if (memcmp (segcmd.segname,
|
|
1074 "__DWARF\0\0\0\0\0\0\0\0\0",
|
|
1075 MACH_O_NAMELEN) == 0)
|
|
1076 {
|
|
1077 if (!macho_add_dwarf_segment (state, descriptor, offset,
|
|
1078 load_command.cmd,
|
|
1079 pcmd + sizeof segcmd,
|
|
1080 (load_command.cmdsize
|
|
1081 - sizeof segcmd),
|
|
1082 segcmd.nsects, error_callback,
|
|
1083 data, &dwarf_sections))
|
|
1084 goto fail;
|
|
1085 have_dwarf = 1;
|
|
1086 }
|
|
1087 }
|
|
1088 break;
|
|
1089
|
|
1090 case MACH_O_LC_SYMTAB:
|
|
1091 if (!skip_symtab)
|
|
1092 {
|
|
1093 struct macho_symtab_command symcmd;
|
|
1094
|
|
1095 memcpy (&symcmd, pcmd, sizeof symcmd);
|
|
1096 if (!macho_add_symtab (state, descriptor, base_address, is_64,
|
|
1097 offset + symcmd.symoff, symcmd.nsyms,
|
|
1098 offset + symcmd.stroff, symcmd.strsize,
|
|
1099 error_callback, data))
|
|
1100 goto fail;
|
|
1101
|
|
1102 *found_sym = 1;
|
|
1103 }
|
|
1104 break;
|
|
1105
|
|
1106 case MACH_O_LC_UUID:
|
|
1107 {
|
|
1108 struct macho_uuid_command uuidcmd;
|
|
1109
|
|
1110 memcpy (&uuidcmd, pcmd, sizeof uuidcmd);
|
|
1111 memcpy (&uuid[0], &uuidcmd.uuid[0], MACH_O_UUID_LEN);
|
|
1112 have_uuid = 1;
|
|
1113 }
|
|
1114 break;
|
|
1115
|
|
1116 default:
|
|
1117 break;
|
|
1118 }
|
|
1119
|
|
1120 cmdoffset += load_command.cmdsize;
|
|
1121 }
|
|
1122
|
|
1123 if (!backtrace_close (descriptor, error_callback, data))
|
|
1124 goto fail;
|
|
1125 descriptor = -1;
|
|
1126
|
|
1127 backtrace_release_view (state, &cmds_view, error_callback, data);
|
|
1128 cmds_view_valid = 0;
|
|
1129
|
|
1130 if (match_uuid != NULL)
|
|
1131 {
|
|
1132 /* If we don't have a UUID, or it doesn't match, just ignore
|
|
1133 this file. */
|
|
1134 if (!have_uuid
|
|
1135 || memcmp (match_uuid, &uuid[0], MACH_O_UUID_LEN) != 0)
|
|
1136 return 1;
|
|
1137 }
|
|
1138
|
|
1139 if (have_dwarf)
|
|
1140 {
|
|
1141 int is_big_endian;
|
|
1142
|
|
1143 is_big_endian = 0;
|
|
1144 #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
|
|
1145 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
|
1146 is_big_endian = 1;
|
|
1147 #endif
|
|
1148 #endif
|
|
1149
|
|
1150 if (!backtrace_dwarf_add (state, base_address, &dwarf_sections,
|
|
1151 is_big_endian, NULL, error_callback, data,
|
|
1152 fileline_fn, NULL))
|
|
1153 goto fail;
|
|
1154 }
|
|
1155
|
|
1156 if (!have_dwarf && have_uuid)
|
|
1157 {
|
|
1158 if (!macho_add_dsym (state, filename, base_address, &uuid[0],
|
|
1159 error_callback, data, fileline_fn))
|
|
1160 goto fail;
|
|
1161 }
|
|
1162
|
|
1163 return 1;
|
|
1164
|
|
1165 fail:
|
|
1166 if (cmds_view_valid)
|
|
1167 backtrace_release_view (state, &cmds_view, error_callback, data);
|
|
1168 if (descriptor != -1)
|
|
1169 backtrace_close (descriptor, error_callback, data);
|
|
1170 return 0;
|
|
1171 }
|
|
1172
|
|
1173 #ifdef HAVE_MACH_O_DYLD_H
|
|
1174
|
|
1175 /* Initialize the backtrace data we need from a Mach-O executable
|
|
1176 using the dyld support functions. This closes descriptor. */
|
|
1177
|
|
1178 int
|
|
1179 backtrace_initialize (struct backtrace_state *state, const char *filename,
|
|
1180 int descriptor, backtrace_error_callback error_callback,
|
|
1181 void *data, fileline *fileline_fn)
|
|
1182 {
|
|
1183 uint32_t c;
|
|
1184 uint32_t i;
|
|
1185 int closed_descriptor;
|
|
1186 int found_sym;
|
|
1187 fileline macho_fileline_fn;
|
|
1188
|
|
1189 closed_descriptor = 0;
|
|
1190 found_sym = 0;
|
|
1191 macho_fileline_fn = macho_nodebug;
|
|
1192
|
|
1193 c = _dyld_image_count ();
|
|
1194 for (i = 0; i < c; ++i)
|
|
1195 {
|
|
1196 uintptr_t base_address;
|
|
1197 const char *name;
|
|
1198 int d;
|
|
1199 fileline mff;
|
|
1200 int mfs;
|
|
1201
|
|
1202 name = _dyld_get_image_name (i);
|
|
1203 if (name == NULL)
|
|
1204 continue;
|
|
1205
|
|
1206 if (strcmp (name, filename) == 0 && !closed_descriptor)
|
|
1207 {
|
|
1208 d = descriptor;
|
|
1209 closed_descriptor = 1;
|
|
1210 }
|
|
1211 else
|
|
1212 {
|
|
1213 int does_not_exist;
|
|
1214
|
|
1215 d = backtrace_open (name, error_callback, data, &does_not_exist);
|
|
1216 if (d < 0)
|
|
1217 continue;
|
|
1218 }
|
|
1219
|
|
1220 base_address = _dyld_get_image_vmaddr_slide (i);
|
|
1221
|
|
1222 mff = macho_nodebug;
|
|
1223 if (!macho_add (state, name, d, 0, NULL, base_address, 0,
|
|
1224 error_callback, data, &mff, &mfs))
|
|
1225 return 0;
|
|
1226
|
|
1227 if (mff != macho_nodebug)
|
|
1228 macho_fileline_fn = mff;
|
|
1229 if (mfs)
|
|
1230 found_sym = 1;
|
|
1231 }
|
|
1232
|
|
1233 if (!closed_descriptor)
|
|
1234 backtrace_close (descriptor, error_callback, data);
|
|
1235
|
|
1236 if (!state->threaded)
|
|
1237 {
|
|
1238 if (found_sym)
|
|
1239 state->syminfo_fn = macho_syminfo;
|
|
1240 else if (state->syminfo_fn == NULL)
|
|
1241 state->syminfo_fn = macho_nosyms;
|
|
1242 }
|
|
1243 else
|
|
1244 {
|
|
1245 if (found_sym)
|
|
1246 backtrace_atomic_store_pointer (&state->syminfo_fn, macho_syminfo);
|
|
1247 else
|
|
1248 (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL,
|
|
1249 macho_nosyms);
|
|
1250 }
|
|
1251
|
|
1252 if (!state->threaded)
|
|
1253 *fileline_fn = state->fileline_fn;
|
|
1254 else
|
|
1255 *fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
|
|
1256
|
|
1257 if (*fileline_fn == NULL || *fileline_fn == macho_nodebug)
|
|
1258 *fileline_fn = macho_fileline_fn;
|
|
1259
|
|
1260 return 1;
|
|
1261 }
|
|
1262
|
|
1263 #else /* !defined (HAVE_MACH_O_DYLD_H) */
|
|
1264
|
|
1265 /* Initialize the backtrace data we need from a Mach-O executable
|
|
1266 without using the dyld support functions. This closes
|
|
1267 descriptor. */
|
|
1268
|
|
1269 int
|
|
1270 backtrace_initialize (struct backtrace_state *state, const char *filename,
|
|
1271 int descriptor, backtrace_error_callback error_callback,
|
|
1272 void *data, fileline *fileline_fn)
|
|
1273 {
|
|
1274 fileline macho_fileline_fn;
|
|
1275 int found_sym;
|
|
1276
|
|
1277 macho_fileline_fn = macho_nodebug;
|
|
1278 if (!macho_add (state, filename, descriptor, 0, NULL, 0, 0,
|
|
1279 error_callback, data, &macho_fileline_fn, &found_sym))
|
|
1280 return 0;
|
|
1281
|
|
1282 if (!state->threaded)
|
|
1283 {
|
|
1284 if (found_sym)
|
|
1285 state->syminfo_fn = macho_syminfo;
|
|
1286 else if (state->syminfo_fn == NULL)
|
|
1287 state->syminfo_fn = macho_nosyms;
|
|
1288 }
|
|
1289 else
|
|
1290 {
|
|
1291 if (found_sym)
|
|
1292 backtrace_atomic_store_pointer (&state->syminfo_fn, macho_syminfo);
|
|
1293 else
|
|
1294 (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL,
|
|
1295 macho_nosyms);
|
|
1296 }
|
|
1297
|
|
1298 if (!state->threaded)
|
|
1299 *fileline_fn = state->fileline_fn;
|
|
1300 else
|
|
1301 *fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
|
|
1302
|
|
1303 if (*fileline_fn == NULL || *fileline_fn == macho_nodebug)
|
|
1304 *fileline_fn = macho_fileline_fn;
|
|
1305
|
|
1306 return 1;
|
|
1307 }
|
|
1308
|
|
1309 #endif /* !defined (HAVE_MACH_O_DYLD_H) */
|