diff options
Diffstat (limited to 'libbacktrace/pecoff.c')
-rw-r--r-- | libbacktrace/pecoff.c | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/libbacktrace/pecoff.c b/libbacktrace/pecoff.c new file mode 100644 index 0000000..7202519 --- /dev/null +++ b/libbacktrace/pecoff.c @@ -0,0 +1,935 @@ +/* pecoff.c -- Get debug data from a PE/COFFF file for backtraces. + Copyright (C) 2015-2021 Free Software Foundation, Inc. + Adapted from elf.c by Tristan Gingold, AdaCore. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + (1) Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + (2) Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include "backtrace.h" +#include "internal.h" + +/* Coff file header. */ + +typedef struct { + uint16_t machine; + uint16_t number_of_sections; + uint32_t time_date_stamp; + uint32_t pointer_to_symbol_table; + uint32_t number_of_symbols; + uint16_t size_of_optional_header; + uint16_t characteristics; +} b_coff_file_header; + +/* Coff optional header. */ + +typedef struct { + uint16_t magic; + uint8_t major_linker_version; + uint8_t minor_linker_version; + uint32_t size_of_code; + uint32_t size_of_initialized_data; + uint32_t size_of_uninitialized_data; + uint32_t address_of_entry_point; + uint32_t base_of_code; + union { + struct { + uint32_t base_of_data; + uint32_t image_base; + } pe; + struct { + uint64_t image_base; + } pep; + } u; +} b_coff_optional_header; + +/* Values of magic in optional header. */ + +#define PE_MAGIC 0x10b /* PE32 executable. */ +#define PEP_MAGIC 0x20b /* PE32+ executable (for 64bit targets). */ + +/* Coff section header. */ + +typedef struct { + char name[8]; + uint32_t virtual_size; + uint32_t virtual_address; + uint32_t size_of_raw_data; + uint32_t pointer_to_raw_data; + uint32_t pointer_to_relocations; + uint32_t pointer_to_line_numbers; + uint16_t number_of_relocations; + uint16_t number_of_line_numbers; + uint32_t characteristics; +} b_coff_section_header; + +/* Coff symbol name. */ + +typedef union { + char short_name[8]; + struct { + unsigned char zeroes[4]; + unsigned char off[4]; + } long_name; +} b_coff_name; + +/* Coff symbol (external representation which is unaligned). */ + +typedef struct { + b_coff_name name; + unsigned char value[4]; + unsigned char section_number[2]; + unsigned char type[2]; + unsigned char storage_class; + unsigned char number_of_aux_symbols; +} b_coff_external_symbol; + +/* Symbol types. */ + +#define N_TBSHFT 4 /* Shift for the derived type. */ +#define IMAGE_SYM_DTYPE_FUNCTION 2 /* Function derived type. */ + +/* Size of a coff symbol. */ + +#define SYM_SZ 18 + +/* Coff symbol, internal representation (aligned). */ + +typedef struct { + const char *name; + uint32_t value; + int16_t sec; + uint16_t type; + uint16_t sc; +} b_coff_internal_symbol; + +/* Names of sections, indexed by enum dwarf_section in internal.h. */ + +static const char * const debug_section_names[DEBUG_MAX] = +{ + ".debug_info", + ".debug_line", + ".debug_abbrev", + ".debug_ranges", + ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" +}; + +/* Information we gather for the sections we care about. */ + +struct debug_section_info +{ + /* Section file offset. */ + off_t offset; + /* Section size. */ + size_t size; +}; + +/* Information we keep for an coff symbol. */ + +struct coff_symbol +{ + /* The name of the symbol. */ + const char *name; + /* The address of the symbol. */ + uintptr_t address; +}; + +/* Information to pass to coff_syminfo. */ + +struct coff_syminfo_data +{ + /* Symbols for the next module. */ + struct coff_syminfo_data *next; + /* The COFF symbols, sorted by address. */ + struct coff_symbol *symbols; + /* The number of symbols. */ + size_t count; +}; + +/* A dummy callback function used when we can't find any debug info. */ + +static int +coff_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED, + uintptr_t pc ATTRIBUTE_UNUSED, + backtrace_full_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, "no debug info in PE/COFF executable", -1); + return 0; +} + +/* A dummy callback function used when we can't find a symbol + table. */ + +static void +coff_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED, + uintptr_t addr ATTRIBUTE_UNUSED, + backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, "no symbol table in PE/COFF executable", -1); +} + +/* Read a potentially unaligned 4 byte word at P, using native endianness. */ + +static uint32_t +coff_read4 (const unsigned char *p) +{ + uint32_t res; + + memcpy (&res, p, 4); + return res; +} + +/* Read a potentially unaligned 2 byte word at P, using native endianness. + All 2 byte word in symbols are always aligned, but for coherency all + fields are declared as char arrays. */ + +static uint16_t +coff_read2 (const unsigned char *p) +{ + uint16_t res; + + memcpy (&res, p, sizeof (res)); + return res; +} + +/* Return the length (without the trailing 0) of a COFF short name. */ + +static size_t +coff_short_name_len (const char *name) +{ + int i; + + for (i = 0; i < 8; i++) + if (name[i] == 0) + return i; + return 8; +} + +/* Return true iff COFF short name CNAME is the same as NAME (a NUL-terminated + string). */ + +static int +coff_short_name_eq (const char *name, const char *cname) +{ + int i; + + for (i = 0; i < 8; i++) + { + if (name[i] != cname[i]) + return 0; + if (name[i] == 0) + return 1; + } + return name[8] == 0; +} + +/* Return true iff NAME is the same as string at offset OFF. */ + +static int +coff_long_name_eq (const char *name, unsigned int off, + struct backtrace_view *str_view) +{ + if (off >= str_view->len) + return 0; + return strcmp (name, (const char *)str_view->data + off) == 0; +} + +/* Compare struct coff_symbol for qsort. */ + +static int +coff_symbol_compare (const void *v1, const void *v2) +{ + const struct coff_symbol *e1 = (const struct coff_symbol *) v1; + const struct coff_symbol *e2 = (const struct coff_symbol *) v2; + + if (e1->address < e2->address) + return -1; + else if (e1->address > e2->address) + return 1; + else + return 0; +} + +/* Convert SYM to internal (and aligned) format ISYM, using string table + from STRTAB and STRTAB_SIZE, and number of sections SECTS_NUM. + Return -1 in case of error (invalid section number or string index). */ + +static int +coff_expand_symbol (b_coff_internal_symbol *isym, + const b_coff_external_symbol *sym, + uint16_t sects_num, + const unsigned char *strtab, size_t strtab_size) +{ + isym->type = coff_read2 (sym->type); + isym->sec = coff_read2 (sym->section_number); + isym->sc = sym->storage_class; + + if (isym->sec > 0 && (uint16_t) isym->sec > sects_num) + return -1; + if (sym->name.short_name[0] != 0) + isym->name = sym->name.short_name; + else + { + uint32_t off = coff_read4 (sym->name.long_name.off); + + if (off >= strtab_size) + return -1; + isym->name = (const char *) strtab + off; + } + return 0; +} + +/* Return true iff SYM is a defined symbol for a function. Data symbols + aren't considered because they aren't easily identified (same type as + section names, presence of symbols defined by the linker script). */ + +static int +coff_is_function_symbol (const b_coff_internal_symbol *isym) +{ + return (isym->type >> N_TBSHFT) == IMAGE_SYM_DTYPE_FUNCTION + && isym->sec > 0; +} + +/* Initialize the symbol table info for coff_syminfo. */ + +static int +coff_initialize_syminfo (struct backtrace_state *state, + uintptr_t base_address, int is_64, + const b_coff_section_header *sects, size_t sects_num, + const b_coff_external_symbol *syms, size_t syms_size, + const unsigned char *strtab, size_t strtab_size, + backtrace_error_callback error_callback, + void *data, struct coff_syminfo_data *sdata) +{ + size_t syms_count; + char *coff_symstr; + size_t coff_symstr_len; + size_t coff_symbol_count; + size_t coff_symbol_size; + struct coff_symbol *coff_symbols; + struct coff_symbol *coff_sym; + char *coff_str; + size_t i; + + syms_count = syms_size / SYM_SZ; + + /* We only care about function symbols. Count them. Also count size of + strings for in-symbol names. */ + coff_symbol_count = 0; + coff_symstr_len = 0; + for (i = 0; i < syms_count; ++i) + { + const b_coff_external_symbol *asym = &syms[i]; + b_coff_internal_symbol isym; + + if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size) < 0) + { + error_callback (data, "invalid section or offset in coff symbol", 0); + return 0; + } + if (coff_is_function_symbol (&isym)) + { + ++coff_symbol_count; + if (asym->name.short_name[0] != 0) + coff_symstr_len += coff_short_name_len (asym->name.short_name) + 1; + } + + i += asym->number_of_aux_symbols; + } + + coff_symbol_size = (coff_symbol_count + 1) * sizeof (struct coff_symbol); + coff_symbols = ((struct coff_symbol *) + backtrace_alloc (state, coff_symbol_size, error_callback, + data)); + if (coff_symbols == NULL) + return 0; + + /* Allocate memory for symbols strings. */ + if (coff_symstr_len > 0) + { + coff_symstr = ((char *) + backtrace_alloc (state, coff_symstr_len, error_callback, + data)); + if (coff_symstr == NULL) + { + backtrace_free (state, coff_symbols, coff_symbol_size, + error_callback, data); + return 0; + } + } + else + coff_symstr = NULL; + + /* Copy symbols. */ + coff_sym = coff_symbols; + coff_str = coff_symstr; + for (i = 0; i < syms_count; ++i) + { + const b_coff_external_symbol *asym = &syms[i]; + b_coff_internal_symbol isym; + + if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size)) + { + /* Should not fail, as it was already tested in the previous + loop. */ + abort (); + } + if (coff_is_function_symbol (&isym)) + { + const char *name; + int16_t secnum; + + if (asym->name.short_name[0] != 0) + { + size_t len = coff_short_name_len (isym.name); + name = coff_str; + memcpy (coff_str, isym.name, len); + coff_str[len] = 0; + coff_str += len + 1; + } + else + name = isym.name; + + if (!is_64) + { + /* Strip leading '_'. */ + if (name[0] == '_') + name++; + } + + /* Symbol value is section relative, so we need to read the address + of its section. */ + secnum = coff_read2 (asym->section_number); + + coff_sym->name = name; + coff_sym->address = (coff_read4 (asym->value) + + sects[secnum - 1].virtual_address + + base_address); + coff_sym++; + } + + i += asym->number_of_aux_symbols; + } + + /* End of symbols marker. */ + coff_sym->name = NULL; + coff_sym->address = -1; + + backtrace_qsort (coff_symbols, coff_symbol_count, + sizeof (struct coff_symbol), coff_symbol_compare); + + sdata->next = NULL; + sdata->symbols = coff_symbols; + sdata->count = coff_symbol_count; + + return 1; +} + +/* Add EDATA to the list in STATE. */ + +static void +coff_add_syminfo_data (struct backtrace_state *state, + struct coff_syminfo_data *sdata) +{ + if (!state->threaded) + { + struct coff_syminfo_data **pp; + + for (pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = sdata; + } + else + { + while (1) + { + struct coff_syminfo_data **pp; + + pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data; + + while (1) + { + struct coff_syminfo_data *p; + + p = backtrace_atomic_load_pointer (pp); + + if (p == NULL) + break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap (pp, NULL, sdata)) + break; + } + } +} + +/* Compare an ADDR against an elf_symbol for bsearch. We allocate one + extra entry in the array so that this can look safely at the next + entry. */ + +static int +coff_symbol_search (const void *vkey, const void *ventry) +{ + const uintptr_t *key = (const uintptr_t *) vkey; + const struct coff_symbol *entry = (const struct coff_symbol *) ventry; + uintptr_t addr; + + addr = *key; + if (addr < entry->address) + return -1; + else if (addr >= entry[1].address) + return 1; + else + return 0; +} + +/* Return the symbol name and value for an ADDR. */ + +static void +coff_syminfo (struct backtrace_state *state, uintptr_t addr, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, + void *data) +{ + struct coff_syminfo_data *sdata; + struct coff_symbol *sym = NULL; + + if (!state->threaded) + { + for (sdata = (struct coff_syminfo_data *) state->syminfo_data; + sdata != NULL; + sdata = sdata->next) + { + sym = ((struct coff_symbol *) + bsearch (&addr, sdata->symbols, sdata->count, + sizeof (struct coff_symbol), coff_symbol_search)); + if (sym != NULL) + break; + } + } + else + { + struct coff_syminfo_data **pp; + + pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data; + while (1) + { + sdata = backtrace_atomic_load_pointer (pp); + if (sdata == NULL) + break; + + sym = ((struct coff_symbol *) + bsearch (&addr, sdata->symbols, sdata->count, + sizeof (struct coff_symbol), coff_symbol_search)); + if (sym != NULL) + break; + + pp = &sdata->next; + } + } + + if (sym == NULL) + callback (data, addr, NULL, 0, 0); + else + callback (data, addr, sym->name, sym->address, 0); +} + +/* Add the backtrace data for one PE/COFF file. Returns 1 on success, + 0 on failure (in both cases descriptor is closed). */ + +static int +coff_add (struct backtrace_state *state, int descriptor, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym, int *found_dwarf) +{ + struct backtrace_view fhdr_view; + off_t fhdr_off; + int magic_ok; + b_coff_file_header fhdr; + off_t opt_sects_off; + size_t opt_sects_size; + unsigned int sects_num; + struct backtrace_view sects_view; + int sects_view_valid; + const b_coff_optional_header *opt_hdr; + const b_coff_section_header *sects; + struct backtrace_view str_view; + int str_view_valid; + size_t str_size; + off_t str_off; + struct backtrace_view syms_view; + off_t syms_off; + size_t syms_size; + int syms_view_valid; + unsigned int syms_num; + unsigned int i; + struct debug_section_info sections[DEBUG_MAX]; + off_t min_offset; + off_t max_offset; + struct backtrace_view debug_view; + int debug_view_valid; + int is_64; + uintptr_t image_base; + struct dwarf_sections dwarf_sections; + + *found_sym = 0; + *found_dwarf = 0; + + sects_view_valid = 0; + syms_view_valid = 0; + str_view_valid = 0; + debug_view_valid = 0; + + /* Map the MS-DOS stub (if any) and extract file header offset. */ + if (!backtrace_get_view (state, descriptor, 0, 0x40, error_callback, + data, &fhdr_view)) + goto fail; + + { + const unsigned char *vptr = fhdr_view.data; + + if (vptr[0] == 'M' && vptr[1] == 'Z') + fhdr_off = coff_read4 (vptr + 0x3c); + else + fhdr_off = 0; + } + + backtrace_release_view (state, &fhdr_view, error_callback, data); + + /* Map the coff file header. */ + if (!backtrace_get_view (state, descriptor, fhdr_off, + sizeof (b_coff_file_header) + 4, + error_callback, data, &fhdr_view)) + goto fail; + + if (fhdr_off != 0) + { + const char *magic = (const char *) fhdr_view.data; + magic_ok = memcmp (magic, "PE\0", 4) == 0; + fhdr_off += 4; + + memcpy (&fhdr, fhdr_view.data + 4, sizeof fhdr); + } + else + { + memcpy (&fhdr, fhdr_view.data, sizeof fhdr); + /* TODO: test fhdr.machine for coff but non-PE platforms. */ + magic_ok = 0; + } + backtrace_release_view (state, &fhdr_view, error_callback, data); + + if (!magic_ok) + { + error_callback (data, "executable file is not COFF", 0); + goto fail; + } + + sects_num = fhdr.number_of_sections; + syms_num = fhdr.number_of_symbols; + + opt_sects_off = fhdr_off + sizeof (fhdr); + opt_sects_size = (fhdr.size_of_optional_header + + sects_num * sizeof (b_coff_section_header)); + + /* To translate PC to file/line when using DWARF, we need to find + the .debug_info and .debug_line sections. */ + + /* Read the optional header and the section headers. */ + + if (!backtrace_get_view (state, descriptor, opt_sects_off, opt_sects_size, + error_callback, data, §s_view)) + goto fail; + sects_view_valid = 1; + opt_hdr = (const b_coff_optional_header *) sects_view.data; + sects = (const b_coff_section_header *) + (sects_view.data + fhdr.size_of_optional_header); + + is_64 = 0; + if (fhdr.size_of_optional_header > sizeof (*opt_hdr)) + { + if (opt_hdr->magic == PE_MAGIC) + image_base = opt_hdr->u.pe.image_base; + else if (opt_hdr->magic == PEP_MAGIC) + { + image_base = opt_hdr->u.pep.image_base; + is_64 = 1; + } + else + { + error_callback (data, "bad magic in PE optional header", 0); + goto fail; + } + } + else + image_base = 0; + + /* Read the symbol table and the string table. */ + + if (fhdr.pointer_to_symbol_table == 0) + { + /* No symbol table, no string table. */ + str_off = 0; + str_size = 0; + syms_num = 0; + syms_size = 0; + } + else + { + /* Symbol table is followed by the string table. The string table + starts with its length (on 4 bytes). + Map the symbol table and the length of the string table. */ + syms_off = fhdr.pointer_to_symbol_table; + syms_size = syms_num * SYM_SZ; + + if (!backtrace_get_view (state, descriptor, syms_off, syms_size + 4, + error_callback, data, &syms_view)) + goto fail; + syms_view_valid = 1; + + str_size = coff_read4 (syms_view.data + syms_size); + + str_off = syms_off + syms_size; + + if (str_size > 4) + { + /* Map string table (including the length word). */ + + if (!backtrace_get_view (state, descriptor, str_off, str_size, + error_callback, data, &str_view)) + goto fail; + str_view_valid = 1; + } + } + + memset (sections, 0, sizeof sections); + + /* Look for the symbol table. */ + for (i = 0; i < sects_num; ++i) + { + const b_coff_section_header *s = sects + i; + unsigned int str_off; + int j; + + if (s->name[0] == '/') + { + /* Extended section name. */ + str_off = atoi (s->name + 1); + } + else + str_off = 0; + + for (j = 0; j < (int) DEBUG_MAX; ++j) + { + const char *dbg_name = debug_section_names[j]; + int match; + + if (str_off != 0) + match = coff_long_name_eq (dbg_name, str_off, &str_view); + else + match = coff_short_name_eq (dbg_name, s->name); + if (match) + { + sections[j].offset = s->pointer_to_raw_data; + sections[j].size = s->virtual_size <= s->size_of_raw_data ? + s->virtual_size : s->size_of_raw_data; + break; + } + } + } + + if (syms_num != 0) + { + struct coff_syminfo_data *sdata; + + sdata = ((struct coff_syminfo_data *) + backtrace_alloc (state, sizeof *sdata, error_callback, data)); + if (sdata == NULL) + goto fail; + + if (!coff_initialize_syminfo (state, image_base, is_64, + sects, sects_num, + syms_view.data, syms_size, + str_view.data, str_size, + error_callback, data, sdata)) + { + backtrace_free (state, sdata, sizeof *sdata, error_callback, data); + goto fail; + } + + *found_sym = 1; + + coff_add_syminfo_data (state, sdata); + } + + backtrace_release_view (state, §s_view, error_callback, data); + sects_view_valid = 0; + if (syms_view_valid) + { + backtrace_release_view (state, &syms_view, error_callback, data); + syms_view_valid = 0; + } + + /* Read all the debug sections in a single view, since they are + probably adjacent in the file. We never release this view. */ + + min_offset = 0; + max_offset = 0; + for (i = 0; i < (int) DEBUG_MAX; ++i) + { + off_t end; + + if (sections[i].size == 0) + continue; + if (min_offset == 0 || sections[i].offset < min_offset) + min_offset = sections[i].offset; + end = sections[i].offset + sections[i].size; + if (end > max_offset) + max_offset = end; + } + if (min_offset == 0 || max_offset == 0) + { + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + *fileline_fn = coff_nodebug; + return 1; + } + + if (!backtrace_get_view (state, descriptor, min_offset, + max_offset - min_offset, + error_callback, data, &debug_view)) + goto fail; + debug_view_valid = 1; + + /* We've read all we need from the executable. */ + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + descriptor = -1; + + for (i = 0; i < (int) DEBUG_MAX; ++i) + { + size_t size = sections[i].size; + dwarf_sections.size[i] = size; + if (size == 0) + dwarf_sections.data[i] = NULL; + else + dwarf_sections.data[i] = ((const unsigned char *) debug_view.data + + (sections[i].offset - min_offset)); + } + + if (!backtrace_dwarf_add (state, /* base_address */ 0, &dwarf_sections, + 0, /* FIXME: is_bigendian */ + NULL, /* altlink */ + error_callback, data, fileline_fn, + NULL /* returned fileline_entry */)) + goto fail; + + *found_dwarf = 1; + + return 1; + + fail: + if (sects_view_valid) + backtrace_release_view (state, §s_view, error_callback, data); + if (str_view_valid) + backtrace_release_view (state, &str_view, error_callback, data); + if (syms_view_valid) + backtrace_release_view (state, &syms_view, error_callback, data); + if (debug_view_valid) + backtrace_release_view (state, &debug_view, error_callback, data); + if (descriptor != -1) + backtrace_close (descriptor, error_callback, data); + return 0; +} + +/* Initialize the backtrace data we need from an ELF executable. At + the ELF level, all we need to do is find the debug info + sections. */ + +int +backtrace_initialize (struct backtrace_state *state, + const char *filename ATTRIBUTE_UNUSED, int descriptor, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn) +{ + int ret; + int found_sym; + int found_dwarf; + fileline coff_fileline_fn; + + ret = coff_add (state, descriptor, error_callback, data, + &coff_fileline_fn, &found_sym, &found_dwarf); + if (!ret) + return 0; + + if (!state->threaded) + { + if (found_sym) + state->syminfo_fn = coff_syminfo; + else if (state->syminfo_fn == NULL) + state->syminfo_fn = coff_nosyms; + } + else + { + if (found_sym) + backtrace_atomic_store_pointer (&state->syminfo_fn, coff_syminfo); + else + (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, + coff_nosyms); + } + + if (!state->threaded) + { + if (state->fileline_fn == NULL || state->fileline_fn == coff_nodebug) + *fileline_fn = coff_fileline_fn; + } + else + { + fileline current_fn; + + current_fn = backtrace_atomic_load_pointer (&state->fileline_fn); + if (current_fn == NULL || current_fn == coff_nodebug) + *fileline_fn = coff_fileline_fn; + } + + return 1; +} |