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