blob: d57f64237b4739e289cc31f87cec073207f3c111 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2011-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 Unix: code shared between Linux, Mac, and Cygwin */
#include "dr_api.h"
#include "drsyms.h"
#include "drsyms_private.h"
#include "drsyms_obj.h"
#include "hashtable.h"
#include <string.h> /* strlen */
#include <errno.h>
#include <stddef.h> /* offsetof */
#include "demangle.h"
#ifdef DRSYM_HAVE_LIBELFTC
# include "libelftc.h"
#endif
#ifdef WINDOWS
# define IF_WINDOWS(x) x
#else
# define IF_WINDOWS(x)
#endif
/* For debugging */
static bool verbose = false;
typedef struct _dbg_module_t {
file_t fd;
size_t file_size;
size_t map_size;
void *map_base;
void *obj_info;
void *dwarf_info;
drsym_debug_kind_t debug_kind;
/* Sometimes we need to have both original and debuglink loaded.
* mod_with_dwarf points at the one w/ DWARF info in that case,
* while the primary mod has symtab+strtab.
*/
struct _dbg_module_t *mod_with_dwarf;
#define SYMTABLE_HASH_BITS 12
hashtable_t symtable;
} dbg_module_t;
/******************************************************************************
* Forward declarations.
*/
static void
unload_module(dbg_module_t *mod);
static bool
follow_debuglink(const char *modpath, dbg_module_t *mod, const char *debuglink,
char debug_modpath[MAXIMUM_PATH]);
static void
drsym_free_hash_key(void *key);
/******************************************************************************
* Module loading and unloading.
*/
static dbg_module_t *
load_module(const char *modpath)
{
dbg_module_t *mod, *newmod = NULL;
bool ok;
const char *debuglink;
uint64 file_size;
/* static depth count to prevent stack overflow from circular .gnu_debuglink
* sections. We're protected by symbol_lock.
*/
static int load_module_depth;
if (load_module_depth >= 2) {
NOTIFY("drsyms: Refusing to follow .gnu_debuglink more than 2 times.\n");
return NULL;
}
load_module_depth++;
NOTIFY("loading debug info for module %s\n", modpath);
/* Alloc and zero the struct so it can be unloaded safely in case of error. */
mod = dr_global_alloc(sizeof(*mod));
memset(mod, 0, sizeof(*mod));
mod->fd = dr_open_file(modpath, DR_FILE_READ);
if (mod->fd == INVALID_FILE) {
NOTIFY("%s: unable to open %s\n", __FUNCTION__, modpath);
goto error;
}
ok = dr_file_size(mod->fd, &file_size);
if (!ok) {
NOTIFY("%s: unable to get file size %s\n", __FUNCTION__, modpath);
goto error;
}
mod->file_size = (size_t)file_size; /* XXX: ignoring truncation */
mod->map_size = mod->file_size;
mod->map_base =
dr_map_file(mod->fd, &mod->map_size, 0, NULL, DR_MEMPROT_READ, DR_MAP_PRIVATE);
/* map_size can be larger than file_size */
if (mod->map_base == NULL || mod->map_size < mod->file_size) {
NOTIFY("%s: unable to map %s\n", __FUNCTION__, modpath);
goto error;
}
/* We have to duplicate most of the strings we add ourselves, so we do not ask for
* additional duplication and use a key freeing function.
*/
hashtable_init_ex(&mod->symtable, SYMTABLE_HASH_BITS, HASH_STRING, false /*strdup*/,
false /*!synch*/, NULL, NULL, NULL);
hashtable_config_t config;
config.size = sizeof(config);
config.resizable = true;
config.resize_threshold = 70;
config.free_key_func = drsym_free_hash_key;
hashtable_configure(&mod->symtable, &config);
/* We need to partially initialize in order to get the debug info */
mod->obj_info = drsym_obj_mod_init_pre(mod->map_base, mod->file_size);
if (mod->obj_info == NULL)
goto error;
/* Figure out what kind of debug info is available for this module. */
mod->debug_kind = drsym_obj_info_avail(mod->obj_info);
/* If there is a .gnu_debuglink section, then all the debug info we care
* about is in the file it points to (except maybe .symtab: see below).
*/
debuglink = drsym_obj_debuglink_section(mod->obj_info, modpath);
if (debuglink != NULL) {
char debug_modpath[MAXIMUM_PATH];
NOTIFY("%s: looking for debuglink %s\n", __FUNCTION__, debuglink);
if (follow_debuglink(modpath, mod, debuglink, debug_modpath)) {
NOTIFY("%s: loading debuglink %s\n", __FUNCTION__, debug_modpath);
newmod = load_module(debug_modpath);
if (newmod != NULL) {
/* We expect that DWARF sections will all be in
* newmod, but .symtab may be empty in newmod and we
* may need to keep mod for that (i#642).
*/
NOTIFY("%s: followed debuglink to %s\n", __FUNCTION__, debug_modpath);
if (!TESTANY(DRSYM_ELF_SYMTAB | DRSYM_PECOFF_SYMTAB,
newmod->debug_kind) &&
TESTANY(DRSYM_ELF_SYMTAB | DRSYM_PECOFF_SYMTAB, mod->debug_kind)) {
/* We need both */
mod->mod_with_dwarf = newmod;
mod->debug_kind |= newmod->debug_kind;
} else {
/* Debuglink is all we need */
unload_module(mod);
mod = newmod;
}
} /* else stick with mod */
} /* else stick with mod */
}
if (newmod == NULL) {
dwarf_lib_handle_t dbg;
/* If there is no .gnu_debuglink, initialize parsing. */
#ifdef WINDOWS
/* i#1395: support switching to expots-only for MinGW, for which we
* need an image mapping. We don't need the file mapping anymore.
*/
if (drsym_obj_remap_as_image(mod->obj_info)) {
dr_unmap_file(mod->map_base, mod->map_size);
mod->map_size = 0;
mod->map_base = dr_map_file(mod->fd, &mod->map_size, 0, NULL, DR_MEMPROT_READ,
DR_MAP_PRIVATE | DR_MAP_IMAGE);
if (mod->map_base == NULL || mod->map_size < mod->file_size) {
NOTIFY("%s: unable to map %s\n", __FUNCTION__, modpath);
goto error;
}
}
#endif
if (TEST(DRSYM_DWARF_LINE, mod->debug_kind) &&
drsym_obj_dwarf_init(mod->obj_info, &dbg)) {
mod->dwarf_info = drsym_dwarf_init(dbg);
} else {
NOTIFY("%s: failed to init DWARF for %s\n", __FUNCTION__, modpath);
mod->dwarf_info = NULL;
}
if (!drsym_obj_mod_init_post(mod->obj_info, mod->map_base, mod->dwarf_info))
goto error;
if (mod->dwarf_info != NULL) {
/* i#1433: obj_info->load_base is initialized in drsym_obj_mod_init_post */
drsym_dwarf_set_load_base(mod->dwarf_info,
drsym_obj_load_base(mod->obj_info));
}
}
NOTIFY("%s: loaded %s\n", __FUNCTION__, modpath);
load_module_depth--;
return mod;
error:
unload_module(mod);
load_module_depth--;
return NULL;
}
/* Construct a hybrid dbg_module_t that loads debug information from the
* debuglink path.
*
* Gdb's search algorithm for finding debug info files is documented here:
* http://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
*
* FIXME: We should allow the user to register additional search directories.
* XXX: We may need to support the --build-id debug file mechanism documented at
* the above URL, but for now, .gnu_debuglink seems to work for most Linux
* systems.
*/
static bool
follow_debuglink(const char *modpath, dbg_module_t *mod, const char *debuglink,
char debug_modpath[MAXIMUM_PATH])
{
char mod_dir[MAXIMUM_PATH];
char *s, *last_slash = NULL;
/* For non-GNU we might be handed an absolute path */
if (debuglink[0] == '/' && dr_file_exists(debuglink)) {
strncpy(debug_modpath, debuglink, MAXIMUM_PATH);
debug_modpath[MAXIMUM_PATH - 1] = '\0';
return true;
}
/* Get the module's directory. */
strncpy(mod_dir, modpath, MAXIMUM_PATH);
NULL_TERMINATE_BUFFER(mod_dir);
/* XXX: we want DR's double_strrchr */
for (s = mod_dir; *s != '\0'; s++) {
if (*s == '/' IF_WINDOWS(|| *s == '\\'))
last_slash = s;
}
if (last_slash != NULL)
*last_slash = '\0';
/* 1. Check /usr/lib/debug/.build-id/xx/$debuglink */
const char *build_id = drsym_obj_build_id(mod->obj_info);
NOTIFY("%s: build id is %s\n", __FUNCTION__, build_id == NULL ? "<null>" : build_id);
if (build_id != NULL && build_id[0] != '\0') {
dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/.build-id/%c%c/%s",
drsym_obj_debug_path(), build_id[0], build_id[1], debuglink);
debug_modpath[MAXIMUM_PATH - 1] = '\0';
NOTIFY("%s: looking for %s\n", __FUNCTION__, debug_modpath);
if (dr_file_exists(debug_modpath))
return true;
}
/* 2. Check $mod_dir/$debuglink */
dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/%s", mod_dir, debuglink);
debug_modpath[MAXIMUM_PATH - 1] = '\0';
NOTIFY("%s: looking for %s\n", __FUNCTION__, debug_modpath);
/* If debuglink is the basename of modpath, this can point to the same file.
* Infinite recursion is prevented with a depth check, but we would fail to
* test the other paths, so we check here if these paths resolve to the same
* file, ignoring hard and soft links and other path quirks.
*/
if (dr_file_exists(debug_modpath) && !drsym_obj_same_file(modpath, debug_modpath))
return true;
/* 3. Check $mod_dir/.debug/$debuglink */
dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/.debug/%s", mod_dir, debuglink);
debug_modpath[MAXIMUM_PATH - 1] = '\0';
NOTIFY("%s: looking for %s\n", __FUNCTION__, debug_modpath);
if (dr_file_exists(debug_modpath))
return true;
/* 4. Check /usr/lib/debug/$mod_dir/$debuglink */
dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/%s/%s", drsym_obj_debug_path(), mod_dir,
debuglink);
debug_modpath[MAXIMUM_PATH - 1] = '\0';
NOTIFY("%s: looking for %s\n", __FUNCTION__, debug_modpath);
if (dr_file_exists(debug_modpath))
return true;
/* We couldn't find the debug file, so we make do with the original module
* instead. We'll still find exports in .dynsym.
*/
return false;
}
/* Free all resources associated with the debug module and the object itself.
*/
static void
unload_module(dbg_module_t *mod)
{
if (mod->dwarf_info != NULL)
drsym_dwarf_exit(mod->dwarf_info);
if (mod->obj_info != NULL)
drsym_obj_mod_exit(mod->obj_info);
if (mod->symtable.table != NULL)
hashtable_delete(&mod->symtable);
if (mod->map_base != NULL)
dr_unmap_file(mod->map_base, mod->map_size);
if (mod->fd != INVALID_FILE)
dr_close_file(mod->fd);
if (mod->mod_with_dwarf != NULL)
unload_module(mod->mod_with_dwarf);
dr_global_free(mod, sizeof(*mod));
}
/******************************************************************************
* Symbol table parsing
*/
static drsym_error_t
symsearch_symtab(dbg_module_t *mod, drsym_enumerate_cb callback,
drsym_enumerate_ex_cb callback_ex, size_t info_size, void *data,
uint flags)
{
int num_syms;
int i;
bool keep_searching = true;
size_t name_buf_size = 1024; /* C++ symbols can be quite long. */
drsym_error_t res = DRSYM_SUCCESS;
drsym_info_t *out;
num_syms = drsym_obj_num_symbols(mod->obj_info);
if (num_syms == 0)
return DRSYM_ERROR;
out = (drsym_info_t *)dr_global_alloc(info_size);
out->name = (char *)dr_global_alloc(name_buf_size);
out->struct_size = info_size;
out->debug_kind = mod->debug_kind;
out->type_id = 0; /* NYI */
/* We don't have line info (see below) */
out->file = NULL;
out->file_size = 0;
out->file_available_size = 0;
/* Fields beyond name require compatibility checks */
if (out->struct_size > offsetof(drsym_info_t, flags)) {
/* Remove unsupported flags */
out->flags = flags & ~(UNSUPPORTED_NONPDB_FLAGS);
}
for (i = 0; keep_searching && i < num_syms; i++) {
const char *mangled = drsym_obj_symbol_name(mod->obj_info, i);
const char *unmangled = mangled; /* Points at mangled or symbol_buf. */
size_t modoffs = 0;
if (mangled == NULL) {
res = DRSYM_ERROR;
break;
}
if (callback_ex != NULL) {
res =
drsym_obj_symbol_offs(mod->obj_info, i, &out->start_offs, &out->end_offs);
} else
res = drsym_obj_symbol_offs(mod->obj_info, i, &modoffs, NULL);
/* Skip imports and missing symbols. */
if (res == DRSYM_ERROR_SYMBOL_NOT_FOUND ||
(callback_ex == NULL && modoffs == 0) || mangled[0] == '\0') {
res = DRSYM_SUCCESS; /* if go off end of loop */
continue;
}
if (res != DRSYM_SUCCESS)
break;
if (TESTANY(DRSYM_DEMANGLE | DRSYM_DEMANGLE_FULL, flags)) {
size_t len;
/* Resize until it's big enough. */
while ((len = drsym_demangle_symbol(out->name, name_buf_size, mangled,
flags)) > name_buf_size) {
dr_global_free(out->name, name_buf_size);
name_buf_size = len;
out->name = (char *)dr_global_alloc(name_buf_size);
}
if (len != 0) {
/* Success. */
unmangled = out->name;
}
} else if (callback_ex != NULL) {
strncpy(out->name, unmangled, name_buf_size);
out->name[name_buf_size - 1] = '\0';
}
if (callback_ex != NULL) {
out->name_size = name_buf_size;
out->name_available_size = strlen(out->name);
/* We can't get line information w/o doing a separate addr lookup
* which may not be the same symbol as this one (not 1-to-1)
*/
keep_searching = callback_ex(out, DRSYM_ERROR_LINE_NOT_AVAILABLE, data);
} else
keep_searching = callback(unmangled, modoffs, data);
}
dr_global_free(out->name, name_buf_size);
dr_global_free(out, info_size);
return res;
}
static drsym_error_t
addrsearch_symtab(dbg_module_t *mod, size_t modoffs, drsym_info_t *info DR_PARAM_INOUT,
uint flags)
{
const char *symbol;
size_t name_len = 0;
uint idx;
drsym_error_t res = drsym_obj_addrsearch_symtab(mod->obj_info, modoffs, &idx);
if (res != DRSYM_SUCCESS)
return res;
symbol = drsym_obj_symbol_name(mod->obj_info, idx);
if (symbol == NULL)
return DRSYM_ERROR;
if (TEST(DRSYM_DEMANGLE, flags) && info->name != NULL) {
name_len = drsym_demangle_symbol(info->name, info->name_size, symbol, flags);
}
if (name_len == 0) {
/* Demangling either failed or was not requested. */
name_len = strlen(symbol) + 1;
if (info->name != NULL) {
strncpy(info->name, symbol, info->name_size);
info->name[info->name_size - 1] = '\0';
}
}
info->name_available_size = name_len;
return drsym_obj_symbol_offs(mod->obj_info, idx, &info->start_offs, &info->end_offs);
}
/******************************************************************************
* Hashtable building for symbol lookup.
*
* We use __wrap_malloc because our demangled strings have larger capacities
* than strlen() will find (see drsym_demangle_helper() where we start with a
* 1024-byte buffer).
* XXX: Should DR export a malloc-matching heap API that does not start with
* underscores for cleaner usage? Most non-static-DR usage though can just
* use malloc(). Here we want to support static DR.
*
* We do not want to pay the cost of hashtable_t doing a further
* strdup on our just-allocated strings so we free keys ourselves.
*/
static void
drsym_free_hash_key(void *key)
{
__wrap_free(key);
}
static char *
drsym_dup_string_until_char(const char *sym, char stop)
{
/* We skip the first char to avoid empty strings. */
const char *found = strchr(sym + 1, stop);
if (found != NULL) {
size_t no_found_sz = found - sym;
char *no_found = (char *)__wrap_malloc(no_found_sz + 1);
dr_snprintf(no_found, no_found_sz, "%s", sym);
no_found[no_found_sz] = '\0';
return no_found;
}
return NULL;
}
static char *
drsym_demangle_helper(const char *sym, uint flags)
{
/* We look for "_Z" to avoid the copy of the same symbol for non-mangled cases. */
if (sym[0] != '_' || sym[1] != 'Z')
return NULL;
size_t len;
size_t buf_size = 1024;
char *buf = (char *)__wrap_malloc(buf_size);
/* Resize until it's big enough. */
while ((len = drsym_demangle_symbol(buf, buf_size, sym, flags)) > buf_size) {
__wrap_free(buf);
buf_size = len;
buf = (char *)__wrap_malloc(buf_size);
}
if (len <= 0) {
return NULL;
}
return buf;
}
static bool
drsym_add_hash_entry(dbg_module_t *mod, const char *copy, size_t modoffs)
{
if (hashtable_add(&mod->symtable, (void *)copy, (void *)modoffs)) {
NOTIFY("%s: added %s\n", __FUNCTION__, copy);
return true;
}
drsym_free_hash_key((void *)copy);
return false;
}
/* Symbol enumeration callback for doing a single lookup.
*/
static bool
drsym_fill_symtable_cb(const char *sym, size_t modoffs, void *data DR_PARAM_OUT)
{
dbg_module_t *mod = (dbg_module_t *)data;
size_t len = strlen(sym);
char *copy = __wrap_malloc(len + 1);
dr_snprintf(copy, len, "%s", sym);
copy[len] = '\0';
if (!drsym_add_hash_entry(mod, copy, modoffs))
return true;
/* We match the name part of versioned symbols: so "foo" will match
* "foo@@GLIBC_2.1". If there are multiple, a user who wants one in
* particular needs to include the version name in the search target.
*/
char *toadd = drsym_dup_string_until_char(sym, '@');
if (toadd != NULL)
drsym_add_hash_entry(mod, toadd, modoffs);
/* Add the demanglings. */
toadd = drsym_demangle_helper(sym, DRSYM_DEMANGLE);
if (toadd == NULL)
return true;
if (!drsym_add_hash_entry(mod, toadd, modoffs))
return true;
/* Add a version without parameters to allow the user to ignore overloads.
* XXX: This is not a great heuristic as there are cases with parentheses for
* namespaces or other parts of the type, such as:
* Foo::(anonymous namespace)::bar()
* std::function<int(int)>::foo().
* If nobody is relying on this maybe we should just remove it?
*/
char *noparen = drsym_dup_string_until_char(toadd, '(');
if (noparen != NULL)
drsym_add_hash_entry(mod, noparen, modoffs);
#ifdef DRSYM_HAVE_LIBELFTC
toadd = drsym_demangle_helper(sym, DRSYM_DEMANGLE_FULL);
if (toadd != NULL)
drsym_add_hash_entry(mod, toadd, modoffs);
#endif
return true;
}
/******************************************************************************
* Exports
*/
void
drsym_unix_init(void)
{
drsym_obj_init();
}
void
drsym_unix_exit(void)
{
/* nothing */
}
void *
drsym_unix_load(const char *modpath)
{
return load_module(modpath);
}
void
drsym_unix_unload(void *mod_in)
{
dbg_module_t *mod = (dbg_module_t *)mod_in;
unload_module(mod);
}
drsym_error_t
drsym_unix_enumerate_symbols(void *mod_in, drsym_enumerate_cb callback,
drsym_enumerate_ex_cb callback_ex, size_t info_size,
void *data, uint flags)
{
dbg_module_t *mod = (dbg_module_t *)mod_in;
if (info_size != sizeof(drsym_info_t))
return DRSYM_ERROR_INVALID_SIZE;
return symsearch_symtab(mod, callback, callback_ex, info_size, data, flags);
}
drsym_error_t
drsym_unix_lookup_symbol(void *mod_in, const char *symbol, size_t *modoffs DR_PARAM_OUT,
uint flags)
{
dbg_module_t *mod = (dbg_module_t *)mod_in;
const char *sym_no_mod;
if (symbol == NULL) {
sym_no_mod = NULL;
} else {
/* Ignore the module portion of the match string. We search the module
* specified by modpath.
*
* FIXME #574: Change the interface for both Linux and Windows
* implementations to not include the module name.
*/
sym_no_mod = strchr(symbol, '!');
if (sym_no_mod != NULL) {
sym_no_mod++;
} else {
sym_no_mod = symbol;
}
}
*modoffs = 0;
if (!TEST(DRSYM_SYMBOLS, mod->debug_kind)) {
/* XXX i#883: we have no symbols and we're just looking at exports so we
* should do a fast hashtable lookup instead of a linear walk.
* DR already has the code for this, accessible via dr_get_proc_address(),
* except that interface will only work for online (i.e., non-standalone)
* use.
*/
}
if (*modoffs == 0) {
if (mod->symtable.entries == 0) {
/* Initialize the hashtable. */
symsearch_symtab(mod, drsym_fill_symtable_cb, NULL, sizeof(drsym_info_t), mod,
DRSYM_LEAVE_MANGLED);
}
*modoffs = (size_t)hashtable_lookup(&mod->symtable, (void *)sym_no_mod);
}
if (*modoffs == 0)
return DRSYM_ERROR_SYMBOL_NOT_FOUND;
return DRSYM_SUCCESS;
}
drsym_error_t
drsym_unix_lookup_address(void *mod_in, size_t modoffs, drsym_info_t *out DR_PARAM_INOUT,
uint flags)
{
dbg_module_t *mod = (dbg_module_t *)mod_in;
drsym_error_t r = addrsearch_symtab(mod, modoffs, out, flags);
/* If we did find an address for the symbol, go look for its line number
* information.
*/
if (r == DRSYM_SUCCESS) {
/* Search through .debug_line for line and file information. We always
* report success even if we only get partial line information we at
* least have the name of the function.
*/
dbg_module_t *mod4line = mod;
if (mod->mod_with_dwarf != NULL)
mod4line = mod->mod_with_dwarf;
if (mod4line->dwarf_info == NULL ||
!drsym_dwarf_search_addr2line(
mod4line->dwarf_info,
(Dwarf_Addr)(ptr_uint_t)(drsym_obj_load_base(mod->obj_info) + modoffs),
out)) {
r = DRSYM_ERROR_LINE_NOT_AVAILABLE;
}
}
out->debug_kind = mod->debug_kind;
/* Fields beyond name require compatibility checks */
if (out->struct_size > offsetof(drsym_info_t, flags)) {
/* Remove unsupported flags */
out->flags = flags & ~(UNSUPPORTED_NONPDB_FLAGS);
}
return r;
}
drsym_error_t
drsym_unix_enumerate_lines(void *mod_in, drsym_enumerate_lines_cb callback, void *data)
{
dbg_module_t *mod = (dbg_module_t *)mod_in;
dbg_module_t *mod4line = mod;
if (mod->mod_with_dwarf != NULL)
mod4line = mod->mod_with_dwarf;
if (mod4line->dwarf_info != NULL)
return drsym_dwarf_enumerate_lines(mod4line->dwarf_info, callback, data);
else
return DRSYM_ERROR_LINE_NOT_AVAILABLE;
}
drsym_error_t
drsym_unix_get_type(void *mod_in, size_t modoffs, uint levels_to_expand, char *buf,
size_t buf_sz, drsym_type_t **type DR_PARAM_OUT)
{
return DRSYM_ERROR_NOT_IMPLEMENTED;
}
drsym_error_t
drsym_unix_get_func_type(void *mod_in, size_t modoffs, char *buf, size_t buf_sz,
drsym_func_type_t **func_type DR_PARAM_OUT)
{
return DRSYM_ERROR_NOT_IMPLEMENTED;
}
drsym_error_t
drsym_unix_expand_type(const char *modpath, uint type_id, uint levels_to_expand,
char *buf, size_t buf_sz,
drsym_type_t **expanded_type DR_PARAM_OUT)
{
return DRSYM_ERROR_NOT_IMPLEMENTED;
}
size_t
drsym_unix_demangle_symbol(char *dst DR_PARAM_OUT, size_t dst_sz, const char *mangled,
uint flags)
{
if (!TEST(DRSYM_DEMANGLE_FULL, flags)) {
/* The demangle.cc implementation is fast and replaces template args
* with <> and omits parameters. Use it if the user doesn't
* want either of those. Its return value always follows our
* conventions.
*/
int len = Demangle(mangled, dst, (int)dst_sz, DEMANGLE_DEFAULT);
if (len > 0) {
return len; /* Success or truncation. */
}
} else {
#ifdef DRSYM_HAVE_LIBELFTC
/* If the user wants template arguments or overloads, we use the
* libelftc demangler which is slower, but can properly demangle
* template arguments.
*/
/* libelftc code use fp ops so we have to save fp state (i#756) */
byte fp_raw[DR_FPSTATE_BUF_SIZE + DR_FPSTATE_ALIGN];
byte *fp_align = (byte *)ALIGN_FORWARD(fp_raw, DR_FPSTATE_ALIGN);
proc_save_fpstate(fp_align);
int status = elftc_demangle(mangled, dst, dst_sz, ELFTC_DEM_GNU3);
proc_restore_fpstate(fp_align);
# ifdef WINDOWS
/* Our libelftc returns the # of chars needed, and copies the truncated
* unmangled name, but it returns -1 on error.
*/
if (status <= 0)
return 0;
if (status > 0)
return status;
# else
/* XXX: let's make the same change to the libelftc we're using here */
if (status == 0) {
return strlen(dst) + 1;
} else if (errno == ENAMETOOLONG) {
/* FIXME: libelftc actually doesn't copy the output into dst and
* truncate it, so we do the next best thing and put the truncated
* mangled name in there.
*/
strncpy(dst, mangled, dst_sz);
dst[dst_sz - 1] = '\0';
/* FIXME: This return value is made up and may not be large enough.
* It will work eventually if the caller reallocates their buffer
* and retries in a loop, or if they just want to detect truncation.
*/
return dst_sz * 2;
}
# endif
#endif /* DRSYM_HAVE_LIBELFTC */
}
/* If the demangling failed, copy the mangled symbol into the output. */
strncpy(dst, mangled, dst_sz);
dst[dst_sz - 1] = '\0';
return 0;
}
drsym_error_t
drsym_unix_get_module_debug_kind(void *mod_in, drsym_debug_kind_t *kind DR_PARAM_OUT)
{
dbg_module_t *mod = (dbg_module_t *)mod_in;
if (mod != NULL) {
*kind = mod->debug_kind;
return DRSYM_SUCCESS;
} else {
return DRSYM_ERROR_LOAD_FAILED;
}
}