| /* ********************************************************** |
| * Copyright (c) 2012-2024 Google, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * 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. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 VMWARE, INC. OR CONTRIBUTORS 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. |
| */ |
| |
| /* DRSyms DynamoRIO Extension */ |
| |
| /* Symbol lookup for DWARF format on Windows |
| * (generated by cygwin or mingw gcc) |
| * |
| * We use the PECOFF symbol table for symbol and address lookup. |
| * XXX: we do not look at exports separately: we assume they're in the table, |
| * unless the symbol table is stripped (i#1395). |
| * |
| * For line numbers, we use DWARF info if available. |
| */ |
| |
| #ifdef WINDOWS |
| # define _CRT_SECURE_NO_DEPRECATE 1 |
| #endif |
| |
| #include "dr_api.h" |
| #include "drsyms.h" |
| #include "drsyms_private.h" |
| #include "drsyms_obj.h" |
| |
| #include "dwarf.h" |
| #include "libdwarf.h" |
| |
| #include <windows.h> |
| #include <stdio.h> /* sscanf */ |
| #include <stdlib.h> /* qsort */ |
| |
| /* For debugging */ |
| static uint verbose = 0; |
| |
| #undef NOTIFY |
| #define NOTIFY(n, ...) \ |
| do { \ |
| if (verbose >= (n)) { \ |
| dr_fprintf(STDERR, __VA_ARGS__); \ |
| } \ |
| } while (0) |
| |
| #define NOTIFY_DWARF(de) \ |
| do { \ |
| if (verbose) { \ |
| dr_fprintf(STDERR, "drsyms: Dwarf error: %s\n", dwarf_errmsg(de)); \ |
| } \ |
| } while (0) |
| |
| /* MS tools use this value insted of the others */ |
| #define IMAGE_SYM_TYPE_FUNCTION 0x20 |
| |
| typedef struct _export_info_t { |
| const char *name; |
| app_pc addr; |
| } export_info_t; |
| |
| typedef struct _pecoff_data_t { |
| byte *map_base; |
| size_t map_size; |
| byte *preferred_base; |
| bool is_64; |
| IMAGE_SYMBOL *symbol_table; |
| uint symbol_count; |
| const char *string_table; |
| /* array of symbols sorted by address */ |
| IMAGE_SYMBOL **sorted_syms; |
| uint sorted_count; |
| /* array of section bases */ |
| size_t *section_base; |
| uint section_count; |
| /* stored section info */ |
| drsym_debug_kind_t debug_kind; |
| byte *debuglink; |
| /* i#1395: handle stripped MinGW */ |
| bool exports_only; |
| export_info_t *sorted_exports; |
| } pecoff_data_t; |
| |
| /* We synchronize all our operations but we assume the outer drsyms_windows |
| * grabs the lock before calling routines here |
| */ |
| |
| static void |
| drsym_pecoff_sort_symbols(pecoff_data_t *mod); |
| |
| static bool |
| drsym_pecoff_sort_exports(pecoff_data_t *mod); |
| |
| /****************************************************************************** |
| * Init and exit |
| */ |
| |
| void |
| drsym_obj_init(void) |
| { |
| } |
| |
| /* The string may not be null-terminated so we return the max size */ |
| static const char * |
| drsym_pecoff_get_section_name(pecoff_data_t *mod, IMAGE_SECTION_HEADER *sec, |
| size_t *max_size) |
| { |
| if (sec->Name[0] == '/') { |
| /* "/N" where N is index into string table for >8-char name */ |
| uint index; |
| if (sscanf((char *)sec->Name + 1, "%u", &index) == 1) { |
| const char *name = mod->string_table + index; |
| if (max_size != NULL) |
| *max_size = strlen(name); |
| return name; |
| } |
| } |
| if (max_size != NULL) |
| *max_size = sizeof(sec->Name); |
| return (const char *)sec->Name; |
| } |
| |
| void * |
| drsym_obj_mod_init_pre(byte *map_base, size_t map_size) |
| { |
| IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)map_base; |
| IMAGE_NT_HEADERS *nt; |
| IMAGE_SECTION_HEADER *sec; |
| uint i; |
| pecoff_data_t *mod; |
| bool is_mingw = false; |
| |
| if (dos->e_magic != IMAGE_DOS_SIGNATURE) |
| return NULL; |
| nt = (IMAGE_NT_HEADERS *)(((ptr_uint_t)dos) + dos->e_lfanew); |
| if (nt == NULL || nt->Signature != IMAGE_NT_SIGNATURE) |
| return NULL; |
| |
| mod = dr_global_alloc(sizeof(*mod)); |
| memset(mod, 0, sizeof(*mod)); |
| mod->map_base = map_base; |
| mod->map_size = map_size; |
| |
| mod->symbol_table = (IMAGE_SYMBOL *)(map_base + nt->FileHeader.PointerToSymbolTable); |
| mod->symbol_count = nt->FileHeader.NumberOfSymbols; |
| NOTIFY(1, "%s: mapped @" PFX " w/ %d symbols\n", __FUNCTION__, map_base, |
| mod->symbol_count); |
| /* String table immediately follows symbol table */ |
| mod->string_table = ((const char *)mod->symbol_table) + |
| (nt->FileHeader.NumberOfSymbols * sizeof(IMAGE_SYMBOL)); |
| |
| if (mod->symbol_count > 0) |
| mod->debug_kind |= DRSYM_SYMBOLS | DRSYM_PECOFF_SYMTAB; |
| |
| mod->is_64 = (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC); |
| mod->preferred_base = |
| (byte *)(ptr_uint_t)(mod->is_64 |
| ? ((IMAGE_OPTIONAL_HEADER64 *)(&nt->OptionalHeader)) |
| ->ImageBase |
| : nt->OptionalHeader.ImageBase); |
| |
| /* We sort the symbols only once we know the time spent is worth it in init_post */ |
| |
| mod->section_count = nt->FileHeader.NumberOfSections; |
| mod->section_base = |
| (size_t *)dr_global_alloc(mod->section_count * sizeof(*mod->section_base)); |
| sec = IMAGE_FIRST_SECTION(nt); |
| for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) { |
| size_t name_maxsz; |
| const char *secname = drsym_pecoff_get_section_name(mod, sec, &name_maxsz); |
| NOTIFY(2, "%s: %.*s\n", __FUNCTION__, name_maxsz, secname); |
| if (strncmp(secname, ".debug_line", name_maxsz) == 0) { |
| mod->debug_kind |= DRSYM_LINE_NUMS | DRSYM_DWARF_LINE; |
| } |
| if (strncmp(secname, ".gnu_debuglink", name_maxsz) == 0) { |
| mod->debuglink = sec->PointerToRawData + mod->map_base; |
| } |
| /* i#1395: heuristic to identify MinGW stripped libraries */ |
| if (strncmp(secname, ".eh_frame", name_maxsz) == 0 || |
| strncmp(secname, ".CRT", name_maxsz) == 0) { |
| is_mingw = true; |
| } |
| mod->section_base[i] = sec->VirtualAddress; |
| } |
| /* i#1395: since dbghelp does not handle MinGW exports properly (strips leading |
| * underscore and does not demangle) we handle them ourselves. |
| * |
| * XXX: it might be better to check for presence of a PDB first, just in case |
| * our heuristics match non-MinGW -- but that's a little awkward to arrange |
| * with the current code setup. |
| */ |
| if (is_mingw && |
| TEST(IMAGE_FILE_LOCAL_SYMS_STRIPPED, nt->FileHeader.Characteristics) && |
| mod->symbol_count == 0) { |
| mod->exports_only = true; |
| NOTIFY(1, "%s: no pecoff symbols and likely no pdb, so using exports\n", |
| __FUNCTION__); |
| } |
| return (void *)mod; |
| } |
| |
| bool |
| drsym_obj_remap_as_image(void *mod_in) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| return mod->exports_only; |
| } |
| |
| bool |
| drsym_obj_mod_init_post(void *mod_in, byte *map_base, void *dwarf_info) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| /* Now that we know we're using this for sure, do any heavyweight init */ |
| |
| /* We bail and go to dbghelp if there are no symbols in the pecoff |
| * symbol table. |
| * XXX i#672: there may still be dwarf2 or stabs sections even if the |
| * symtable is stripped and we could do symbol lookup via dwarf2 |
| */ |
| if (mod->symbol_count == 0 && !mod->exports_only) { |
| NOTIFY(1, "%s: no pecoff symbols\n", __FUNCTION__); |
| return false; |
| } |
| |
| mod->map_base = map_base; |
| if (mod->exports_only) |
| return drsym_pecoff_sort_exports(mod); |
| else |
| drsym_pecoff_sort_symbols(mod); |
| return true; |
| } |
| |
| bool |
| drsym_obj_dwarf_init(void *mod_in, dwarf_lib_handle_t *dbg) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| Dwarf_Error de; /* expensive to init (DrM#1770) */ |
| if (mod == NULL) |
| return false; |
| if (dwarf_pecoff_init(mod->map_base, DW_DLC_READ, NULL, NULL, dbg, &de) != |
| DW_DLV_OK) { |
| NOTIFY_DWARF(de); |
| return false; |
| } |
| return true; |
| } |
| |
| void |
| drsym_obj_mod_exit(void *mod_in) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| if (mod->section_base != NULL) { |
| dr_global_free(mod->section_base, |
| mod->section_count * sizeof(*mod->section_base)); |
| } |
| if (mod->sorted_syms != NULL) |
| dr_global_free(mod->sorted_syms, mod->symbol_count * sizeof(*mod->sorted_syms)); |
| if (mod->sorted_exports != NULL) { |
| dr_global_free(mod->sorted_exports, |
| mod->sorted_count * sizeof(*mod->sorted_exports)); |
| } |
| dr_global_free(mod, sizeof(*mod)); |
| } |
| |
| /****************************************************************************** |
| * Lookup routines |
| */ |
| |
| drsym_debug_kind_t |
| drsym_obj_info_avail(void *mod_in) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| return mod->debug_kind; |
| } |
| |
| byte * |
| drsym_obj_load_base(void *mod_in) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| return mod->preferred_base; |
| } |
| |
| /* Return the path contained in the .gnu_debuglink section or NULL if we cannot |
| * find it. |
| */ |
| const char * |
| drsym_obj_debuglink_section(void *mod_in, const char *modpath) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| return (const char *)mod->debuglink; |
| } |
| |
| /* caller holds lock */ |
| static const char * |
| drsym_pecoff_symbol_name(pecoff_data_t *mod, IMAGE_SYMBOL *sym) |
| { |
| static char buf[sizeof(sym->N.ShortName) + 1]; |
| const char *name; |
| size_t name_sz; |
| if (sym->N.Name.Short == 0) { |
| /* longer than 8 chars, so index into string table */ |
| name = mod->string_table + sym->N.Name.Long; |
| name_sz = strlen(name); |
| } else { |
| const char *c; |
| name = (const char *)sym->N.ShortName; |
| /* not null-terminated if 8 chars. caller holds lock so we can |
| * use a static buffer to add a NULL, which caller requires. |
| */ |
| for (c = name; c < name + sizeof(sym->N.ShortName); c++) { |
| if (*c == '\0') |
| break; |
| } |
| if (c == name + sizeof(sym->N.ShortName)) { |
| memcpy(buf, name, sizeof(sym->N.ShortName)); |
| NULL_TERMINATE_BUFFER(buf); |
| name = buf; |
| } |
| } |
| #ifndef X64 |
| /* XXX: all 32-bit pecoff symtables I've seen have leading underscores, |
| * which we drop here. Is this always the case? It's true for Cygwin |
| * gcc 3.4.4 and MinGW gcc 4.6.1. It's NOT true for MinGWx64 gcc 4.7.0 |
| * but there's no 4.7 32-bit so I'm not sure whether the next MinGW |
| * release is going to break us. We at least don't remove from "_Z" |
| * so we'll work w/ mangled names (though there could be a C name that |
| * starts w/ Z that was added by the linker and should start w/ _?) |
| */ |
| if (name[0] == '_' && name[1] != 'Z') |
| name++; |
| #endif |
| return name; |
| } |
| |
| static int |
| compare_symbols(const void *a_in, const void *b_in) |
| { |
| const IMAGE_SYMBOL *a = *(const IMAGE_SYMBOL **)a_in; |
| const IMAGE_SYMBOL *b = *(const IMAGE_SYMBOL **)b_in; |
| /* sections must be ascending order, according to pecoff_v8.doc. |
| * if <= 0 we want those first anyway |
| */ |
| if (a->SectionNumber > b->SectionNumber) |
| return 1; |
| if (a->SectionNumber < b->SectionNumber) |
| return -1; |
| if (a->Value > b->Value) |
| return 1; |
| if (a->Value < b->Value) |
| return -1; |
| /* sort the section name entries that sometimes have same Value as func */ |
| if (a->Type > b->Type) |
| return 1; |
| if (a->Type < b->Type) |
| return -1; |
| return 0; |
| } |
| |
| /* Creates a sorted array of IMAGE_SYMBOL* entries that we can use for address lookup |
| * and for simpler iteration with no gaps from aux entries |
| */ |
| static void |
| drsym_pecoff_sort_symbols(pecoff_data_t *mod) |
| { |
| IMAGE_SYMBOL *sym = mod->symbol_table; |
| uint i; |
| uint aux_skip = 0; |
| /* symbol count includes aux entries so it's an over-count but it's not worth |
| * doing a separate pass to count, or re-allocating |
| */ |
| mod->sorted_syms = |
| (IMAGE_SYMBOL **)dr_global_alloc(mod->symbol_count * sizeof(*mod->sorted_syms)); |
| mod->sorted_count = 0; |
| for (i = 0; i < mod->symbol_count; i++, sym++) { |
| if (aux_skip > 0) { |
| aux_skip--; |
| continue; |
| } |
| /* just skip if invalid entry */ |
| if (sym->SectionNumber != IMAGE_SYM_UNDEFINED) |
| mod->sorted_syms[mod->sorted_count++] = sym; |
| /* aux entries just have more info on same symbol so skip */ |
| aux_skip = sym->NumberOfAuxSymbols; |
| } |
| /* XXX: for now using ntdll.dll/libc qsort. We could put qsort sources |
| * into DR if we want more lib independence or are worried about the lib |
| * impl calling some non-re-entrant routine: though unlikely as this is |
| * an in-place sort and the BSD and glibc impls are self-contained. |
| */ |
| qsort(mod->sorted_syms, mod->sorted_count, sizeof(IMAGE_SYMBOL *), compare_symbols); |
| |
| if (verbose >= 3) { |
| NOTIFY(3, "%s:\n", __FUNCTION__); |
| for (i = 0; i < mod->sorted_count; i++) { |
| sym = mod->sorted_syms[i]; |
| NOTIFY(3, " #%d: %-20s Value=0x%x, Sec=%d, Type=%d, Storage=%d, #aux=%d\n", |
| i, drsym_pecoff_symbol_name(mod, sym), sym->Value, sym->SectionNumber, |
| sym->Type, sym->StorageClass, sym->NumberOfAuxSymbols); |
| } |
| } |
| } |
| |
| uint |
| drsym_obj_num_symbols(void *mod_in) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| if (mod == NULL) |
| return 0; |
| return mod->sorted_count; |
| } |
| |
| /* caller holds lock */ |
| const char * |
| drsym_obj_symbol_name(void *mod_in, uint idx) |
| { |
| IMAGE_SYMBOL *sym; |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| if (mod == NULL || idx >= mod->sorted_count) |
| return NULL; |
| if (mod->exports_only) |
| return mod->sorted_exports[idx].name; |
| /* index is into sorted_syms to avoid aux entries */ |
| sym = mod->sorted_syms[idx]; |
| return drsym_pecoff_symbol_name(mod, sym); |
| } |
| |
| static drsym_error_t |
| drsym_pecoff_symbol_offs(pecoff_data_t *mod, IMAGE_SYMBOL *sym, size_t *offs DR_PARAM_OUT) |
| { |
| /* SectionNumber is 1-based */ |
| if (offs == NULL) |
| return DRSYM_ERROR_INVALID_PARAMETER; |
| if (sym->SectionNumber > 0 && (uint)sym->SectionNumber <= mod->section_count) |
| *offs = sym->Value + mod->section_base[sym->SectionNumber - 1]; |
| else if (sym->SectionNumber == IMAGE_SYM_ABSOLUTE || |
| sym->SectionNumber == IMAGE_SYM_DEBUG) { |
| /* No offset */ |
| *offs = 0; |
| /* XXX: still return success? Someone might want to look it up. |
| * It's not like an import in .dynsym (i#1256). |
| */ |
| } else { |
| NOTIFY(1, "%s: unknown section # %d val 0x%x\n", __FUNCTION__, sym->SectionNumber, |
| sym->Value); |
| *offs = 0; |
| return DRSYM_ERROR_NOT_IMPLEMENTED; |
| } |
| return DRSYM_SUCCESS; |
| } |
| |
| drsym_error_t |
| drsym_obj_symbol_offs(void *mod_in, uint idx, size_t *offs_start DR_PARAM_OUT, |
| size_t *offs_end DR_PARAM_OUT) |
| { |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| drsym_error_t res; |
| if (offs_start == NULL || mod == NULL || idx >= mod->sorted_count) |
| return DRSYM_ERROR_INVALID_PARAMETER; |
| if (mod->exports_only) { |
| *offs_start = mod->sorted_exports[idx].addr - mod->map_base; |
| if (offs_end != NULL) { |
| if (idx + 1 < mod->sorted_count) { |
| *offs_end = mod->sorted_exports[idx + 1].addr - mod->map_base; |
| } else |
| *offs_end = *offs_start + 1; |
| } |
| return DRSYM_SUCCESS; |
| } |
| res = drsym_pecoff_symbol_offs(mod, mod->sorted_syms[idx], offs_start); |
| if (res != DRSYM_SUCCESS) |
| return res; |
| if (offs_end != NULL) { |
| /* XXX: we don't have the end offs so we use the next sym */ |
| #if 0 /* recording code to identify function */ |
| /* From pecoff_v8.doc 5.5.1, these define a function */ |
| /* XXX: how know when to use IMAGE_SYMBOL_EX? */ |
| if ((sym->Type & 0xff) == IMAGE_SYM_TYPE_FUNCTION && |
| (sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL || |
| sym->StorageClass == IMAGE_SYM_CLASS_STATIC)) { |
| /* It's a function. |
| * XXX: doesn't have the aux entries in the pecoff_v8 spec |
| * so we can't find the end bound of the function. |
| */ |
| } |
| #endif |
| if (idx + 1 < mod->sorted_count) { |
| res = drsym_pecoff_symbol_offs(mod, mod->sorted_syms[idx + 1], offs_end); |
| } else |
| *offs_end = *offs_start + 1; |
| } |
| return res; |
| } |
| |
| drsym_error_t |
| drsym_obj_addrsearch_symtab(void *mod_in, size_t modoffs, uint *idx DR_PARAM_OUT) |
| { |
| /* This routine is used for both symbol table (mod->sorted_syms[]) |
| * and exports (mod->sorted_exports) searching, so don't go and |
| * access mod->sorted_syms[] w/o checking for mod->exports_only! |
| */ |
| pecoff_data_t *mod = (pecoff_data_t *)mod_in; |
| uint min = 0; |
| uint max = mod->sorted_count - 1; |
| int min_lower = -1; |
| drsym_error_t res; |
| if (mod == NULL || idx == NULL) |
| return DRSYM_ERROR_INVALID_PARAMETER; |
| if (modoffs >= mod->map_size) |
| return DRSYM_ERROR_SYMBOL_NOT_FOUND; |
| /* XXX: if a function is split into non-contiguous pieces, will it |
| * have multiple entries? |
| */ |
| /* binary search */ |
| NOTIFY(1, "%s: 0x%x\n", __FUNCTION__, modoffs); |
| while (max >= min) { |
| uint i = (min + max) / 2; |
| size_t symoffs; |
| /* we ignore unknown sec here and treat all such as 0 at front of array */ |
| res = drsym_obj_symbol_offs(mod, i, &symoffs, NULL); |
| NOTIFY(2, "\tbinary search %d => 0x%x == %s\n", i, symoffs, |
| drsym_obj_symbol_name(mod_in, i)); |
| if (res != DRSYM_SUCCESS && res != DRSYM_ERROR_SYMBOL_NOT_FOUND) |
| return res; |
| if (modoffs < symoffs) { |
| max = i - 1; |
| } else if (modoffs >= symoffs) { |
| if (max == min || modoffs == symoffs) { |
| /* found closest sym with offs <= target */ |
| min_lower = i; |
| break; |
| } else { |
| min_lower = i; |
| min = i + 1; |
| } |
| } |
| } |
| NOTIFY(2, "\tbinary search => %d\n", min_lower); |
| if (min_lower > -1 && !mod->exports_only) { |
| /* found closest sym with offs <= target */ |
| /* sometimes a section-name entry will have the same offs as a function. |
| * prefer the function. |
| * we sorted by type so we know function is later. |
| */ |
| if (mod->sorted_syms[min_lower]->Type == 0 && |
| (uint)min_lower + 1 < mod->sorted_count && |
| mod->sorted_syms[min_lower]->Value == mod->sorted_syms[min_lower + 1]->Value) |
| min_lower++; |
| *idx = min_lower; |
| return DRSYM_SUCCESS; |
| } |
| return DRSYM_ERROR_SYMBOL_NOT_FOUND; |
| } |
| |
| /****************************************************************************** |
| * Exports-only |
| */ |
| |
| static int |
| compare_exports(const void *a_in, const void *b_in) |
| { |
| const export_info_t *a = (const export_info_t *)a_in; |
| const export_info_t *b = (const export_info_t *)b_in; |
| if (a->addr > b->addr) |
| return 1; |
| if (a->addr < b->addr) |
| return -1; |
| return 0; |
| } |
| |
| static bool |
| drsym_pecoff_sort_exports(pecoff_data_t *mod) |
| { |
| uint i = 0; |
| dr_symbol_export_iterator_t *exp_iter; |
| |
| /* We need to map as an image to read the exports dir (and we can't map |
| * as an image for symtable reading b/c that won't come along). |
| * XXX: unmap the non-image mapped by drsyms_unix to save space. |
| * XXX: for online use, use the actual already-loaded image to save space. |
| */ |
| /* 1st pass to get the count */ |
| exp_iter = dr_symbol_export_iterator_start((module_handle_t)mod->map_base); |
| while (dr_symbol_export_iterator_hasnext(exp_iter)) { |
| dr_symbol_export_t *sym = dr_symbol_export_iterator_next(exp_iter); |
| if (sym->is_code) |
| i++; |
| } |
| dr_symbol_export_iterator_stop(exp_iter); |
| if (i == 0) { |
| /* No exports found, so better to try our luck with dbghelp */ |
| return false; |
| } |
| mod->sorted_count = i; |
| mod->sorted_exports = (export_info_t *)dr_global_alloc(mod->sorted_count * |
| sizeof(*mod->sorted_exports)); |
| /* 2nd pass */ |
| i = 0; |
| dr_symbol_export_iterator_start((module_handle_t)mod->map_base); |
| while (dr_symbol_export_iterator_hasnext(exp_iter)) { |
| dr_symbol_export_t *sym = dr_symbol_export_iterator_next(exp_iter); |
| if (sym->is_code) { |
| mod->sorted_exports[i].name = sym->name; |
| mod->sorted_exports[i].addr = sym->addr; |
| i++; |
| } |
| } |
| dr_symbol_export_iterator_stop(exp_iter); |
| |
| /* XXX: using ndll qsort just like drsym_pecoff_sort_symbols */ |
| qsort(mod->sorted_exports, mod->sorted_count, sizeof(*mod->sorted_exports), |
| compare_exports); |
| |
| if (verbose >= 3) { |
| NOTIFY(3, "%s:\n", __FUNCTION__); |
| for (i = 0; i < mod->sorted_count; i++) { |
| NOTIFY(3, " #%d: %-20s addr=" PFX "\n", i, mod->sorted_exports[i].name, |
| mod->sorted_exports[i].addr); |
| } |
| } |
| return true; |
| } |
| |
| /****************************************************************************** |
| * Linux-specific helpers |
| */ |
| |
| /* Returns true if the two paths have the same inode. Returns false if there |
| * was an error or they are different. |
| */ |
| bool |
| drsym_obj_same_file(const char *path1, const char *path2) |
| { |
| /* XXX: ignoring symlinks and 8.3 */ |
| return (strcmp(path1, path2) == 0); |
| } |
| |
| const char * |
| drsym_obj_debug_path(void) |
| { |
| /* XXX: figure out where cygwin is really installed */ |
| /* XXX: also search mingw debug path */ |
| return "c:\\cygwin\\lib\\debug"; |
| } |
| |
| const char * |
| drsym_obj_build_id(void *mod_in) |
| { |
| /* NYI. Are build id-based dirs used on cygwin? */ |
| return NULL; |
| } |