1 | // pecoff.c: |
2 | #include <stdlib.h> |
3 | #include <string.h> |
4 | #include <sys/types.h> |
5 | |
6 | typedef struct { |
7 | uint16_t machine; |
8 | uint16_t number_of_sections; |
9 | uint32_t time_date_stamp; |
10 | uint32_t pointer_to_symbol_table; |
11 | uint32_t number_of_symbols; |
12 | uint16_t size_of_optional_header; |
13 | uint16_t characteristics; |
14 | } b_coff_file_header; |
15 | |
16 | typedef struct { |
17 | uint16_t magic; |
18 | uint8_t major_linker_version; |
19 | uint8_t minor_linker_version; |
20 | uint32_t size_of_code; |
21 | uint32_t size_of_initialized_data; |
22 | uint32_t size_of_uninitialized_data; |
23 | uint32_t address_of_entry_point; |
24 | uint32_t base_of_code; |
25 | union { |
26 | struct { |
27 | uint32_t base_of_data; |
28 | uint32_t image_base; |
29 | } pe; |
30 | struct { |
31 | uint64_t image_base; |
32 | } pep; |
33 | } u; |
34 | } b_coff_optional_header; |
35 | |
36 | #define PE_MAGIC 0x10b |
37 | #define PEP_MAGIC 0x20b |
38 | |
39 | typedef struct { |
40 | char name[8]; |
41 | uint32_t virtual_size; |
42 | uint32_t virtual_address; |
43 | uint32_t size_of_raw_data; |
44 | uint32_t pointer_to_raw_data; |
45 | uint32_t pointer_to_relocations; |
46 | uint32_t pointer_to_line_numbers; |
47 | uint16_t number_of_relocations; |
48 | uint16_t number_of_line_numbers; |
49 | uint32_t characteristics; |
50 | } b_coff_section_header; |
51 | |
52 | typedef union { |
53 | char short_name[8]; |
54 | struct { |
55 | unsigned char zeroes[4]; |
56 | unsigned char off[4]; |
57 | } long_name; |
58 | } b_coff_name; |
59 | |
60 | typedef struct { |
61 | b_coff_name name; |
62 | unsigned char value[4]; |
63 | unsigned char section_number[2]; |
64 | unsigned char type[2]; |
65 | unsigned char storage_class; |
66 | unsigned char number_of_aux_symbols; |
67 | } b_coff_external_symbol; |
68 | |
69 | #define N_TBSHFT 4 |
70 | #define IMAGE_SYM_DTYPE_FUNCTION 2 |
71 | |
72 | #define SYM_SZ 18 |
73 | |
74 | typedef struct { |
75 | const char *name; |
76 | uint32_t value; |
77 | int16_t sec; |
78 | uint16_t type; |
79 | uint16_t sc; |
80 | } b_coff_internal_symbol; |
81 | |
82 | static const char *const debug_section_names[DEBUG_MAX] = { |
83 | ".debug_info", ".debug_line", ".debug_abbrev", |
84 | ".debug_ranges", ".debug_str", ".debug_addr", |
85 | ".debug_str_offsets", ".debug_line_str", ".debug_rnglists"}; |
86 | |
87 | struct debug_section_info { |
88 | off_t offset; |
89 | |
90 | size_t size; |
91 | }; |
92 | |
93 | struct coff_symbol { |
94 | const char *name; |
95 | |
96 | uintptr_t address; |
97 | }; |
98 | |
99 | struct coff_syminfo_data { |
100 | struct coff_syminfo_data *next; |
101 | |
102 | struct coff_symbol *symbols; |
103 | |
104 | size_t count; |
105 | }; |
106 | |
107 | static int coff_nodebug(struct backtrace_state *state ATTRIBUTE_UNUSED, |
108 | uintptr_t pc ATTRIBUTE_UNUSED, |
109 | backtrace_full_callback callback ATTRIBUTE_UNUSED, |
110 | backtrace_error_callback error_callback, void *data) { |
111 | error_callback(data, "no debug info in PE/COFF executable", -1); |
112 | return 0; |
113 | } |
114 | |
115 | static void coff_nosyms(struct backtrace_state *state ATTRIBUTE_UNUSED, |
116 | uintptr_t addr ATTRIBUTE_UNUSED, |
117 | backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, |
118 | backtrace_error_callback error_callback, void *data) { |
119 | error_callback(data, "no symbol table in PE/COFF executable", -1); |
120 | } |
121 | |
122 | static uint32_t coff_read4(const unsigned char *p) { |
123 | uint32_t res; |
124 | |
125 | memcpy(&res, p, 4); |
126 | return res; |
127 | } |
128 | |
129 | static uint16_t coff_read2(const unsigned char *p) { |
130 | uint16_t res; |
131 | |
132 | memcpy(&res, p, sizeof(res)); |
133 | return res; |
134 | } |
135 | |
136 | static size_t coff_short_name_len(const char *name) { |
137 | int i; |
138 | |
139 | for (i = 0; i < 8; i++) |
140 | if (name[i] == 0) return i; |
141 | return 8; |
142 | } |
143 | |
144 | static int coff_short_name_eq(const char *name, const char *cname) { |
145 | int i; |
146 | |
147 | for (i = 0; i < 8; i++) { |
148 | if (name[i] != cname[i]) return 0; |
149 | if (name[i] == 0) return 1; |
150 | } |
151 | return name[8] == 0; |
152 | } |
153 | |
154 | static int coff_long_name_eq(const char *name, unsigned int off, |
155 | struct backtrace_view *str_view) { |
156 | if (off >= str_view->len) return 0; |
157 | return strcmp(name, (const char *)str_view->data + off) == 0; |
158 | } |
159 | |
160 | static int coff_symbol_compare(const void *v1, const void *v2) { |
161 | const struct coff_symbol *e1 = (const struct coff_symbol *)v1; |
162 | const struct coff_symbol *e2 = (const struct coff_symbol *)v2; |
163 | |
164 | if (e1->address < e2->address) |
165 | return -1; |
166 | else if (e1->address > e2->address) |
167 | return 1; |
168 | else |
169 | return 0; |
170 | } |
171 | |
172 | static int coff_expand_symbol(b_coff_internal_symbol *isym, |
173 | const b_coff_external_symbol *sym, |
174 | uint16_t sects_num, const unsigned char *strtab, |
175 | size_t strtab_size) { |
176 | isym->type = coff_read2(sym->type); |
177 | isym->sec = coff_read2(sym->section_number); |
178 | isym->sc = sym->storage_class; |
179 | |
180 | if (isym->sec > 0 && (uint16_t)isym->sec > sects_num) return -1; |
181 | if (sym->name.short_name[0] != 0) |
182 | isym->name = sym->name.short_name; |
183 | else { |
184 | uint32_t off = coff_read4(sym->name.long_name.off); |
185 | |
186 | if (off >= strtab_size) return -1; |
187 | isym->name = (const char *)strtab + off; |
188 | } |
189 | return 0; |
190 | } |
191 | |
192 | static int coff_is_function_symbol(const b_coff_internal_symbol *isym) { |
193 | return (isym->type >> N_TBSHFT) == IMAGE_SYM_DTYPE_FUNCTION && isym->sec > 0; |
194 | } |
195 | |
196 | static int coff_initialize_syminfo( |
197 | struct backtrace_state *state, uintptr_t base_address, int is_64, |
198 | const b_coff_section_header *sects, size_t sects_num, |
199 | const b_coff_external_symbol *syms, size_t syms_size, |
200 | const unsigned char *strtab, size_t strtab_size, |
201 | backtrace_error_callback error_callback, void *data, |
202 | struct coff_syminfo_data *sdata) { |
203 | size_t syms_count; |
204 | char *coff_symstr; |
205 | size_t coff_symstr_len; |
206 | size_t coff_symbol_count; |
207 | size_t coff_symbol_size; |
208 | struct coff_symbol *coff_symbols; |
209 | struct coff_symbol *coff_sym; |
210 | char *coff_str; |
211 | size_t i; |
212 | |
213 | syms_count = syms_size / SYM_SZ; |
214 | |
215 | coff_symbol_count = 0; |
216 | coff_symstr_len = 0; |
217 | for (i = 0; i < syms_count; ++i) { |
218 | const b_coff_external_symbol *asym = &syms[i]; |
219 | b_coff_internal_symbol isym; |
220 | |
221 | if (coff_expand_symbol(&isym, asym, sects_num, strtab, strtab_size) < 0) { |
222 | error_callback(data, "invalid section or offset in coff symbol", 0); |
223 | return 0; |
224 | } |
225 | if (coff_is_function_symbol(&isym)) { |
226 | ++coff_symbol_count; |
227 | if (asym->name.short_name[0] != 0) |
228 | coff_symstr_len += coff_short_name_len(asym->name.short_name) + 1; |
229 | } |
230 | |
231 | i += asym->number_of_aux_symbols; |
232 | } |
233 | |
234 | coff_symbol_size = (coff_symbol_count + 1) * sizeof(struct coff_symbol); |
235 | coff_symbols = ((struct coff_symbol *)backtrace_alloc(state, coff_symbol_size, |
236 | error_callback, data)); |
237 | if (coff_symbols == NULL) return 0; |
238 | |
239 | if (coff_symstr_len > 0) { |
240 | coff_symstr = |
241 | ((char *)backtrace_alloc(state, coff_symstr_len, error_callback, data)); |
242 | if (coff_symstr == NULL) { |
243 | backtrace_free(state, coff_symbols, coff_symbol_size, error_callback, |
244 | data); |
245 | return 0; |
246 | } |
247 | } else |
248 | coff_symstr = NULL; |
249 | |
250 | coff_sym = coff_symbols; |
251 | coff_str = coff_symstr; |
252 | for (i = 0; i < syms_count; ++i) { |
253 | const b_coff_external_symbol *asym = &syms[i]; |
254 | b_coff_internal_symbol isym; |
255 | |
256 | if (coff_expand_symbol(&isym, asym, sects_num, strtab, strtab_size)) { |
257 | abort(); |
258 | } |
259 | if (coff_is_function_symbol(&isym)) { |
260 | const char *name; |
261 | int16_t secnum; |
262 | |
263 | if (asym->name.short_name[0] != 0) { |
264 | size_t len = coff_short_name_len(isym.name); |
265 | name = coff_str; |
266 | memcpy(coff_str, isym.name, len); |
267 | coff_str[len] = 0; |
268 | coff_str += len + 1; |
269 | } else |
270 | name = isym.name; |
271 | |
272 | if (!is_64) { |
273 | if (name[0] == '_') name++; |
274 | } |
275 | |
276 | secnum = coff_read2(asym->section_number); |
277 | |
278 | coff_sym->name = name; |
279 | coff_sym->address = (coff_read4(asym->value) + |
280 | sects[secnum - 1].virtual_address + base_address); |
281 | coff_sym++; |
282 | } |
283 | |
284 | i += asym->number_of_aux_symbols; |
285 | } |
286 | |
287 | coff_sym->name = NULL; |
288 | coff_sym->address = -1; |
289 | |
290 | backtrace_qsort(coff_symbols, coff_symbol_count, sizeof(struct coff_symbol), |
291 | coff_symbol_compare); |
292 | |
293 | sdata->next = NULL; |
294 | sdata->symbols = coff_symbols; |
295 | sdata->count = coff_symbol_count; |
296 | |
297 | return 1; |
298 | } |
299 | |
300 | static void coff_add_syminfo_data(struct backtrace_state *state, |
301 | struct coff_syminfo_data *sdata) { |
302 | if (!state->threaded) { |
303 | struct coff_syminfo_data **pp; |
304 | |
305 | for (pp = (struct coff_syminfo_data **)(void *)&state->syminfo_data; |
306 | *pp != NULL; pp = &(*pp)->next) |
307 | ; |
308 | *pp = sdata; |
309 | } else { |
310 | while (1) { |
311 | struct coff_syminfo_data **pp; |
312 | |
313 | pp = (struct coff_syminfo_data **)(void *)&state->syminfo_data; |
314 | |
315 | while (1) { |
316 | struct coff_syminfo_data *p; |
317 | |
318 | p = backtrace_atomic_load_pointer(pp); |
319 | |
320 | if (p == NULL) break; |
321 | |
322 | pp = &p->next; |
323 | } |
324 | |
325 | if (__sync_bool_compare_and_swap(pp, NULL, sdata)) break; |
326 | } |
327 | } |
328 | } |
329 | |
330 | static int coff_symbol_search(const void *vkey, const void *ventry) { |
331 | const uintptr_t *key = (const uintptr_t *)vkey; |
332 | const struct coff_symbol *entry = (const struct coff_symbol *)ventry; |
333 | uintptr_t addr; |
334 | |
335 | addr = *key; |
336 | if (addr < entry->address) |
337 | return -1; |
338 | else if (addr >= entry[1].address) |
339 | return 1; |
340 | else |
341 | return 0; |
342 | } |
343 | |
344 | static void coff_syminfo( |
345 | struct backtrace_state *state, uintptr_t addr, |
346 | backtrace_syminfo_callback callback, |
347 | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data) { |
348 | struct coff_syminfo_data *sdata; |
349 | struct coff_symbol *sym = NULL; |
350 | |
351 | if (!state->threaded) { |
352 | for (sdata = (struct coff_syminfo_data *)state->syminfo_data; sdata != NULL; |
353 | sdata = sdata->next) { |
354 | sym = ((struct coff_symbol *)bsearch(&addr, sdata->symbols, sdata->count, |
355 | sizeof(struct coff_symbol), |
356 | coff_symbol_search)); |
357 | if (sym != NULL) break; |
358 | } |
359 | } else { |
360 | struct coff_syminfo_data **pp; |
361 | |
362 | pp = (struct coff_syminfo_data **)(void *)&state->syminfo_data; |
363 | while (1) { |
364 | sdata = backtrace_atomic_load_pointer(pp); |
365 | if (sdata == NULL) break; |
366 | |
367 | sym = ((struct coff_symbol *)bsearch(&addr, sdata->symbols, sdata->count, |
368 | sizeof(struct coff_symbol), |
369 | coff_symbol_search)); |
370 | if (sym != NULL) break; |
371 | |
372 | pp = &sdata->next; |
373 | } |
374 | } |
375 | |
376 | if (sym == NULL) |
377 | callback(data, addr, NULL, 0, 0); |
378 | else |
379 | callback(data, addr, sym->name, sym->address, 0); |
380 | } |
381 | |
382 | static int coff_add(struct backtrace_state *state, int descriptor, |
383 | backtrace_error_callback error_callback, void *data, |
384 | fileline *fileline_fn, int *found_sym, int *found_dwarf) { |
385 | struct backtrace_view fhdr_view; |
386 | off_t fhdr_off; |
387 | int magic_ok; |
388 | b_coff_file_header fhdr; |
389 | off_t opt_sects_off; |
390 | size_t opt_sects_size; |
391 | unsigned int sects_num; |
392 | struct backtrace_view sects_view; |
393 | int sects_view_valid; |
394 | const b_coff_optional_header *opt_hdr; |
395 | const b_coff_section_header *sects; |
396 | struct backtrace_view str_view; |
397 | int str_view_valid; |
398 | size_t str_size; |
399 | off_t str_off; |
400 | struct backtrace_view syms_view; |
401 | off_t syms_off; |
402 | size_t syms_size; |
403 | int syms_view_valid; |
404 | unsigned int syms_num; |
405 | unsigned int i; |
406 | struct debug_section_info sections[DEBUG_MAX]; |
407 | off_t min_offset; |
408 | off_t max_offset; |
409 | struct backtrace_view debug_view; |
410 | int debug_view_valid; |
411 | int is_64; |
412 | uintptr_t image_base; |
413 | struct dwarf_sections dwarf_sections; |
414 | |
415 | *found_sym = 0; |
416 | *found_dwarf = 0; |
417 | |
418 | sects_view_valid = 0; |
419 | syms_view_valid = 0; |
420 | str_view_valid = 0; |
421 | debug_view_valid = 0; |
422 | |
423 | if (!backtrace_get_view(state, descriptor, 0, 0x40, error_callback, data, |
424 | &fhdr_view)) |
425 | goto fail; |
426 | |
427 | { |
428 | const unsigned char *vptr = fhdr_view.data; |
429 | |
430 | if (vptr[0] == 'M' && vptr[1] == 'Z') |
431 | fhdr_off = coff_read4(vptr + 0x3c); |
432 | else |
433 | fhdr_off = 0; |
434 | } |
435 | |
436 | backtrace_release_view(state, &fhdr_view, error_callback, data); |
437 | |
438 | if (!backtrace_get_view(state, descriptor, fhdr_off, |
439 | sizeof(b_coff_file_header) + 4, error_callback, data, |
440 | &fhdr_view)) |
441 | goto fail; |
442 | |
443 | if (fhdr_off != 0) { |
444 | const char *magic = (const char *)fhdr_view.data; |
445 | magic_ok = memcmp(magic, "PE\0", 4) == 0; |
446 | fhdr_off += 4; |
447 | |
448 | memcpy(&fhdr, fhdr_view.data + 4, sizeof fhdr); |
449 | } else { |
450 | memcpy(&fhdr, fhdr_view.data, sizeof fhdr); |
451 | |
452 | magic_ok = 0; |
453 | } |
454 | backtrace_release_view(state, &fhdr_view, error_callback, data); |
455 | |
456 | if (!magic_ok) { |
457 | error_callback(data, "executable file is not COFF", 0); |
458 | goto fail; |
459 | } |
460 | |
461 | sects_num = fhdr.number_of_sections; |
462 | syms_num = fhdr.number_of_symbols; |
463 | |
464 | opt_sects_off = fhdr_off + sizeof(fhdr); |
465 | opt_sects_size = (fhdr.size_of_optional_header + |
466 | sects_num * sizeof(b_coff_section_header)); |
467 | |
468 | if (!backtrace_get_view(state, descriptor, opt_sects_off, opt_sects_size, |
469 | error_callback, data, §s_view)) |
470 | goto fail; |
471 | sects_view_valid = 1; |
472 | opt_hdr = (const b_coff_optional_header *)sects_view.data; |
473 | sects = (const b_coff_section_header *)(sects_view.data + |
474 | fhdr.size_of_optional_header); |
475 | |
476 | is_64 = 0; |
477 | if (fhdr.size_of_optional_header > sizeof(*opt_hdr)) { |
478 | if (opt_hdr->magic == PE_MAGIC) |
479 | image_base = opt_hdr->u.pe.image_base; |
480 | else if (opt_hdr->magic == PEP_MAGIC) { |
481 | image_base = opt_hdr->u.pep.image_base; |
482 | is_64 = 1; |
483 | } else { |
484 | error_callback(data, "bad magic in PE optional header", 0); |
485 | goto fail; |
486 | } |
487 | } else |
488 | image_base = 0; |
489 | |
490 | if (fhdr.pointer_to_symbol_table == 0) { |
491 | str_off = 0; |
492 | str_size = 0; |
493 | syms_num = 0; |
494 | syms_size = 0; |
495 | } else { |
496 | syms_off = fhdr.pointer_to_symbol_table; |
497 | syms_size = syms_num * SYM_SZ; |
498 | |
499 | if (!backtrace_get_view(state, descriptor, syms_off, syms_size + 4, |
500 | error_callback, data, &syms_view)) |
501 | goto fail; |
502 | syms_view_valid = 1; |
503 | |
504 | str_size = coff_read4(syms_view.data + syms_size); |
505 | |
506 | str_off = syms_off + syms_size; |
507 | |
508 | if (str_size > 4) { |
509 | if (!backtrace_get_view(state, descriptor, str_off, str_size, |
510 | error_callback, data, &str_view)) |
511 | goto fail; |
512 | str_view_valid = 1; |
513 | } |
514 | } |
515 | |
516 | memset(sections, 0, sizeof sections); |
517 | |
518 | for (i = 0; i < sects_num; ++i) { |
519 | const b_coff_section_header *s = sects + i; |
520 | unsigned int str_off; |
521 | int j; |
522 | |
523 | if (s->name[0] == '/') { |
524 | str_off = atoi(s->name + 1); |
525 | } else |
526 | str_off = 0; |
527 | |
528 | for (j = 0; j < (int)DEBUG_MAX; ++j) { |
529 | const char *dbg_name = debug_section_names[j]; |
530 | int match; |
531 | |
532 | if (str_off != 0) |
533 | match = coff_long_name_eq(dbg_name, str_off, &str_view); |
534 | else |
535 | match = coff_short_name_eq(dbg_name, s->name); |
536 | if (match) { |
537 | sections[j].offset = s->pointer_to_raw_data; |
538 | sections[j].size = s->virtual_size <= s->size_of_raw_data |
539 | ? s->virtual_size |
540 | : s->size_of_raw_data; |
541 | break; |
542 | } |
543 | } |
544 | } |
545 | |
546 | if (syms_num != 0) { |
547 | struct coff_syminfo_data *sdata; |
548 | |
549 | sdata = ((struct coff_syminfo_data *)backtrace_alloc(state, sizeof *sdata, |
550 | error_callback, data)); |
551 | if (sdata == NULL) goto fail; |
552 | |
553 | if (!coff_initialize_syminfo(state, image_base, is_64, sects, sects_num, |
554 | syms_view.data, syms_size, str_view.data, |
555 | str_size, error_callback, data, sdata)) { |
556 | backtrace_free(state, sdata, sizeof *sdata, error_callback, data); |
557 | goto fail; |
558 | } |
559 | |
560 | *found_sym = 1; |
561 | |
562 | coff_add_syminfo_data(state, sdata); |
563 | } |
564 | |
565 | backtrace_release_view(state, §s_view, error_callback, data); |
566 | sects_view_valid = 0; |
567 | if (syms_view_valid) { |
568 | backtrace_release_view(state, &syms_view, error_callback, data); |
569 | syms_view_valid = 0; |
570 | } |
571 | |
572 | min_offset = 0; |
573 | max_offset = 0; |
574 | for (i = 0; i < (int)DEBUG_MAX; ++i) { |
575 | off_t end; |
576 | |
577 | if (sections[i].size == 0) continue; |
578 | if (min_offset == 0 || sections[i].offset < min_offset) |
579 | min_offset = sections[i].offset; |
580 | end = sections[i].offset + sections[i].size; |
581 | if (end > max_offset) max_offset = end; |
582 | } |
583 | if (min_offset == 0 || max_offset == 0) { |
584 | if (!backtrace_close(descriptor, error_callback, data)) goto fail; |
585 | *fileline_fn = coff_nodebug; |
586 | return 1; |
587 | } |
588 | |
589 | if (!backtrace_get_view(state, descriptor, min_offset, |
590 | max_offset - min_offset, error_callback, data, |
591 | &debug_view)) |
592 | goto fail; |
593 | debug_view_valid = 1; |
594 | |
595 | if (!backtrace_close(descriptor, error_callback, data)) goto fail; |
596 | descriptor = -1; |
597 | |
598 | for (i = 0; i < (int)DEBUG_MAX; ++i) { |
599 | size_t size = sections[i].size; |
600 | dwarf_sections.size[i] = size; |
601 | if (size == 0) |
602 | dwarf_sections.data[i] = NULL; |
603 | else |
604 | dwarf_sections.data[i] = ((const unsigned char *)debug_view.data + |
605 | (sections[i].offset - min_offset)); |
606 | } |
607 | |
608 | if (!backtrace_dwarf_add(state, 0, &dwarf_sections, 0, NULL, error_callback, |
609 | data, fileline_fn, NULL)) |
610 | goto fail; |
611 | |
612 | *found_dwarf = 1; |
613 | |
614 | return 1; |
615 | |
616 | fail: |
617 | if (sects_view_valid) |
618 | backtrace_release_view(state, §s_view, error_callback, data); |
619 | if (str_view_valid) |
620 | backtrace_release_view(state, &str_view, error_callback, data); |
621 | if (syms_view_valid) |
622 | backtrace_release_view(state, &syms_view, error_callback, data); |
623 | if (debug_view_valid) |
624 | backtrace_release_view(state, &debug_view, error_callback, data); |
625 | if (descriptor != -1) backtrace_close(descriptor, error_callback, data); |
626 | return 0; |
627 | } |
628 | |
629 | int backtrace_initialize(struct backtrace_state *state, |
630 | const char *filename ATTRIBUTE_UNUSED, int descriptor, |
631 | backtrace_error_callback error_callback, void *data, |
632 | fileline *fileline_fn) { |
633 | int ret; |
634 | int found_sym; |
635 | int found_dwarf; |
636 | fileline coff_fileline_fn; |
637 | |
638 | ret = coff_add(state, descriptor, error_callback, data, &coff_fileline_fn, |
639 | &found_sym, &found_dwarf); |
640 | if (!ret) return 0; |
641 | |
642 | if (!state->threaded) { |
643 | if (found_sym) |
644 | state->syminfo_fn = coff_syminfo; |
645 | else if (state->syminfo_fn == NULL) |
646 | state->syminfo_fn = coff_nosyms; |
647 | } else { |
648 | if (found_sym) |
649 | backtrace_atomic_store_pointer(&state->syminfo_fn, coff_syminfo); |
650 | else |
651 | (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL, coff_nosyms); |
652 | } |
653 | |
654 | if (!state->threaded) { |
655 | if (state->fileline_fn == NULL || state->fileline_fn == coff_nodebug) |
656 | *fileline_fn = coff_fileline_fn; |
657 | } else { |
658 | fileline current_fn; |
659 | |
660 | current_fn = backtrace_atomic_load_pointer(&state->fileline_fn); |
661 | if (current_fn == NULL || current_fn == coff_nodebug) |
662 | *fileline_fn = coff_fileline_fn; |
663 | } |
664 | |
665 | return 1; |
666 | } |
667 | |
668 | // read.c: |
669 | #include <errno.h> |
670 | #include <stdlib.h> |
671 | #include <sys/types.h> |
672 | #include <unistd.h> |
673 | |
674 | int backtrace_get_view(struct backtrace_state *state, int descriptor, |
675 | off_t offset, uint64_t size, |
676 | backtrace_error_callback error_callback, void *data, |
677 | struct backtrace_view *view) { |
678 | uint64_t got; |
679 | ssize_t r; |
680 | |
681 | if ((uint64_t)(size_t)size != size) { |
682 | error_callback(data, "file size too large", 0); |
683 | return 0; |
684 | } |
685 | |
686 | if (lseek(descriptor, offset, SEEK_SET) < 0) { |
687 | error_callback(data, "lseek", errno); |
688 | return 0; |
689 | } |
690 | |
691 | view->base = backtrace_alloc(state, size, error_callback, data); |
692 | if (view->base == NULL) return 0; |
693 | view->data = view->base; |
694 | view->len = size; |
695 | |
696 | got = 0; |
697 | while (got < size) { |
698 | r = read(descriptor, view->base, size - got); |
699 | if (r < 0) { |
700 | error_callback(data, "read", errno); |
701 | free(view->base); |
702 | return 0; |
703 | } |
704 | if (r == 0) break; |
705 | got += (uint64_t)r; |
706 | } |
707 | |
708 | if (got < size) { |
709 | error_callback(data, "file too short", 0); |
710 | free(view->base); |
711 | return 0; |
712 | } |
713 | |
714 | return 1; |
715 | } |
716 | |
717 | void backtrace_release_view(struct backtrace_state *state, |
718 | struct backtrace_view *view, |
719 | backtrace_error_callback error_callback, |
720 | void *data) { |
721 | backtrace_free(state, view->base, view->len, error_callback, data); |
722 | view->data = NULL; |
723 | view->base = NULL; |
724 | } |
725 | |
726 | // alloc.c: |
727 | #include <errno.h> |
728 | #include <stdlib.h> |
729 | #include <sys/types.h> |
730 | |
731 | void *backtrace_alloc(struct backtrace_state *state ATTRIBUTE_UNUSED, |
732 | size_t size, backtrace_error_callback error_callback, |
733 | void *data) { |
734 | void *ret; |
735 | |
736 | ret = malloc(size); |
737 | if (ret == NULL) { |
738 | if (error_callback) error_callback(data, "malloc", errno); |
739 | } |
740 | return ret; |
741 | } |
742 | |
743 | void backtrace_free(struct backtrace_state *state ATTRIBUTE_UNUSED, void *p, |
744 | size_t size ATTRIBUTE_UNUSED, |
745 | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
746 | void *data ATTRIBUTE_UNUSED) { |
747 | free(p); |
748 | } |
749 | |
750 | void *backtrace_vector_grow(struct backtrace_state *state ATTRIBUTE_UNUSED, |
751 | size_t size, |
752 | backtrace_error_callback error_callback, void *data, |
753 | struct backtrace_vector *vec) { |
754 | void *ret; |
755 | |
756 | if (size > vec->alc) { |
757 | size_t alc; |
758 | void *base; |
759 | |
760 | if (vec->size == 0) |
761 | alc = 32 * size; |
762 | else if (vec->size >= 4096) |
763 | alc = vec->size + 4096; |
764 | else |
765 | alc = 2 * vec->size; |
766 | |
767 | if (alc < vec->size + size) alc = vec->size + size; |
768 | |
769 | base = realloc(vec->base, alc); |
770 | if (base == NULL) { |
771 | error_callback(data, "realloc", errno); |
772 | return NULL; |
773 | } |
774 | |
775 | vec->base = base; |
776 | vec->alc = alc - vec->size; |
777 | } |
778 | |
779 | ret = (char *)vec->base + vec->size; |
780 | vec->size += size; |
781 | vec->alc -= size; |
782 | return ret; |
783 | } |
784 | |
785 | void *backtrace_vector_finish(struct backtrace_state *state, |
786 | struct backtrace_vector *vec, |
787 | backtrace_error_callback error_callback, |
788 | void *data) { |
789 | void *ret; |
790 | |
791 | if (!backtrace_vector_release(state, vec, error_callback, data)) return NULL; |
792 | ret = vec->base; |
793 | vec->base = NULL; |
794 | vec->size = 0; |
795 | vec->alc = 0; |
796 | return ret; |
797 | } |
798 | |
799 | int backtrace_vector_release(struct backtrace_state *state ATTRIBUTE_UNUSED, |
800 | struct backtrace_vector *vec, |
801 | backtrace_error_callback error_callback, |
802 | void *data) { |
803 | vec->alc = 0; |
804 | |
805 | if (vec->size == 0) { |
806 | free(vec->base); |
807 | vec->base = NULL; |
808 | return 1; |
809 | } |
810 | |
811 | vec->base = realloc(vec->base, vec->size); |
812 | if (vec->base == NULL) { |
813 | error_callback(data, "realloc", errno); |
814 | return 0; |
815 | } |
816 | |
817 | return 1; |
818 | } |