|  | /* 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; | 
|  | } |