dwflpp.cxx - systemtap
Data types defined
Functions defined
Macros defined
Source code
#include "dwflpp.h"
#include "config.h"
#include <cxxabi.h>
#include "staptree.h"
#include "elaborate.h"
#include "tapsets.h"
#include "task_finder.h"
#include "translate.h"
#include "session.h"
#include "util.h"
#include "buildrun.h"
#include "dwarf_wrappers.h"
#include "auto_free.h"
#include "hash.h"
#include "rpm_finder.h"
#include "setupdwfl.h"
#include <cstdlib>
#include <algorithm>
#include <deque>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <cstdarg>
#include <cassert>
#include <iomanip>
#include <cerrno>
extern "C" {
#include <fcntl.h>
#include <elfutils/libdwfl.h>
#include <elfutils/libdw.h>
#include <dwarf.h>
#include <elf.h>
#include <obstack.h>
#include <regex.h>
#include <glob.h>
#include <fnmatch.h>
#include <stdio.h>
#include <sys/types.h>
#include "loc2c.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
}
#ifndef STB_GNU_UNIQUE
#define STB_GNU_UNIQUE 10
#endif
using namespace std;
using namespace __gnu_cxx;
static string TOK_KERNEL("kernel");
dwflpp::dwflpp(systemtap_session & session, const string& name, bool kernel_p):
sess(session), module(NULL), module_bias(0), mod_info(NULL),
module_start(0), module_end(0), cu(NULL), dwfl(NULL),
module_dwarf(NULL), function(NULL), blacklist_func(), blacklist_func_ret(),
blacklist_file(), blacklist_enabled(false)
{
if (kernel_p)
setup_kernel(name, session);
else
{
vector<string> modules;
modules.push_back(name);
setup_user(modules);
}
}
dwflpp::dwflpp(systemtap_session & session, const vector<string>& names,
bool kernel_p):
sess(session), module(NULL), module_bias(0), mod_info(NULL),
module_start(0), module_end(0), cu(NULL), dwfl(NULL),
module_dwarf(NULL), function(NULL), blacklist_enabled(false)
{
if (kernel_p)
setup_kernel(names);
else
setup_user(names);
}
dwflpp::~dwflpp()
{
delete_map(module_cu_cache);
delete_map(cu_function_cache);
delete_map(mod_function_cache);
delete_map(cu_inl_function_cache);
delete_map(global_alias_cache);
delete_map(cu_die_parent_cache);
cu_lines_cache_t::iterator i;
for (i = cu_lines_cache.begin(); i != cu_lines_cache.end(); ++i)
delete_map(*i->second);
delete_map(cu_lines_cache);
if (dwfl)
dwfl_end(dwfl);
}
module_cache::~module_cache ()
{
delete_map(cache);
}
void
dwflpp::get_module_dwarf(bool required, bool report)
{
module_dwarf = dwfl_module_getdwarf(module, &module_bias);
mod_info->dwarf_status = (module_dwarf ? info_present : info_absent);
if (!module_dwarf && report)
{
string msg = _("cannot find ");
if (module_name == "")
msg += "kernel";
else
msg += string("module ") + module_name;
msg += " debuginfo";
int i = dwfl_errno();
if (i)
msg += string(": ") + dwfl_errmsg (i);
msg += " [man warning::debuginfo]";
find_debug_rpms(sess, module_name.c_str());
if (required)
throw SEMANTIC_ERROR (msg);
else
sess.print_warning(msg);
}
}
void
dwflpp::focus_on_module(Dwfl_Module * m, module_info * mi)
{
module = m;
mod_info = mi;
if (m)
{
module_name = dwfl_module_info(module, NULL, &module_start, &module_end,
NULL, NULL, NULL, NULL) ?: "module";
}
else
{
assert(mi && mi->name && mi->name == TOK_KERNEL);
module_name = mi->name;
module_start = 0;
module_end = 0;
module_bias = mi->bias;
}
module_dwarf = NULL;
cu = NULL;
function_name.clear();
function = NULL;
}
void
dwflpp::focus_on_cu(Dwarf_Die * c)
{
assert(c);
assert(module);
cu = c;
function_name.clear();
function = NULL;
}
string
dwflpp::cu_name(void)
{
return dwarf_diename(cu) ?: "<unknown source>";
}
void
dwflpp::focus_on_function(Dwarf_Die * f)
{
assert(f);
assert(module);
assert(cu);
function = f;
function_name = dwarf_diename(function) ?: "function";
}
Dwarf_Die *
dwflpp::query_cu_containing_address(Dwarf_Addr a)
{
Dwarf_Addr bias;
assert(dwfl);
assert(module);
get_module_dwarf();
Dwarf_Die* cudie = dwfl_module_addrdie(module, a, &bias);
assert(bias == module_bias);
return cudie;
}
bool
dwflpp::module_name_matches(const string& pattern)
{
bool t = (fnmatch(pattern.c_str(), module_name.c_str(), 0) == 0);
if (t && sess.verbose>3)
clog << _F("pattern '%s' matches module '%s'\n",
pattern.c_str(), module_name.c_str());
if (!t && sess.verbose>4)
clog << _F("pattern '%s' does not match module '%s'\n",
pattern.c_str(), module_name.c_str());
return t;
}
bool
dwflpp::name_has_wildcard (const string& pattern)
{
return (pattern.find('*') != string::npos ||
pattern.find('?') != string::npos ||
pattern.find('[') != string::npos);
}
bool
dwflpp::module_name_final_match(const string& pattern)
{
return !name_has_wildcard(pattern);
}
bool
dwflpp::function_name_matches_pattern(const string& name, const string& pattern)
{
bool t = (fnmatch(pattern.c_str(), name.c_str(), 0) == 0);
if (t && sess.verbose>3)
clog << _F("pattern '%s' matches function '%s'\n", pattern.c_str(), name.c_str());
return t;
}
bool
dwflpp::function_name_matches(const string& pattern)
{
assert(function);
return function_name_matches_pattern(function_name, pattern);
}
bool
dwflpp::function_scope_matches(const vector<string>& scopes)
{
Dwarf_Die* die = function;
for (int i = scopes.size() - 1; i >= 0; --i)
{
die = get_parent_scope(die);
string name = dwarf_diename(die) ?: "";
if (name_has_wildcard(scopes[i]) ?
function_name_matches_pattern(name, scopes[i]) :
name == scopes[i])
function_name = name + "::" + function_name;
else
return false;
if (!die && i > 0)
return false;
}
return true;
}
void
dwflpp::setup_kernel(const string& name, systemtap_session & s, bool debuginfo_needed)
{
if (! sess.module_cache)
sess.module_cache = new module_cache ();
unsigned offline_search_matches = 0;
dwfl = setup_dwfl_kernel(name, &offline_search_matches, sess);
if (offline_search_matches < 1)
{
if (debuginfo_needed) {
string dir = string(sess.sysroot + "/lib/modules/" + sess.kernel_release );
find_debug_rpms(sess, dir.c_str());
}
throw SEMANTIC_ERROR (_F("missing %s kernel/module debuginfo [man warning::debuginfo] under '%s'",
sess.architecture.c_str(), sess.kernel_build_tree.c_str()));
}
if (dwfl != NULL)
{
ptrdiff_t off = 0;
do
{
assert_no_interrupts();
off = dwfl_getmodules (dwfl, &add_module_build_id_to_hash, &s, off);
}
while (off > 0);
DWFL_ASSERT("dwfl_getmodules", off == 0);
}
build_kernel_blacklist();
}
void
dwflpp::setup_kernel(const vector<string> &names, bool debuginfo_needed)
{
if (! sess.module_cache)
sess.module_cache = new module_cache ();
unsigned offline_search_matches = 0;
set<string> offline_search_names(names.begin(), names.end());
dwfl = setup_dwfl_kernel(offline_search_names,
&offline_search_matches,
sess);
if (offline_search_matches < offline_search_names.size())
{
if (debuginfo_needed) {
string dir = string(sess.sysroot + "/lib/modules/" + sess.kernel_release );
find_debug_rpms(sess, dir.c_str());
}
throw SEMANTIC_ERROR (_F("missing %s kernel/module debuginfo [man warning::debuginfo] under '%s'",
sess.architecture.c_str(), sess.kernel_build_tree.c_str()));
}
build_kernel_blacklist();
}
void
dwflpp::setup_user(const vector<string>& modules, bool debuginfo_needed)
{
if (! sess.module_cache)
sess.module_cache = new module_cache ();
vector<string>::const_iterator it = modules.begin();
dwfl = setup_dwfl_user(it, modules.end(), debuginfo_needed, sess);
if (debuginfo_needed && it != modules.end())
DWFL_ASSERT (string(_F("missing process %s %s debuginfo",
(*it).c_str(), sess.architecture.c_str())),
dwfl);
build_user_blacklist();
}
template<> void
dwflpp::iterate_over_modules<void>(int (*callback)(Dwfl_Module*,
void**,
const char*,
Dwarf_Addr,
void*),
void *data)
{
dwfl_getmodules (dwfl, callback, data, 0);
XXX }
template<> void
dwflpp::iterate_over_cus<void>(int (*callback)(Dwarf_Die*, void*),
void *data,
bool want_types)
{
get_module_dwarf(false);
Dwarf *dw = module_dwarf;
if (!dw) return;
vector<Dwarf_Die>* v = module_cu_cache[dw];
if (v == 0)
{
v = new vector<Dwarf_Die>;
module_cu_cache[dw] = v;
Dwarf_Off off = 0;
size_t cuhl;
Dwarf_Off noff;
while (dwarf_nextcu (dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0)
{
assert_no_interrupts();
Dwarf_Die die_mem;
Dwarf_Die *die;
die = dwarf_offdie (dw, off + cuhl, &die_mem);
if (dwarf_tag (die) == DW_TAG_compile_unit)
v->push_back (*die); off = noff;
}
}
if (want_types && module_tus_read.find(dw) == module_tus_read.end())
{
Dwarf_Off off = 0;
size_t cuhl;
Dwarf_Off noff;
uint64_t type_signature;
while (dwarf_next_unit (dw, off, &noff, &cuhl, NULL, NULL, NULL, NULL,
&type_signature, NULL) == 0)
{
assert_no_interrupts();
Dwarf_Die die_mem;
Dwarf_Die *die;
die = dwarf_offdie_types (dw, off + cuhl, &die_mem);
if (dwarf_tag (die) == DW_TAG_type_unit)
v->push_back (*die); off = noff;
}
module_tus_read.insert(dw);
}
for (vector<Dwarf_Die>::iterator i = v->begin(); i != v->end(); ++i)
{
int rc = (*callback)(&*i, data);
assert_no_interrupts();
if (rc != DWARF_CB_OK)
break;
}
}
bool
dwflpp::func_is_inline()
{
assert (function);
return dwarf_func_inline (function) != 0;
}
bool
dwflpp::func_is_exported()
{
const char *name = dwarf_linkage_name (function) ?: dwarf_diename (function);
assert (function);
int syms = dwfl_module_getsymtab (module);
DWFL_ASSERT (_("Getting symbols"), syms >= 0);
for (int i = 0; i < syms; i++)
{
GElf_Sym sym;
GElf_Word shndxp;
const char *symname = dwfl_module_getsym(module, i, &sym, &shndxp);
if (symname
&& strcmp (name, symname) == 0)
{
if (GELF_ST_TYPE(sym.st_info) == STT_FUNC
&& (GELF_ST_BIND(sym.st_info) == STB_GLOBAL
|| GELF_ST_BIND(sym.st_info) == STB_WEAK
|| GELF_ST_BIND(sym.st_info) == STB_GNU_UNIQUE))
return true;
else
return false;
}
}
return false;
}
void
dwflpp::cache_inline_instances (Dwarf_Die* die)
{
Dwarf_Die origin;
if (dwarf_tag(die) == DW_TAG_inlined_subroutine &&
dwarf_attr_die(die, DW_AT_abstract_origin, &origin))
{
vector<Dwarf_Die>*& v = cu_inl_function_cache[origin.addr];
if (!v)
v = new vector<Dwarf_Die>;
v->push_back(*die);
}
Dwarf_Die child, import;
if (dwarf_child(die, &child) == 0)
do
{
switch (dwarf_tag (&child))
{
case DW_TAG_compile_unit:
case DW_TAG_module:
case DW_TAG_lexical_block:
case DW_TAG_with_stmt:
case DW_TAG_catch_block:
case DW_TAG_try_block:
case DW_TAG_entry_point:
case DW_TAG_inlined_subroutine:
case DW_TAG_subprogram:
cache_inline_instances(&child);
break;
case DW_TAG_imported_unit:
if (dwarf_attr_die(&child, DW_AT_import, &import))
cache_inline_instances(&import);
break;
default:
break;
}
}
while (dwarf_siblingof(&child, &child) == 0);
}
template<> void
dwflpp::iterate_over_inline_instances<void>(int (*callback)(Dwarf_Die*, void*),
void *data)
{
assert (function);
assert (func_is_inline ());
if (cu_inl_function_cache_done.insert(cu->addr).second)
cache_inline_instances(cu);
vector<Dwarf_Die>* v = cu_inl_function_cache[function->addr];
if (!v)
return;
for (vector<Dwarf_Die>::iterator i = v->begin(); i != v->end(); ++i)
{
int rc = (*callback)(&*i, data);
assert_no_interrupts();
if (rc != DWARF_CB_OK)
break;
}
}
void
dwflpp::cache_die_parents(cu_die_parent_cache_t* parents, Dwarf_Die* die)
{
Dwarf_Die child, import;
if (dwarf_child(die, &child) == 0)
do
{
switch (dwarf_tag (&child))
{
case DW_TAG_compile_unit:
case DW_TAG_module:
case DW_TAG_lexical_block:
case DW_TAG_with_stmt:
case DW_TAG_catch_block:
case DW_TAG_try_block:
case DW_TAG_entry_point:
case DW_TAG_inlined_subroutine:
case DW_TAG_subprogram:
case DW_TAG_namespace:
case DW_TAG_class_type:
case DW_TAG_structure_type:
parents->insert(make_pair(child.addr, *die));
cache_die_parents(parents, &child);
break;
case DW_TAG_label:
parents->insert(make_pair(child.addr, *die));
break;
case DW_TAG_imported_unit:
if (dwarf_attr_die(&child, DW_AT_import, &import))
{
parents->insert(make_pair(import.addr, *die));
cache_die_parents(parents, &import);
}
break;
default:
break;
}
}
while (dwarf_siblingof(&child, &child) == 0);
}
cu_die_parent_cache_t*
dwflpp::get_die_parents()
{
assert (cu);
cu_die_parent_cache_t *& parents = cu_die_parent_cache[cu->addr];
if (!parents)
{
parents = new cu_die_parent_cache_t;
cache_die_parents(parents, cu);
if (sess.verbose > 4)
clog << _F("die parent cache %s:%s size %zu", module_name.c_str(),
cu_name().c_str(), parents->size()) << endl;
}
return parents;
}
vector<Dwarf_Die>
dwflpp::getscopes_die(Dwarf_Die* die)
{
cu_die_parent_cache_t *parents = get_die_parents();
vector<Dwarf_Die> scopes;
Dwarf_Die *scope = die;
cu_die_parent_cache_t::iterator it;
do
{
scopes.push_back(*scope);
it = parents->find(scope->addr);
scope = &it->second;
}
while (it != parents->end());
#ifdef DEBUG_DWFLPP_GETSCOPES
Dwarf_Die *dscopes = NULL;
int nscopes = dwarf_getscopes_die(die, &dscopes);
assert(nscopes == (int)scopes.size());
for (unsigned i = 0; i < scopes.size(); ++i)
assert(scopes[i].addr == dscopes[i].addr);
free(dscopes);
#endif
return scopes;
}
std::vector<Dwarf_Die>
dwflpp::getscopes(Dwarf_Die* die)
{
cu_die_parent_cache_t *parents = get_die_parents();
vector<Dwarf_Die> scopes;
Dwarf_Die origin;
Dwarf_Die *scope = die;
cu_die_parent_cache_t::iterator it;
do
{
scopes.push_back(*scope);
if (dwarf_tag(scope) == DW_TAG_inlined_subroutine &&
dwarf_attr_die(scope, DW_AT_abstract_origin, &origin))
scope = &origin;
it = parents->find(scope->addr);
scope = &it->second;
}
while (it != parents->end());
#ifdef DEBUG_DWFLPP_GETSCOPES
Dwarf_Addr pc;
if (die_entrypc(die, &pc))
{
Dwarf_Die *dscopes = NULL;
int nscopes = dwarf_getscopes(cu, pc, &dscopes);
if (nscopes > 0 && dscopes[0].addr == die->addr)
{
assert(nscopes == (int)scopes.size());
for (unsigned i = 0; i < scopes.size(); ++i)
assert(scopes[i].addr == dscopes[i].addr);
}
free(dscopes);
}
#endif
return scopes;
}
std::vector<Dwarf_Die>
dwflpp::getscopes(Dwarf_Addr pc)
{
assert (cu);
vector<Dwarf_Die> scopes;
Dwarf_Die* dwarf_scopes;
int nscopes = dwarf_getscopes(cu, pc, &dwarf_scopes);
if (nscopes > 0)
{
scopes.assign(dwarf_scopes, dwarf_scopes + nscopes);
free(dwarf_scopes);
}
#ifdef DEBUG_DWFLPP_GETSCOPES
if (!scopes.empty())
{
vector<Dwarf_Die> other = getscopes(&scopes[0]);
assert(scopes.size() == other.size());
for (unsigned i = 0; i < scopes.size(); ++i)
assert(scopes[i].addr == other[i].addr);
}
#endif
return scopes;
}
Dwarf_Die*
dwflpp::get_parent_scope(Dwarf_Die* die)
{
Dwarf_Die specification;
if (dwarf_attr_die(die, DW_AT_specification, &specification))
die = &specification;
cu_die_parent_cache_t *parents = get_die_parents();
cu_die_parent_cache_t::iterator it = parents->find(die->addr);
while (it != parents->end())
{
Dwarf_Die* scope = &it->second;
switch (dwarf_tag (scope))
{
case DW_TAG_namespace:
case DW_TAG_class_type:
case DW_TAG_structure_type:
return scope;
default:
break;
}
it = parents->find(scope->addr);
}
return NULL;
}
static const char*
cache_type_prefix(Dwarf_Die* type)
{
switch (dwarf_tag(type))
{
case DW_TAG_enumeration_type:
return "enum ";
case DW_TAG_structure_type:
case DW_TAG_class_type:
return "struct ";
case DW_TAG_union_type:
return "union ";
}
return "";
}
static bool
has_only_decl_members (Dwarf_Die *die)
{
Dwarf_Die child, import;
if (dwarf_child(die, &child) != 0)
return false;
do
{
if (! dwarf_hasattr(&child, DW_AT_declaration))
return false; int tag = dwarf_tag(&child);
if ((tag == DW_TAG_namespace
|| tag == DW_TAG_structure_type
|| tag == DW_TAG_class_type)
&& ! has_only_decl_members (&child))
return false;
if (tag == DW_TAG_imported_unit
&& dwarf_attr_die(&child, DW_AT_import, &import)
&& ! has_only_decl_members (&import))
return false;
}
while (dwarf_siblingof(&child, &child) == 0);
return true; }
int
dwflpp::global_alias_caching_callback(Dwarf_Die *die, bool has_inner_types,
const string& prefix, cu_type_cache_t *cache)
{
const char *name = dwarf_diename(die);
if (!name || dwarf_hasattr(die, DW_AT_declaration)
|| has_only_decl_members(die))
return DWARF_CB_OK;
int tag = dwarf_tag(die);
if (has_inner_types && (tag == DW_TAG_namespace
|| tag == DW_TAG_structure_type
|| tag == DW_TAG_class_type))
iterate_over_types(die, has_inner_types, prefix + name + "::",
global_alias_caching_callback, cache);
if (tag != DW_TAG_namespace)
{
string type_name = prefix + cache_type_prefix(die) + name;
if (cache->find(type_name) == cache->end())
(*cache)[type_name] = *die;
}
return DWARF_CB_OK;
}
int
dwflpp::global_alias_caching_callback_cus(Dwarf_Die *die, dwflpp *dw)
{
mod_cu_type_cache_t *global_alias_cache;
global_alias_cache = &dw->global_alias_cache;
cu_type_cache_t *v = (*global_alias_cache)[die->addr];
if (v != 0)
return DWARF_CB_OK;
v = new cu_type_cache_t;
(*global_alias_cache)[die->addr] = v;
iterate_over_globals(die, global_alias_caching_callback, v);
return DWARF_CB_OK;
}
Dwarf_Die *
dwflpp::declaration_resolve_other_cus(const string& name)
{
iterate_over_cus(global_alias_caching_callback_cus, this, true);
for (mod_cu_type_cache_t::iterator i = global_alias_cache.begin();
i != global_alias_cache.end(); ++i)
{
cu_type_cache_t *v = (*i).second;
if (v->find(name) != v->end())
return & ((*v)[name]);
}
return NULL;
}
Dwarf_Die *
dwflpp::declaration_resolve(const string& name)
{
cu_type_cache_t *v = global_alias_cache[cu->addr];
if (v == 0) {
v = new cu_type_cache_t;
global_alias_cache[cu->addr] = v;
iterate_over_globals(cu, global_alias_caching_callback, v);
if (sess.verbose > 4)
clog << _F("global alias cache %s:%s size %zu", module_name.c_str(),
cu_name().c_str(), v->size()) << endl;
}
XXX
if (v->find(name) == v->end())
return declaration_resolve_other_cus(name);
return & ((*v)[name]);
}
Dwarf_Die *
dwflpp::declaration_resolve(Dwarf_Die *type)
{
const char* name = dwarf_diename(type);
if (!name)
return NULL;
string type_name = cache_type_prefix(type) + string(name);
return declaration_resolve(type_name);
}
int
dwflpp::cu_function_caching_callback (Dwarf_Die* func, cu_function_cache_t *v)
{
const char *name = dwarf_diename(func);
if (!name)
return DWARF_CB_OK;
v->insert(make_pair(string(name), *func));
return DWARF_CB_OK;
}
int
dwflpp::mod_function_caching_callback (Dwarf_Die* cu, cu_function_cache_t *v)
{
dwarf_getfuncs (cu, (int (*)(Dwarf_Die*, void*))cu_function_caching_callback,
v, 0);
return DWARF_CB_OK;
}
template<> int
dwflpp::iterate_over_functions<void>(int (*callback)(Dwarf_Die*, void*),
void *data, const string& function)
{
int rc = DWARF_CB_OK;
assert (module);
assert (cu);
cu_function_cache_t *v = cu_function_cache[cu->addr];
if (v == 0)
{
v = new cu_function_cache_t;
cu_function_cache[cu->addr] = v;
dwarf_getfuncs (cu, (int (*)(Dwarf_Die*, void*))cu_function_caching_callback,
v, 0);
if (sess.verbose > 4)
clog << _F("function cache %s:%s size %zu", module_name.c_str(),
cu_name().c_str(), v->size()) << endl;
mod_info->update_symtab(v);
}
cu_function_cache_t::iterator it;
cu_function_cache_range_t range = v->equal_range(function);
if (range.first != range.second)
{
for (it = range.first; it != range.second; ++it)
{
Dwarf_Die& die = it->second;
if (sess.verbose > 4)
clog << _F("function cache %s:%s hit %s", module_name.c_str(),
cu_name().c_str(), function.c_str()) << endl;
rc = (*callback)(& die, data);
if (rc != DWARF_CB_OK) break;
}
}
else if (startswith(function, "_Z"))
{
for (it = v->begin(); it != v->end(); ++it)
{
if (pending_interrupts) return DWARF_CB_ABORT;
Dwarf_Die& die = it->second;
const char* linkage_name = NULL;
if ((linkage_name = dwarf_linkage_name (&die))
&& function_name_matches_pattern (linkage_name, function))
{
if (sess.verbose > 4)
clog << _F("function cache %s:%s match %s vs %s", module_name.c_str(),
cu_name().c_str(), linkage_name, function.c_str()) << endl;
rc = (*callback)(& die, data);
if (rc != DWARF_CB_OK) break;
}
}
}
else if (name_has_wildcard (function))
{
for (it = v->begin(); it != v->end(); ++it)
{
if (pending_interrupts) return DWARF_CB_ABORT;
const string& func_name = it->first;
Dwarf_Die& die = it->second;
if (function_name_matches_pattern (func_name, function))
{
if (sess.verbose > 4)
clog << _F("function cache %s:%s match %s vs %s", module_name.c_str(),
cu_name().c_str(), func_name.c_str(), function.c_str()) << endl;
rc = (*callback)(& die, data);
if (rc != DWARF_CB_OK) break;
}
}
}
else {
}
return rc;
}
template<> int
dwflpp::iterate_single_function<void>(int (*callback)(Dwarf_Die*, void*),
void *data, const string& function)
{
int rc = DWARF_CB_OK;
assert (module);
get_module_dwarf(false);
if (!module_dwarf)
return rc;
cu_function_cache_t *v = mod_function_cache[module_dwarf];
if (v == 0)
{
v = new cu_function_cache_t;
mod_function_cache[module_dwarf] = v;
iterate_over_cus (mod_function_caching_callback, v, false);
if (sess.verbose > 4)
clog << _F("module function cache %s size %zu", module_name.c_str(),
v->size()) << endl;
mod_info->update_symtab(v);
}
cu_function_cache_t::iterator it;
cu_function_cache_range_t range = v->equal_range(function);
if (range.first != range.second)
{
for (it = range.first; it != range.second; ++it)
{
Dwarf_Die cu_mem;
Dwarf_Die& die = it->second;
if (sess.verbose > 4)
clog << _F("module function cache %s hit %s", module_name.c_str(),
function.c_str()) << endl;
focus_on_cu(dwarf_diecu(&die, &cu_mem, NULL, NULL));
rc = (*callback)(& die, data);
if (rc != DWARF_CB_OK) break;
}
}
this->cu = NULL;
this->function_name.clear();
this->function = NULL;
return rc;
}
template<> int
dwflpp::iterate_over_globals<void>(Dwarf_Die *cu_die,
int (*callback)(Dwarf_Die*,
bool,
const string&,
void*),
void *data)
{
assert (cu_die);
assert (dwarf_tag(cu_die) == DW_TAG_compile_unit
|| dwarf_tag(cu_die) == DW_TAG_type_unit
|| dwarf_tag(cu_die) == DW_TAG_partial_unit);
if (dwarf_tag(cu_die) == DW_TAG_partial_unit)
return DWARF_CB_OK;
bool has_inner_types = dwarf_srclang(cu_die) == DW_LANG_C_plus_plus;
return iterate_over_types(cu_die, has_inner_types, "", callback, data);
}
template<> int
dwflpp::iterate_over_types<void>(Dwarf_Die *top_die,
bool has_inner_types,
const string& prefix,
int (* callback)(Dwarf_Die*,
bool,
const string&,
void*),
void *data)
{
int rc = DWARF_CB_OK;
Dwarf_Die die, import;
assert (top_die);
if (dwarf_child(top_die, &die) != 0)
return rc;
do
switch (dwarf_tag(&die))
{
case DW_TAG_base_type:
case DW_TAG_enumeration_type:
case DW_TAG_structure_type:
case DW_TAG_class_type:
case DW_TAG_typedef:
case DW_TAG_union_type:
case DW_TAG_namespace:
rc = (*callback)(&die, has_inner_types, prefix, data);
break;
case DW_TAG_imported_unit:
if (dwarf_attr_die(&die, DW_AT_import, &import))
rc = iterate_over_types(&import, has_inner_types, prefix,
callback, data);
break;
}
while (rc == DWARF_CB_OK && dwarf_siblingof(&die, &die) == 0);
return rc;
}
template<> int
dwflpp::iterate_over_notes<void>(void *object, void (*callback)(void*,
const string&,
const string&,
int,
const char*,
size_t))
{
Dwarf_Addr bias;
Elf* elf = dwfl_module_getelf (module, &bias);
size_t shstrndx;
if (elf_getshdrstrndx (elf, &shstrndx))
return elf_errno();
Elf_Scn *scn = NULL;
vector<Dwarf_Die> notes;
while ((scn = elf_nextscn (elf, scn)) != NULL)
{
GElf_Shdr shdr;
if (gelf_getshdr (scn, &shdr) == NULL)
continue;
switch (shdr.sh_type)
{
case SHT_NOTE:
if (!(shdr.sh_flags & SHF_ALLOC))
{
string scn_name = elf_strptr(elf, shstrndx, shdr.sh_name);
Elf_Data *data = elf_getdata (scn, NULL);
size_t next;
GElf_Nhdr nhdr;
size_t name_off;
size_t desc_off;
for (size_t offset = 0;
(next = gelf_getnote (data, offset, &nhdr, &name_off, &desc_off)) > 0;
offset = next)
{
const char *note_name_addr = (const char *)data->d_buf + name_off;
const char *note_desc_addr = (const char *)data->d_buf + desc_off;
string note_name = nhdr.n_namesz > 1 ? string(note_name_addr, nhdr.n_namesz-1) : "";
(*callback) (object, scn_name, note_name, nhdr.n_type,
note_desc_addr, nhdr.n_descsz);
}
}
break;
}
}
return 0;
}
template<> void
dwflpp::iterate_over_libraries<void>(void (*callback)(void*, const char*),
void *data)
{
std::set<std::string> added;
string interpreter;
assert (this->module_name.length() != 0);
Dwarf_Addr bias;
Elf *elf = dwfl_module_getelf (module, &bias);
for (int i = 0; ; i++)
{
GElf_Phdr mem;
GElf_Phdr *phdr;
phdr = gelf_getphdr (elf, i, &mem);
if (phdr == NULL)
break;
if (phdr->p_type == PT_INTERP)
{
size_t maxsize;
char *filedata = elf_rawfile (elf, &maxsize);
if (filedata != NULL && phdr->p_offset < maxsize)
interpreter = (char*) (filedata + phdr->p_offset);
break;
}
}
if (interpreter.length() == 0)
return;
if (interpreter != "/lib/ld.so.1" && interpreter != "/lib/ld64.so.1" && interpreter != "/lib64/ld64.so.1"
&& interpreter != "/lib/ld-linux-ia64.so.2" && interpreter != "/emul/ia32-linux/lib/ld-linux.so.2"
&& interpreter != "/lib64/ld-linux-x86-64.so.2" && interpreter != "/lib/ld-linux.so.2" && interpreter != "/lib/ld-linux.so.3" && interpreter != "/lib/ld-linux-armhf.so.3" && interpreter != "/lib/ld-linux-aarch64.so.1" )
{
sess.print_warning (_F("module %s --ldd skipped: unsupported interpreter: %s",
module_name.c_str(), interpreter.c_str()));
return;
}
vector<string> ldd_command;
ldd_command.push_back("/usr/bin/env");
ldd_command.push_back("LD_TRACE_LOADED_OBJECTS=1");
ldd_command.push_back("LD_WARN=yes");
ldd_command.push_back("LD_BIND_NOW=yes");
ldd_command.push_back(interpreter);
ldd_command.push_back(module_name);
FILE *fp;
int child_fd;
pid_t child = stap_spawn_piped(sess.verbose, ldd_command, NULL, &child_fd);
if (child <= 0 || !(fp = fdopen(child_fd, "r")))
clog << _F("library iteration on %s failed: %s",
module_name.c_str(), strerror(errno)) << endl;
else
{
while (1) {
char linebuf[256];
char *soname = 0;
char *shlib = 0;
unsigned long int addr = 0;
char *line = fgets (linebuf, 256, fp);
if (line == 0) break;
#if __GLIBC__ >2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 7)
#define MS_FMT "%ms"
#else
#define MS_FMT "%as"
#endif
int nf = sscanf (line, MS_FMT " => " MS_FMT " (0x%lx)",
&soname, &shlib, &addr);
if (nf != 3 || shlib[0] != '/')
{
nf = sscanf (line, " " MS_FMT " (0x%lx)", &shlib, &addr);
if (nf != 2 || shlib[0] != '/')
continue; }
if (added.find (shlib) == added.end())
{
if (sess.verbose > 2)
{
clog << _F("Added -d '%s", shlib);
if (nf == 3)
clog << _F("' due to '%s'", soname);
else
clog << "'";
clog << endl;
}
added.insert (shlib);
}
free (soname);
free (shlib);
}
if ((fclose(fp) || stap_waitpid(sess.verbose, child)))
sess.print_warning("failed to read libraries from " + module_name + ": " + strerror(errno));
}
for (std::set<std::string>::iterator it = added.begin();
it != added.end();
it++)
{
string modname = *it;
(callback) (data, modname.c_str());
}
}
template<> int
dwflpp::iterate_over_plt<void>(void *object, void (*callback)(void*,
const char*,
size_t))
{
Dwarf_Addr load_addr;
Elf* elf = dwfl_module_getelf (module, &load_addr);
size_t shstrndx;
assert (elf_getshdrstrndx (elf, &shstrndx) >= 0);
for (int i = 0; ; i++)
{
GElf_Phdr mem;
GElf_Phdr *phdr;
phdr = gelf_getphdr (elf, i, &mem);
if (phdr == NULL)
break;
if (phdr->p_type == PT_LOAD)
{
load_addr = phdr->p_vaddr;
break;
}
}
Elf_Scn *scn = NULL;
GElf_Shdr *plt_shdr = NULL;
GElf_Shdr plt_shdr_mem;
while ((scn = elf_nextscn (elf, scn)))
{
GElf_Shdr *shdr = gelf_getshdr (scn, &plt_shdr_mem);
assert (shdr != NULL);
if (strcmp (elf_strptr (elf, shstrndx, shdr->sh_name), ".plt") == 0)
{
plt_shdr = shdr;
break;
}
}
if (plt_shdr == NULL)
return 0;
int plt0_entry_size;
int plt_entry_size;
GElf_Ehdr ehdr_mem;
GElf_Ehdr* em = gelf_getehdr (elf, &ehdr_mem);
switch (em->e_machine)
{
case EM_386: plt0_entry_size = 16; plt_entry_size = 16; break;
case EM_X86_64: plt0_entry_size = 16; plt_entry_size = 16; break;
case EM_ARM: plt0_entry_size = 20; plt_entry_size = 12; break;
case EM_PPC64:
case EM_S390:
case EM_PPC:
default:
throw SEMANTIC_ERROR(".plt is not supported on this architecture");
}
scn = NULL;
while ((scn = elf_nextscn (elf, scn)))
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
bool have_rela = false;
bool have_rel = false;
if (shdr == NULL)
continue;
assert (shdr != NULL);
if ((have_rela = (strcmp (elf_strptr (elf, shstrndx, shdr->sh_name), ".rela.plt") == 0))
|| (have_rel = (strcmp (elf_strptr (elf, shstrndx, shdr->sh_name), ".rel.plt") == 0)))
{
Elf_Data *data = elf_getdata (scn, NULL);
assert (data != NULL);
Elf_Scn *symscn = elf_getscn (elf, shdr->sh_link);
GElf_Shdr symshdr_mem;
GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
assert (symshdr != NULL);
Elf_Data *symdata = elf_getdata (symscn, NULL);
assert (symdata != NULL);
unsigned int nsyms = shdr->sh_size / shdr->sh_entsize;
for (unsigned int cnt = 0; cnt < nsyms; ++cnt)
{
GElf_Ehdr ehdr_mem;
GElf_Ehdr* em = gelf_getehdr (elf, &ehdr_mem);
if (em == 0) { DWFL_ASSERT ("dwfl_getehdr", dwfl_errno()); }
GElf_Rela relamem;
GElf_Rela *rela = NULL;
GElf_Rel relmem;
GElf_Rel *rel = NULL;
if (have_rela)
{
rela = gelf_getrela (data, cnt, &relamem);
assert (rela != NULL);
}
else if (have_rel)
{
rel = gelf_getrel (data, cnt, &relmem);
assert (rel != NULL);
}
GElf_Sym symmem;
Elf32_Word xndx;
Elf_Data *xndxdata = NULL;
GElf_Sym *sym =
gelf_getsymshndx (symdata, xndxdata,
GELF_R_SYM (have_rela ? rela->r_info : rel->r_info),
&symmem, &xndx);
assert (sym != NULL);
Dwarf_Addr addr = plt_shdr->sh_offset + plt0_entry_size + cnt * plt_entry_size;
if (elf_strptr (elf, symshdr->sh_link, sym->st_name))
(*callback) (object, elf_strptr (elf, symshdr->sh_link, sym->st_name), addr + load_addr);
}
break; }
}
return 0;
}
static bool
compare_lines(Dwarf_Line* a, Dwarf_Line* b)
{
if (a == b)
return false;
int lineno_a = DWARF_LINENO(a);
int lineno_b = DWARF_LINENO(b);
if (lineno_a == lineno_b)
return DWARF_LINEADDR(a) < DWARF_LINEADDR(b);
return lineno_a < lineno_b;
}
struct lineno_comparator {
int lineno;
lineno_comparator(int lineno): lineno(lineno) {}
bool operator() (Dwarf_Line* a, Dwarf_Line* b)
{
assert(a || b);
if (a)
return DWARF_LINENO(a) < lineno;
else
return lineno < DWARF_LINENO(b);
}
};
static lines_range_t
lineno_equal_range(lines_t* v, int lineno)
{
lineno_comparator lc(lineno);
return equal_range(v->begin(), v->end(), (Dwarf_Line*)NULL, lc);
}
lines_t*
dwflpp::get_cu_lines_sorted_by_lineno(const char *srcfile)
{
assert(cu);
srcfile_lines_cache_t *srcfile_lines = cu_lines_cache[cu];
if (!srcfile_lines)
{
srcfile_lines = new srcfile_lines_cache_t();
cu_lines_cache[cu] = srcfile_lines;
}
lines_t *lines = (*srcfile_lines)[srcfile];
if (!lines)
{
size_t nlines_cu = 0;
Dwarf_Lines *lines_cu = NULL;
DWARF_ASSERT("dwarf_getsrclines",
dwarf_getsrclines(cu, &lines_cu, &nlines_cu));
lines = new lines_t();
(*srcfile_lines)[srcfile] = lines;
for (size_t i = 0; i < nlines_cu; i++)
{
Dwarf_Line *line = dwarf_onesrcline(lines_cu, i);
const char *linesrc = DWARF_LINESRC(line);
if (strcmp(srcfile, linesrc))
continue;
lines->push_back(line);
}
if (lines->size() > 1)
sort(lines->begin(), lines->end(), compare_lines);
if (sess.verbose > 3)
{
clog << _F("found the following lines for %s:", srcfile) << endl;
lines_t::iterator i;
for (i = lines->begin(); i != lines->end(); ++i)
cout << DWARF_LINENO(*i) << " = " << hex
<< DWARF_LINEADDR(*i) << dec << endl;
}
}
return lines;
}
static Dwarf_Line*
get_func_first_line(Dwarf_Die *cu, base_func_info& func)
{
Dwarf_Line *line = dwarf_getsrc_die(cu, func.entrypc);
if (line && DWARF_LINEADDR(line) == func.entrypc)
return line;
size_t nlines = 0;
Dwarf_Lines *lines = NULL;
DWARF_ASSERT("dwarf_getsrclines",
dwarf_getsrclines(cu, &lines, &nlines));
for (size_t i = 0; i < nlines; i++)
{
line = dwarf_onesrcline(lines, i);
if (dwarf_haspc(&func.die, DWARF_LINEADDR(line)))
return line;
}
return NULL;
}
static lines_t
collect_lines_in_die(lines_range_t range, Dwarf_Die *die)
{
lines_t lines_in_die;
for (lines_t::iterator line = range.first;
line != range.second; ++line)
if (dwarf_haspc(die, DWARF_LINEADDR(*line)))
lines_in_die.push_back(*line);
return lines_in_die;
}
static void
add_matching_lines_in_func(Dwarf_Die *cu,
lines_t *cu_lines,
base_func_info& func,
lines_t& matching_lines)
{
Dwarf_Line *start_line = get_func_first_line(cu, func);
if (!start_line)
return;
for (int lineno = DWARF_LINENO(start_line);;)
{
lines_range_t range = lineno_equal_range(cu_lines, lineno);
lines_t lines_in_die = collect_lines_in_die(range, &func.die);
if (lines_in_die.empty())
break;
matching_lines.push_back(lines_in_die[0]);
if (range.second == cu_lines->end())
break;
lineno = DWARF_LINENO(*range.second);
}
}
void
dwflpp::collect_all_lines(char const * srcfile,
base_func_info_map_t& funcs,
lines_t& matching_lines)
{
lines_t *cu_lines = get_cu_lines_sorted_by_lineno(srcfile);
for (base_func_info_map_t::iterator func = funcs.begin();
func != funcs.end(); ++func)
add_matching_lines_in_func(cu, cu_lines, *func, matching_lines);
}
static void
add_matching_line_in_die(lines_t *cu_lines,
lines_t& matching_lines,
Dwarf_Die *die, int lineno)
{
lines_range_t lineno_range = lineno_equal_range(cu_lines, lineno);
lines_t lines_in_die = collect_lines_in_die(lineno_range, die);
if (lines_in_die.empty())
return;
matching_lines.push_back(lines_in_die[0]);
}
void
dwflpp::collect_lines_for_single_lineno(char const * srcfile,
int lineno,
bool is_relative,
base_func_info_map_t& funcs,
lines_t& matching_lines)
{
lines_t *cu_lines = get_cu_lines_sorted_by_lineno(srcfile);
for (base_func_info_map_t::iterator func = funcs.begin();
func != funcs.end(); ++func)
add_matching_line_in_die(cu_lines, matching_lines, &func->die,
is_relative ? lineno + func->decl_line
: lineno);
}
static bool
functions_have_lineno(base_func_info_map_t& funcs,
lines_t *lines, int lineno)
{
lines_range_t lineno_range = lineno_equal_range(lines, lineno);
if (lineno_range.first == lineno_range.second)
return false;
for (base_func_info_map_t::iterator func = funcs.begin();
func != funcs.end(); ++func)
if (!collect_lines_in_die(lineno_range, &func->die).empty())
return true;
return false;
}
pair<int,int>
dwflpp::get_nearest_linenos(char const * srcfile,
int lineno,
base_func_info_map_t& funcs)
{
assert(cu);
lines_t *cu_lines = get_cu_lines_sorted_by_lineno(srcfile);
pair<int,int> nearest_linenos = make_pair(-1, -1);
for (size_t i = 1; i < 6; ++i)
{
if (nearest_linenos.first == -1 && functions_have_lineno(funcs, cu_lines, lineno-i))
nearest_linenos.first = lineno - i;
if (nearest_linenos.second == -1 && functions_have_lineno(funcs, cu_lines, lineno+i))
nearest_linenos.second = lineno + i;
}
return nearest_linenos;
}
int
dwflpp::get_nearest_lineno(char const * srcfile,
int lineno,
base_func_info_map_t& funcs)
{
assert(cu);
pair<int,int> nearest_linenos = get_nearest_linenos(srcfile, lineno, funcs);
if (nearest_linenos.first > 0
&& nearest_linenos.second > 0)
{
if (lineno - nearest_linenos.first < nearest_linenos.second - lineno)
return nearest_linenos.first;
else
return nearest_linenos.second;
}
else if (nearest_linenos.first > 0)
return nearest_linenos.first;
else if (nearest_linenos.second > 0)
return nearest_linenos.second;
else
return -1;
}
void
dwflpp::suggest_alternative_linenos(char const * srcfile,
int lineno,
base_func_info_map_t& funcs)
{
assert(cu);
pair<int,int> nearest_linenos = get_nearest_linenos(srcfile, lineno, funcs);
stringstream advice;
advice << _F("no line records for %s:%d [man error::dwarf]", srcfile, lineno);
if (nearest_linenos.first > 0 || nearest_linenos.second > 0)
{
advice << _(" (try ");
if (nearest_linenos.first > 0)
advice << ":" << nearest_linenos.first;
if (nearest_linenos.first > 0 && nearest_linenos.second > 0)
advice << _(" or ");
if (nearest_linenos.second > 0)
advice << ":" << nearest_linenos.second;
advice << ")";
}
throw SEMANTIC_ERROR (advice.str());
}
static base_func_info_map_t
get_funcs_in_srcfile(base_func_info_map_t& funcs,
const char * srcfile)
{
base_func_info_map_t matching_funcs;
for (base_func_info_map_t::iterator func = funcs.begin();
func != funcs.end(); ++func)
if (strcmp(srcfile, func->decl_file) == 0)
matching_funcs.push_back(*func);
return matching_funcs;
}
template<> void
dwflpp::iterate_over_srcfile_lines<void>(char const * srcfile,
const vector<int>& linenos,
enum lineno_t lineno_type,
base_func_info_map_t& funcs,
void (* callback) (Dwarf_Addr,
int, void*),
bool has_nearest,
void *data)
{
assert(cu);
base_func_info_map_t current_funcs = get_funcs_in_srcfile(funcs, srcfile);
if (current_funcs.empty())
return;
lines_t matching_lines;
if (lineno_type == ABSOLUTE)
collect_lines_for_single_lineno(srcfile, linenos[0], false, current_funcs, matching_lines);
else if (lineno_type == RELATIVE)
collect_lines_for_single_lineno(srcfile, linenos[0], true, current_funcs, matching_lines);
else if (lineno_type == WILDCARD)
collect_all_lines(srcfile, current_funcs, matching_lines);
else if (lineno_type == ENUMERATED)
for (vector<int>::const_iterator it = linenos.begin(); it != linenos.end(); it++)
collect_lines_for_single_lineno(srcfile, *it, false, current_funcs, matching_lines);
if (matching_lines.empty() && has_nearest)
{
int lineno = linenos[0];
if (lineno_type == RELATIVE)
lineno += current_funcs[0].decl_line;
int nearest_lineno = get_nearest_lineno(srcfile, lineno, current_funcs);
if (nearest_lineno > 0)
collect_lines_for_single_lineno(srcfile, nearest_lineno, false, current_funcs, matching_lines);
}
if (!matching_lines.empty())
{
set<Dwarf_Addr> probed_addrs;
for (lines_t::iterator line = matching_lines.begin();
line != matching_lines.end(); ++line)
{
int lineno = DWARF_LINENO(*line);
Dwarf_Addr addr = DWARF_LINEADDR(*line);
bool is_new_addr = probed_addrs.insert(addr).second;
if (is_new_addr)
callback(addr, lineno, data);
}
}
else if (lineno_type == ABSOLUTE ||
lineno_type == RELATIVE)
{
int lineno = linenos[0];
if (lineno_type == RELATIVE)
lineno += current_funcs[0].decl_line;
suggest_alternative_linenos(srcfile, lineno, current_funcs);
}
}
template<> void
dwflpp::iterate_over_labels<void>(Dwarf_Die *begin_die,
const string& sym,
const base_func_info& function,
const vector<int>& linenos,
enum lineno_t lineno_type,
void *data,
void (* callback)(const base_func_info&,
const char*,
const char*,
int,
Dwarf_Die*,
Dwarf_Addr,
void*))
{
get_module_dwarf();
Dwarf_Die die, import;
const char *name;
int res = dwarf_child (begin_die, &die);
if (res != 0)
return;
do
{
switch (dwarf_tag(&die))
{
case DW_TAG_label:
name = dwarf_diename (&die);
if (name &&
(name == sym
|| (name_has_wildcard(sym)
&& function_name_matches_pattern (name, sym))))
{
Dwarf_Addr stmt_addr;
if (dwarf_lowpc (&die, &stmt_addr) == 0)
{
int dline;
const char *file = dwarf_decl_file (&die) ?: "<unknown source>";
dwarf_decl_line (&die, &dline);
vector<Dwarf_Die> scopes = getscopes_die(&die);
if (scopes.size() > 1)
{
Dwarf_Die scope;
if (!inner_die_containing_pc(scopes[1], stmt_addr, scope))
{
sess.print_warning(_F("label '%s' at address %s (dieoffset: %s) is not "
"contained by its scope '%s' (dieoffset: %s) -- bad"
" debuginfo? [man error::dwarf]", name, lex_cast_hex(stmt_addr).c_str(),
lex_cast_hex(dwarf_dieoffset(&die)).c_str(),
(dwarf_diename(&scope) ?: "<unknown>"),
lex_cast_hex(dwarf_dieoffset(&scope)).c_str()));
}
bool matches_lineno;
if (lineno_type == ABSOLUTE)
matches_lineno = dline == linenos[0];
else if (lineno_type == RELATIVE)
matches_lineno = dline == linenos[0] + function.decl_line;
else if (lineno_type == ENUMERATED)
matches_lineno = (binary_search(linenos.begin(), linenos.end(), dline));
else matches_lineno = true;
if (matches_lineno)
callback(function, name, file, dline,
&scope, stmt_addr, data);
}
}
}
break;
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine:
break;
case DW_TAG_imported_unit:
if (dwarf_attr_die(&die, DW_AT_import, &import))
iterate_over_labels (&import, sym, function, linenos,
lineno_type, data, callback);
break;
default:
if (dwarf_haschildren (&die))
iterate_over_labels (&die, sym, function, linenos,
lineno_type, data, callback);
break;
}
}
while (dwarf_siblingof (&die, &die) == 0);
}
struct external_function_query {
dwflpp* dw;
const string name;
Dwarf_Die die;
Dwarf_Addr addr;
bool resolved;
external_function_query(dwflpp* dw, const string& name):
dw(dw), name(name), addr(0), resolved(false) {}
};
int
dwflpp::external_function_cu_callback (Dwarf_Die* cu, external_function_query *efq)
{
efq->dw->focus_on_cu(cu);
return efq->dw->iterate_over_functions(external_function_func_callback,
efq, efq->name);
}
int
dwflpp::external_function_func_callback (Dwarf_Die* func, external_function_query *efq)
{
Dwarf_Attribute external;
Dwarf_Addr func_addr;
if (dwarf_attr_integrate(func, DW_AT_external, &external) != NULL &&
dwarf_lowpc(func, &func_addr) == 0)
{
efq->die = *func;
efq->addr = func_addr;
efq->resolved = true;
return DWARF_CB_ABORT; }
return DWARF_CB_OK;
}
template<> void
dwflpp::iterate_over_callees<void>(Dwarf_Die *begin_die,
const string& sym,
long recursion_depth,
void *data,
void (* callback)(base_func_info&,
base_func_info&,
stack<Dwarf_Addr>*,
void*),
base_func_info& caller,
stack<Dwarf_Addr> *callers)
{
get_module_dwarf();
Dwarf_Die die, import;
Dwarf_Die origin;
Dwarf_Addr func_addr;
Dwarf_Addr caller_uw_addr;
Dwarf_Attribute attr;
base_func_info callee;
if (dwarf_child(begin_die, &die) != 0)
return;
bool free_callers = false;
if (callers == NULL) {
callers = new stack<Dwarf_Addr>();
free_callers = true;
}
do
{
bool inlined = false;
switch (dwarf_tag(&die))
{
case DW_TAG_inlined_subroutine:
inlined = true;
case DW_TAG_GNU_call_site:
callee.name = dwarf_diename(&die) ?: "";
if (callee.name.empty())
continue;
if (callee.name != sym)
{
if (!name_has_wildcard(sym))
continue;
if (!function_name_matches_pattern(callee.name, sym))
continue;
}
if (dwarf_attr_die(&die, DW_AT_abstract_origin, &origin) == NULL)
continue;
if (dwarf_lowpc(&die, &caller_uw_addr) != 0)
continue;
if (inlined)
func_addr = caller_uw_addr;
else if (dwarf_lowpc(&origin, &func_addr) != 0)
{
if (dwarf_attr_integrate(&origin, DW_AT_external,
&attr) != NULL)
{
Dwarf_Die *old_cu = cu;
external_function_query efq(this, dwarf_linkage_name(&origin) ?: callee.name);
iterate_over_cus(external_function_cu_callback, &efq, false);
cu = old_cu;
if (!efq.resolved) continue;
func_addr = efq.addr;
origin = efq.die;
}
else continue;
}
callee.decl_file = dwarf_decl_file (&origin) ?: "<unknown source>";
dwarf_decl_line (&origin, &callee.decl_line);
if (!inlined)
callers->push(caller_uw_addr);
callee.die = inlined ? die : origin;
callee.entrypc = func_addr;
callback(callee, caller, callers, data);
if (!inlined
&& dwarf_attr_integrate(&die, DW_AT_GNU_tail_call, &attr) != NULL)
sess.print_warning (_F("Callee \"%s\" in function \"%s\" is a tail call: "
".callee probe may not fire. Try placing the probe "
"directly on the callee function instead.",
callee.name.c_str(), caller.name.c_str()));
if (recursion_depth > 1)
iterate_over_callees(inlined ? &die : &origin,
sym, recursion_depth-1, data,
callback, callee, callers);
if (!inlined)
callers->pop();
break;
case DW_TAG_subprogram:
break;
case DW_TAG_imported_unit:
if (dwarf_attr_die(&die, DW_AT_import, &import))
iterate_over_callees (&import, sym, recursion_depth, data,
callback, caller, callers);
break;
default:
if (dwarf_haschildren (&die))
iterate_over_callees (&die, sym, recursion_depth, data,
callback, caller, callers);
break;
}
}
while (dwarf_siblingof (&die, &die) == 0);
if (free_callers && callers != NULL)
delete callers;
}
void
dwflpp::collect_srcfiles_matching (string const & pattern,
set<string> & filtered_srcfiles)
{
assert (module);
assert (cu);
size_t nfiles;
Dwarf_Files *srcfiles;
string prefixed_pattern = string("*/") + pattern;
DWARF_ASSERT ("dwarf_getsrcfiles",
dwarf_getsrcfiles (cu, &srcfiles, &nfiles));
{
for (size_t i = 0; i < nfiles; ++i)
{
char const * fname = dwarf_filesrc (srcfiles, i, NULL, NULL);
if (fnmatch (pattern.c_str(), fname, 0) == 0 ||
fnmatch (prefixed_pattern.c_str(), fname, 0) == 0)
{
filtered_srcfiles.insert (fname);
if (sess.verbose>2)
clog << _F("selected source file '%s'\n", fname);
}
}
}
}
void
dwflpp::resolve_prologue_endings (func_info_map_t & funcs)
{
assert(module);
assert(cu);
size_t nlines = 0;
Dwarf_Lines *lines = NULL;
DWARF_ASSERT ("dwarf_getsrclines",
dwarf_getsrclines(cu, &lines, &nlines));
vector<Dwarf_Addr> addrs;
for (size_t i = 0; i < nlines; i++)
{
Dwarf_Line* line = dwarf_onesrcline(lines, i);
Dwarf_Addr addr = DWARF_LINEADDR(line);
if (!addrs.empty() && addr < addrs.back())
throw SEMANTIC_ERROR(_("lines from dwarf_getsrclines() not sorted"));
addrs.push_back(addr);
}
bool consider_decl_line = false;
{
string prod, vers;
if (is_gcc_producer(cu, prod, vers)
&& strverscmp(vers.c_str(), "4.4.0") < 0)
consider_decl_line = true;
}
for(func_info_map_t::iterator it = funcs.begin(); it != funcs.end(); it++)
{
#if 0#endif
Dwarf_Addr entrypc = it->entrypc;
Dwarf_Addr highpc; DWFL_ASSERT ("dwarf_highpc", dwarf_highpc (& it->die,
& highpc));
if (it->decl_file == 0) it->decl_file = "";
unsigned entrypc_srcline_idx = 0;
Dwarf_Line *entrypc_srcline = NULL;
{
vector<Dwarf_Addr>::const_iterator it_addr =
lower_bound(addrs.begin(), addrs.end(), entrypc);
if (it_addr != addrs.end() && *it_addr == entrypc)
{
entrypc_srcline_idx = it_addr - addrs.begin();
entrypc_srcline = dwarf_onesrcline(lines, entrypc_srcline_idx);
}
}
if (!entrypc_srcline)
{
if (sess.verbose > 2)
clog << _F("missing entrypc dwarf line record for function '%s'\n",
it->name.c_str());
continue;
}
if (entrypc == 0)
{
if (sess.verbose > 2)
clog << _F("null entrypc dwarf line record for function '%s'\n",
it->name.c_str());
continue;
}
if (sess.verbose>2)
clog << _F("searching for prologue of function '%s' %#" PRIx64 "-%#" PRIx64
"@%s:%d\n", it->name.c_str(), entrypc, highpc, it->decl_file,
it->decl_line);
int entrypc_srcline_lineno = DWARF_LINENO(entrypc_srcline);
unsigned postprologue_srcline_idx = entrypc_srcline_idx;
Dwarf_Line *postprologue_srcline = entrypc_srcline;
while (postprologue_srcline_idx < nlines)
{
postprologue_srcline = dwarf_onesrcline(lines,
postprologue_srcline_idx);
Dwarf_Addr lineaddr = DWARF_LINEADDR(postprologue_srcline);
const char* linesrc = DWARF_LINESRC(postprologue_srcline);
int lineno = DWARF_LINENO(postprologue_srcline);
bool lineprologue_end = DWARF_LINEPROLOGUEEND(postprologue_srcline);
if (sess.verbose>2)
clog << _F("checking line record %#" PRIx64 "@%s:%d%s\n", lineaddr,
linesrc, lineno, lineprologue_end ? " (marked)" : "");
if (lineaddr >= highpc)
break;
if (lineprologue_end)
break;
if (strcmp(linesrc, it->decl_file))
break;
if (lineno != entrypc_srcline_lineno)
break;
if (postprologue_srcline_idx != entrypc_srcline_idx)
break;
if (consider_decl_line && lineno != it->decl_line)
break;
postprologue_srcline_idx ++;
}
Dwarf_Addr postprologue_addr = DWARF_LINEADDR(postprologue_srcline);
if (postprologue_addr >= highpc)
{
Dwarf_Line *lr = dwarf_onesrcline(lines, postprologue_srcline_idx-1);
postprologue_addr = DWARF_LINEADDR(lr);
}
it->prologue_end = postprologue_addr;
if (sess.verbose>2)
{
clog << _F("prologue found function '%s'", it->name.c_str());
if (postprologue_addr == entrypc)
clog << _(" (naked)");
if (DWARF_LINEADDR(postprologue_srcline) >= highpc)
clog << _(" (tail-call?)");
if (DWARF_LINEPROLOGUEEND(postprologue_srcline))
clog << _(" (marked)");
clog << " = 0x" << hex << postprologue_addr << dec << "\n";
}
} }
bool
dwflpp::function_entrypc (Dwarf_Addr * addr)
{
assert (function);
return (dwarf_entrypc (function, addr) == 0 && *addr != 0);
}
bool
dwflpp::die_entrypc (Dwarf_Die * die, Dwarf_Addr * addr)
{
int rc = 0;
string lookup_method;
* addr = 0;
lookup_method = "dwarf_entrypc";
rc = dwarf_entrypc (die, addr);
if (rc)
{
lookup_method = "dwarf_ranges";
Dwarf_Addr base;
Dwarf_Addr begin;
Dwarf_Addr end;
ptrdiff_t offset = dwarf_ranges (die, 0, &base, &begin, &end);
if (offset < 0) rc = -1;
else if (offset > 0)
{
* addr = begin;
rc = 0;
unsigned extra = 0;
while ((offset = dwarf_ranges (die, offset, &base, &begin, &end)) > 0)
extra ++;
if (extra)
lookup_method += _F(", ignored %s more", lex_cast(extra).c_str());
}
}
if (rc == 0 && *addr == 0)
{
lookup_method += _(" (skip comdat)");
rc = 1;
}
if (sess.verbose > 2)
clog << _F("entry-pc lookup (%s dieoffset: %s) = %#" PRIx64 " (rc %d)", lookup_method.c_str(),
lex_cast_hex(dwarf_dieoffset(die)).c_str(), *addr, rc) << endl;
return (rc == 0);
}
void
dwflpp::function_die (Dwarf_Die *d)
{
assert (function);
*d = *function;
}
void
dwflpp::function_file (char const ** c)
{
assert (function);
assert (c);
*c = dwarf_decl_file (function) ?: "<unknown source>";
}
void
dwflpp::function_line (int *linep)
{
assert (function);
dwarf_decl_line (function, linep);
}
bool
dwflpp::die_has_pc (Dwarf_Die & die, Dwarf_Addr pc)
{
int res = dwarf_haspc (&die, pc);
return res == 1;
}
bool
dwflpp::inner_die_containing_pc(Dwarf_Die& scope, Dwarf_Addr addr,
Dwarf_Die& result)
{
result = scope;
if (!die_has_pc(scope, addr))
return false;
Dwarf_Die child, import;
int rc = dwarf_child(&result, &child);
while (rc == 0)
{
switch (dwarf_tag (&child))
{
case DW_TAG_imported_unit:
if (dwarf_attr_die(&child, DW_AT_import, &import))
{
Dwarf_Die import_result;
if (inner_die_containing_pc(import, addr, import_result))
{
result = import_result;
return true;
}
}
break;
case DW_TAG_lexical_block:
case DW_TAG_with_stmt:
case DW_TAG_catch_block:
case DW_TAG_try_block:
case DW_TAG_entry_point:
if (die_has_pc(child, addr))
{
result = child;
rc = dwarf_child(&result, &child);
continue;
}
}
rc = dwarf_siblingof(&child, &child);
}
return true;
}
void
dwflpp::loc2c_error (void *, const char *fmt, ...)
{
const char *msg = "?";
char *tmp = NULL;
int rc;
va_list ap;
va_start (ap, fmt);
rc = vasprintf (& tmp, fmt, ap);
if (rc < 0)
msg = "?";
else
msg = tmp;
va_end (ap);
throw SEMANTIC_ERROR (msg);
}
void
dwflpp::emit_address (struct obstack *pool, Dwarf_Addr address)
{
int n = dwfl_module_relocations (module);
DWFL_ASSERT ("dwfl_module_relocations", n >= 0);
Dwarf_Addr reloc_address = address;
const char *secname = "";
if (n > 1)
{
int i = dwfl_module_relocate_address (module, &reloc_address);
DWFL_ASSERT ("dwfl_module_relocate_address", i >= 0);
secname = dwfl_module_relocation_info (module, i, NULL);
}
if (sess.verbose > 2)
{
clog << _F("emit dwarf addr %#" PRIx64 " => module %s section %s relocaddr %#" PRIx64,
address, module_name.c_str (), (secname ?: "null"),
reloc_address) << endl;
}
if (n > 0 && !(n == 1 && secname == NULL))
{
DWFL_ASSERT ("dwfl_module_relocation_info", secname);
if (n > 1 || secname[0] != '\0')
{
obstack_printf (pool, "({ unsigned long addr = 0; ");
obstack_printf (pool, "addr = _stp_kmodule_relocate (\"%s\",\"%s\",%#" PRIx64 "); ",
module_name.c_str(), secname, reloc_address);
obstack_printf (pool, "addr; })");
}
else if (n == 1 && module_name == TOK_KERNEL && secname[0] == '\0')
{
address -= sess.sym_stext;
secname = "_stext";
obstack_printf (pool, "({ static unsigned long addr = 0; ");
obstack_printf (pool, "if (addr==0) addr = _stp_kmodule_relocate (\"%s\",\"%s\",%#" PRIx64 "); ",
module_name.c_str(), secname, address); obstack_printf (pool, "addr; })");
}
else
{
obstack_printf (pool, "/* pragma:vma */");
obstack_printf (pool, "({ unsigned long addr = 0; ");
obstack_printf (pool, "addr = _stp_umodule_relocate (\"%s\",%#" PRIx64 ", current); ",
resolve_path(module_name.c_str()).c_str(), address);
obstack_printf (pool, "addr; })");
}
}
else
obstack_printf (pool, "%#" PRIx64 "UL", address); }
void
dwflpp::loc2c_emit_address (void *arg, struct obstack *pool,
Dwarf_Addr address)
{
static_cast<dwflpp *>(arg)->emit_address (pool, address);
}
void
dwflpp::get_locals(vector<Dwarf_Die>& scopes, set<string>& locals)
{
XXX
get_locals_die(scopes[0], locals);
}
void
dwflpp::get_locals_die(Dwarf_Die& die, set<string>& locals)
{
Dwarf_Die child, import;
if (dwarf_child (&die, &child) == 0)
{
do
{
const char *name;
switch (dwarf_tag (&child))
{
case DW_TAG_variable:
case DW_TAG_formal_parameter:
name = dwarf_diename (&child);
if (name)
locals.insert(string("$") + name);
break;
case DW_TAG_imported_unit:
if (dwarf_attr_die(&child, DW_AT_import, &import))
get_locals_die (import, locals);
break;
default:
break;
}
}
while (dwarf_siblingof (&child, &child) == 0);
}
}
Dwarf_Attribute *
dwflpp::find_variable_and_frame_base (vector<Dwarf_Die>& scopes,
Dwarf_Addr pc,
string const & local,
const target_symbol *e,
Dwarf_Die *vardie,
Dwarf_Attribute *fb_attr_mem)
{
Dwarf_Die *scope_die = &scopes[0];
Dwarf_Attribute *fb_attr = NULL;
assert (cu);
int declaring_scope = dwarf_getscopevar (&scopes[0], scopes.size(),
local.c_str(),
0, NULL, 0, 0,
vardie);
if (declaring_scope < 0)
{
set<string> locals;
get_locals(scopes, locals);
string sugs = levenshtein_suggest(local, locals); if (pc)
throw SEMANTIC_ERROR (_F("unable to find local '%s', [man error::dwarf] dieoffset %s in %s, near pc %s %s %s %s (%s)",
local.c_str(),
lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(),
module_name.c_str(),
lex_cast_hex(pc).c_str(),
(scope_die == NULL) ? "" : _("in"),
(dwarf_diename(scope_die) ?: "<unknown>"),
(dwarf_diename(cu) ?: "<unknown>"),
(sugs.empty()
? (_("<no alternatives>"))
: (_("alternatives: ") + sugs + ")")).c_str()),
e->tok);
else
throw SEMANTIC_ERROR (_F("unable to find global '%s', [man error::dwarf] dieoffset %s in %s, %s %s %s (%s)",
local.c_str(),
lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(),
module_name.c_str(),
(scope_die == NULL) ? "" : _("in"),
(dwarf_diename(scope_die) ?: "<unknown>"),
cu_name().c_str(),
(sugs.empty()
? (_("<no alternatives>"))
: (_("alternatives: ") + sugs + ")")).c_str()),
e->tok);
}
Dwarf_Attribute attr_mem;
if (dwarf_attr_integrate (vardie, DW_AT_const_value, &attr_mem) == NULL
&& dwarf_attr_integrate (vardie, DW_AT_location, &attr_mem) == NULL
&& dwarf_attr_integrate (vardie, DW_AT_external, &attr_mem) != NULL
&& dwarf_tag(&scopes[declaring_scope]) == DW_TAG_compile_unit)
{
Dwarf_Die orig_vardie = *vardie;
bool alt_found = false;
if (dwarf_child(&scopes[declaring_scope], vardie) == 0)
do
{
if (dwarf_tag (vardie) == DW_TAG_variable
&& strcmp (dwarf_diename (vardie), local.c_str ()) == 0
&& (dwarf_attr_integrate (vardie, DW_AT_external, &attr_mem)
!= NULL)
&& ((dwarf_attr_integrate (vardie, DW_AT_const_value, &attr_mem)
!= NULL)
|| (dwarf_attr_integrate (vardie, DW_AT_location, &attr_mem)
!= NULL)))
alt_found = true;
}
while (!alt_found && dwarf_siblingof(vardie, vardie) == 0);
if (! alt_found)
*vardie = orig_vardie;
}
if (pc == 0)
return NULL;
vector<Dwarf_Die> physcopes, *fbscopes = &scopes;
for (size_t inner = declaring_scope;
inner < fbscopes->size() && fb_attr == NULL;
++inner)
{
Dwarf_Die& scope = (*fbscopes)[inner];
switch (dwarf_tag (&scope))
{
default:
continue;
case DW_TAG_subprogram:
case DW_TAG_entry_point:
fb_attr = dwarf_attr_integrate (&scope,
DW_AT_frame_base,
fb_attr_mem);
break;
case DW_TAG_inlined_subroutine:
if (declaring_scope != -1)
{
physcopes = getscopes_die(&scope);
if (physcopes.empty())
throw SEMANTIC_ERROR (_F("unable to get die scopes for '%s' in an inlined subroutine",
local.c_str()), e->tok);
fbscopes = &physcopes;
inner = 0; declaring_scope = -1;
}
break;
}
}
return fb_attr;
}
static string
suggested_locations_string(Dwarf_Attribute *attr)
{
string locsstr;
if (attr == NULL)
locsstr = "<no alternatives for NULL attribute>";
else
{
#if _ELFUTILS_PREREQ (0, 158)
Dwarf_Op *expr;
size_t exprlen;
Dwarf_Addr base, start, end;
ptrdiff_t off = 0;
off = dwarf_getlocations (attr, off, &base,
&start, &end,
&expr, &exprlen);
if (off > 0)
{
locsstr = _("alternative locations: ");
while (off > 0)
{
locsstr += "[";
locsstr += lex_cast_hex(start);
locsstr += ",";
locsstr += lex_cast_hex(end);
locsstr += "]";
off = dwarf_getlocations (attr, off, &base,
&start, &end,
&expr, &exprlen);
if (off > 0)
locsstr += ", ";
}
}
else if (off == 0)
locsstr = _("<no alternative locations>");
else
locsstr = _F("<error getting alternative locations: %s>",
dwarf_errmsg(-1));
#else
locsstr = "<cannot suggest any alternative locations, elfutils too old>";
#endif }
return locsstr;
}
static string
die_name_string (Dwarf_Die *die)
{
string res;
const char *name = dwarf_linkage_name(die);
if (name == NULL)
name = dwarf_diename (die);
size_t demangle_buffer_len = 0;
char *demangle_buffer = NULL;
if (name != NULL && name[0] == '_' && name[1] == 'Z')
{
int status = -1;
char *dsymname = abi::__cxa_demangle (name, demangle_buffer,
&demangle_buffer_len, &status);
if (status == 0)
name = demangle_buffer = dsymname;
}
if (name != NULL)
res = name;
else
res = _("<unknown");
free (demangle_buffer);
return res;
}
string
dwflpp::pc_die_line_string (Dwarf_Addr pc, Dwarf_Die *die)
{
string linestr;
int lineno, col;
const char *src = NULL;
lineno = col = -1;
if (die == NULL)
{
Dwarf_Line *line = dwarf_getsrc_die (cu, pc);
if (line != NULL)
{
src = dwarf_linesrc (line, NULL, NULL);
dwarf_lineno (line, &lineno);
dwarf_linecol (line, &col);
}
}
else
{
Dwarf_Files *files;
if (dwarf_getsrcfiles (cu, &files, NULL) == 0)
{
Dwarf_Attribute attr;
Dwarf_Word val;
if (dwarf_formudata (dwarf_attr (die, DW_AT_call_file, &attr),
&val) == 0)
{
src = dwarf_filesrc (files, val, NULL, NULL);
if (dwarf_formudata (dwarf_attr (die, DW_AT_call_line,
&attr), &val) == 0)
{
lineno = val;
if (dwarf_formudata (dwarf_attr (die, DW_AT_call_column,
&attr), &val) == 0)
col = val;
}
}
}
}
if (src != NULL)
{
linestr += src;
if (lineno > 0)
{
linestr += ":" + lex_cast(lineno);
if (col > 0)
linestr += ":" + lex_cast(col);
}
}
else
linestr += _("unknown source");
return linestr;
}
string
dwflpp::die_location_as_string(Dwarf_Addr pc, Dwarf_Die *die)
{
string locstr;
locstr += _("dieoffset: ");
locstr += lex_cast_hex(dwarf_dieoffset(die));
const char *debugfile;
locstr += _(" from ");
if (dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, NULL,
&debugfile) == NULL || debugfile == NULL)
{
locstr += _("unknown debug file for ");
locstr += module_name;
}
else
locstr += debugfile;
return locstr;
}
string
dwflpp::die_location_as_function_string(Dwarf_Addr pc, Dwarf_Die *die)
{
string locstr;
locstr = _("function: ");
Dwarf_Die funcdie_mem;
Dwarf_Die *funcdie = NULL;
string funcname = "";
Dwarf_Die *scopes = NULL;
int nscopes = dwarf_getscopes (cu, pc, &scopes);
for (int i = 0; funcname == "" && i < nscopes; i++)
{
Dwarf_Die *scope = &scopes[i];
int tag = dwarf_tag (scope);
if (tag == DW_TAG_subprogram
|| tag == DW_TAG_inlined_subroutine
|| tag == DW_TAG_entry_point)
funcname = die_name_string (scope);
if (funcname != "")
{
funcdie_mem = *scope;
funcdie = &funcdie_mem;
}
}
free (scopes);
if (funcname == "")
locstr += _("<unknown> at ") + pc_die_line_string (pc, NULL);
else
{
int nscopes = dwarf_getscopes_die (funcdie, &scopes);
if (nscopes > 0)
{
locstr += funcname;
locstr += _(" at ");
locstr += pc_die_line_string (pc, NULL);
Dwarf_Die *last_scope = &scopes[0];
for (int i = 1; i < nscopes; i++)
{
Dwarf_Die *scope = &scopes[i];
int tag = dwarf_tag (scope);
if (tag != DW_TAG_inlined_subroutine
&& tag != DW_TAG_entry_point
&& tag != DW_TAG_subprogram)
continue;
locstr += _(" inlined by ");
locstr += die_name_string (scope);
locstr += _(" at ");
locstr += pc_die_line_string (pc, last_scope);
if (tag == DW_TAG_subprogram)
break;
last_scope = scope;
}
}
else
{
locstr += funcname;
locstr += _(" at ");
locstr += pc_die_line_string (pc, NULL);
}
free (scopes);
}
return locstr;
}
struct location *
dwflpp::translate_location(struct obstack *pool,
Dwarf_Attribute *attr, Dwarf_Die *die,
Dwarf_Addr pc,
Dwarf_Attribute *fb_attr,
struct location **tail,
const target_symbol *e)
{
if (dwarf_whatattr (attr) == DW_AT_const_value)
{
*tail = c_translate_constant (pool, &loc2c_error, this,
&loc2c_emit_address, 0, pc, attr);
return *tail;
}
Dwarf_Op *expr;
size_t len;
retry:
switch (dwarf_getlocation_addr (attr, pc , &expr, &len, 1))
{
case 1: if (len > 0)
break;
case 0: {
Dwarf_Addr pc2 = pr15123_retry_addr (pc, die);
if (pc2 != 0) {
pc = pc2;
goto retry;
}
}
{
string msg = _F("not accessible at this address (pc: %s) [man error::dwarf]", lex_cast_hex(pc).c_str());
semantic_error err(ERR_SRC, msg, e->tok);
err.details.push_back(die_location_as_string(pc, die));
err.details.push_back(die_location_as_function_string(pc, die));
err.details.push_back(suggested_locations_string(attr));
throw err;
}
default: case -1:
{
string msg = _F("dwarf_getlocation_addr failed at this address (pc: %s) [man error::dwarf]", lex_cast_hex(pc).c_str());
semantic_error err(ERR_SRC, msg, e->tok);
string dwarf_err = _F("dwarf_error: %s", dwarf_errmsg(-1));
err.details.push_back(dwarf_err);
err.details.push_back(die_location_as_string(pc, die));
err.details.push_back(die_location_as_function_string(pc, die));
err.details.push_back(suggested_locations_string(attr));
throw err;
}
}
Dwarf_Op *cfa_ops;
if (pc)
{
Dwarf_Addr addr = pc + module_bias;
cfa_ops = get_cfa_ops (addr);
}
else
cfa_ops = NULL;
return c_translate_location (pool, &loc2c_error, this,
&loc2c_emit_address,
1, 0 ,
pc, attr, expr, len, tail, fb_attr, cfa_ops);
}
void
dwflpp::get_members(Dwarf_Die *typedie, set<string>& members, set<string> &dupes)
{
const int typetag = dwarf_tag (typedie);
if (typetag != DW_TAG_structure_type &&
typetag != DW_TAG_class_type &&
typetag != DW_TAG_union_type &&
typetag != DW_TAG_compile_unit &&
typetag != DW_TAG_partial_unit)
{
throw SEMANTIC_ERROR(_F("Type %s isn't a struct/class/union",
dwarf_type_name(typedie).c_str()));
}
Dwarf_Die die_mem, import;
Dwarf_Die *die = &die_mem;
switch (dwarf_child (typedie, die))
{
case 1: throw SEMANTIC_ERROR(_F("Type %s is empty", dwarf_type_name(typedie).c_str()));
case -1: default: throw SEMANTIC_ERROR(_F("Type %s: %s", dwarf_type_name(typedie).c_str(),
dwarf_errmsg(-1)));
case 0: break;
}
do
{
int tag = dwarf_tag(die);
if (tag == DW_TAG_imported_unit
&& dwarf_attr_die(die, DW_AT_import, &import))
get_members(&import, members, dupes);
if (tag != DW_TAG_member && tag != DW_TAG_inheritance)
continue;
const char *member = dwarf_diename (die) ;
if ( tag == DW_TAG_member && member != NULL )
{
if (dupes.insert(member).second)
members.insert(member);
}
else
{
Dwarf_Die temp_die;
if (!dwarf_attr_die (die, DW_AT_type, &temp_die))
{
string source = dwarf_decl_file(die) ?: "<unknown source>";
int line = -1;
dwarf_decl_line(die, &line);
throw SEMANTIC_ERROR(_F("Couldn't obtain type attribute for anonymous "
"member at %s:%d", source.c_str(), line));
}
get_members(&temp_die, members, dupes);
}
}
while (dwarf_siblingof (die, die) == 0);
}
bool
dwflpp::find_struct_member(const target_symbol::component& c,
Dwarf_Die *parentdie,
Dwarf_Die *memberdie,
vector<Dwarf_Die>& dies,
vector<Dwarf_Attribute>& locs)
{
Dwarf_Attribute attr;
Dwarf_Die die;
deque<Dwarf_Die> inheritees(1, *parentdie);
for (; !inheritees.empty(); inheritees.pop_front())
{
switch (dwarf_child (&inheritees.front(), &die))
{
case 0: break;
case 1: continue;
case -1: default: throw SEMANTIC_ERROR (dwarf_type_name(&inheritees.front()) + ": "
+ string (dwarf_errmsg (-1)),
c.tok);
}
do
{
int tag = dwarf_tag(&die);
Dwarf_Die import;
if (tag == DW_TAG_imported_unit
&& dwarf_attr_die(&die, DW_AT_import, &import)
&& find_struct_member(c, &import, memberdie, dies, locs))
goto success;
if (tag != DW_TAG_member && tag != DW_TAG_inheritance)
continue;
const char *name = dwarf_diename(&die);
if (tag == DW_TAG_inheritance)
{
Dwarf_Die inheritee;
if (dwarf_attr_die (&die, DW_AT_type, &inheritee))
inheritees.push_back(inheritee);
}
else if (name == NULL)
{
Dwarf_Die subdie;
if (dwarf_attr_die (&die, DW_AT_type, &subdie) &&
find_struct_member(c, &subdie, memberdie, dies, locs))
goto success;
}
else if (name == c.member)
{
*memberdie = die;
goto success;
}
}
while (dwarf_siblingof (&die, &die) == 0);
}
return false;
success:
if (dwarf_attr_integrate (&die, DW_AT_data_member_location, &attr))
{
dies.insert(dies.begin(), die);
locs.insert(locs.begin(), attr);
}
else if (dwarf_tag(parentdie) != DW_TAG_union_type)
throw SEMANTIC_ERROR (_F("no location for field '%s':%s",
c.member.c_str(), dwarf_errmsg(-1)), c.tok);
return true;
}
static inline void
dwarf_die_type (Dwarf_Die *die, Dwarf_Die *typedie_mem, const token *tok=NULL)
{
if (!dwarf_attr_die (die, DW_AT_type, typedie_mem))
throw SEMANTIC_ERROR (_F("cannot get type of field: %s", dwarf_errmsg(-1)), tok);
}
void
dwflpp::translate_components(struct obstack *pool,
struct location **tail,
Dwarf_Addr pc,
const target_symbol *e,
Dwarf_Die *vardie,
Dwarf_Die *typedie,
unsigned first)
{
unsigned i = first;
while (i < e->components.size())
{
const target_symbol::component& c = e->components[i];
XXX#if 0
#endif
switch (dwarf_tag (typedie))
{
case DW_TAG_typedef:
case DW_TAG_const_type:
case DW_TAG_volatile_type:
case DW_TAG_restrict_type:
dwarf_die_type (typedie, typedie, c.tok);
break;
case DW_TAG_reference_type:
case DW_TAG_rvalue_reference_type:
if (pool)
c_translate_pointer (pool, 1, 0 , typedie, tail);
dwarf_die_type (typedie, typedie, c.tok);
break;
case DW_TAG_pointer_type:
if (!dwarf_hasattr_integrate (typedie, DW_AT_type))
throw SEMANTIC_ERROR (_F("invalid access '%s' vs '%s'", lex_cast(c).c_str(),
dwarf_type_name(typedie).c_str()), c.tok);
if (pool)
c_translate_pointer (pool, 1, 0 , typedie, tail);
if (c.type != target_symbol::comp_literal_array_index &&
c.type != target_symbol::comp_expression_array_index)
{
dwarf_die_type (typedie, typedie, c.tok);
break;
}
case DW_TAG_array_type:
if (c.type == target_symbol::comp_literal_array_index)
{
if (pool)
c_translate_array (pool, 1, 0 , typedie, tail,
NULL, c.num_index);
}
else if (c.type == target_symbol::comp_expression_array_index)
{
string index = "STAP_ARG_index" + lex_cast(i);
if (pool)
c_translate_array (pool, 1, 0 , typedie, tail,
index.c_str(), 0);
}
else
throw SEMANTIC_ERROR (_F("invalid access '%s' for array type",
lex_cast(c).c_str()), c.tok);
dwarf_die_type (typedie, typedie, c.tok);
*vardie = *typedie;
++i;
break;
case DW_TAG_structure_type:
case DW_TAG_union_type:
case DW_TAG_class_type:
if (c.type != target_symbol::comp_struct_member)
throw SEMANTIC_ERROR (_F("invalid access '%s' for %s",
lex_cast(c).c_str(), dwarf_type_name(typedie).c_str()));
if (dwarf_hasattr(typedie, DW_AT_declaration))
{
Dwarf_Die *tmpdie = declaration_resolve(typedie);
if (tmpdie == NULL)
throw SEMANTIC_ERROR (_F("unresolved %s", dwarf_type_name(typedie).c_str()), c.tok);
*typedie = *tmpdie;
}
{
vector<Dwarf_Die> dies;
vector<Dwarf_Attribute> locs;
if (!find_struct_member(c, typedie, vardie, dies, locs))
{
string source;
if (!dwarf_hasattr_integrate(typedie, DW_AT_name))
{
int line;
const char *file = dwarf_decl_file(typedie);
if (file && dwarf_decl_line(typedie, &line) == 0)
source = " (" + string(file) + ":"
+ lex_cast(line) + ")";
}
set<string> members, member_dupes;
get_members(typedie, members, member_dupes);
string sugs = levenshtein_suggest(c.member, members);
if (!sugs.empty())
sugs = " (alternatives: " + sugs + ")";
throw SEMANTIC_ERROR(_F("unable to find member '%s' for %s%s%s", c.member.c_str(),
dwarf_type_name(typedie).c_str(), source.c_str(),
sugs.c_str()), c.tok);
}
for (unsigned j = 0; j < locs.size(); ++j)
if (pool)
translate_location (pool, &locs[j], &dies[j],
pc, NULL, tail, e);
}
dwarf_die_type (vardie, typedie, c.tok);
++i;
break;
case DW_TAG_enumeration_type:
case DW_TAG_base_type:
throw SEMANTIC_ERROR (_F("invalid access '%s' vs. %s", lex_cast(c).c_str(),
dwarf_type_name(typedie).c_str()), c.tok);
break;
case -1:
throw SEMANTIC_ERROR (_F("cannot find type: %s", dwarf_errmsg(-1)), c.tok);
break;
default:
throw SEMANTIC_ERROR (_F("%s: unexpected type tag %s", dwarf_type_name(typedie).c_str(),
lex_cast(dwarf_tag(typedie)).c_str()), c.tok);
break;
}
}
}
void
dwflpp::resolve_unqualified_inner_typedie (Dwarf_Die *typedie,
Dwarf_Die *innerdie,
const target_symbol *e)
{
int typetag = dwarf_tag (typedie);
*innerdie = *typedie;
while (typetag == DW_TAG_typedef ||
typetag == DW_TAG_const_type ||
typetag == DW_TAG_volatile_type ||
typetag == DW_TAG_restrict_type)
{
if (!dwarf_attr_die (innerdie, DW_AT_type, innerdie))
throw SEMANTIC_ERROR (_F("cannot get type of pointee: %s", dwarf_errmsg(-1)), e->tok);
typetag = dwarf_tag (innerdie);
}
}
void
dwflpp::translate_final_fetch_or_store (struct obstack *pool,
struct location **tail,
Dwarf_Addr ,
Dwarf_Die *vardie,
Dwarf_Die *start_typedie,
bool lvalue,
const target_symbol *e,
string &,
string & postlude,
Dwarf_Die *typedie)
{
resolve_unqualified_inner_typedie (start_typedie, typedie, e);
if (e->addressof)
{
if (lvalue)
throw SEMANTIC_ERROR (_("cannot write to member address"), e->tok);
if (dwarf_hasattr_integrate (vardie, DW_AT_bit_offset))
throw SEMANTIC_ERROR (_("cannot take address of bit-field"), e->tok);
c_translate_addressof (pool, 1, 0, vardie, typedie, tail, "STAP_RETVALUE");
return;
}
int typetag = dwarf_tag (typedie);
switch (typetag)
{
default:
throw SEMANTIC_ERROR (_F("unsupported type tag %s for %s", lex_cast(typetag).c_str(),
dwarf_type_name(typedie).c_str()), e->tok);
break;
case DW_TAG_structure_type:
case DW_TAG_class_type:
case DW_TAG_union_type:
throw SEMANTIC_ERROR (_F("'%s' is being accessed instead of a member",
dwarf_type_name(typedie).c_str()), e->tok);
break;
case DW_TAG_base_type:
{
Dwarf_Attribute encoding_attr;
Dwarf_Word encoding = (Dwarf_Word) -1;
dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding, &encoding_attr),
& encoding);
if (encoding == (Dwarf_Word) -1)
{
throw SEMANTIC_ERROR (_F("unsupported type (mystery encoding %s for %s", lex_cast(encoding).c_str(),
dwarf_type_name(typedie).c_str()), e->tok);
}
if (encoding == DW_ATE_float
|| encoding == DW_ATE_complex_float
XXX)
{
throw SEMANTIC_ERROR (_F("unsupported type (encoding %s) for %s", lex_cast(encoding).c_str(),
dwarf_type_name(typedie).c_str()), e->tok);
}
}
case DW_TAG_enumeration_type:
if (lvalue)
c_translate_store (pool, 1, 0 , vardie, typedie, tail,
"STAP_ARG_value");
else
c_translate_fetch (pool, 1, 0 , vardie, typedie, tail,
"STAP_RETVALUE");
break;
case DW_TAG_array_type:
case DW_TAG_pointer_type:
case DW_TAG_reference_type:
case DW_TAG_rvalue_reference_type:
if (lvalue)
{
if (typetag == DW_TAG_array_type)
throw SEMANTIC_ERROR (_("cannot write to array address"), e->tok);
if (typetag == DW_TAG_reference_type ||
typetag == DW_TAG_rvalue_reference_type)
throw SEMANTIC_ERROR (_("cannot write to reference"), e->tok);
assert (typetag == DW_TAG_pointer_type);
c_translate_pointer_store (pool, 1, 0 , typedie, tail,
"STAP_ARG_value");
}
else
{
if (typetag == DW_TAG_array_type)
c_translate_array (pool, 1, 0 , typedie, tail, NULL, 0);
else
c_translate_pointer (pool, 1, 0 , typedie, tail);
c_translate_addressof (pool, 1, 0 , NULL, NULL, tail,
"STAP_RETVALUE");
}
break;
}
if (lvalue)
postlude += " STAP_RETVALUE = STAP_ARG_value;\n";
}
string
dwflpp::express_as_string (string prelude,
string postlude,
struct location *head)
{
size_t bufsz = 0;
char *buf = 0; FILE *memstream = open_memstream (&buf, &bufsz);
assert(memstream);
fprintf(memstream, "{\n");
fprintf(memstream, "%s", prelude.c_str());
unsigned int stack_depth;
bool deref = c_emit_location (memstream, head, 1, &stack_depth);
if (stack_depth > 32)
throw SEMANTIC_ERROR("oversized DWARF stack");
fprintf(memstream, "%s", postlude.c_str());
fprintf(memstream, " goto out;\n");
fprintf(memstream, "if (0) goto deref_fault;\n");
XXX (void) deref;
fprintf(memstream,
"deref_fault:\n"
" goto out;\n");
fprintf(memstream, "}\n");
fclose (memstream);
string result(buf);
free (buf);
return result;
}
Dwarf_Addr
dwflpp::vardie_from_symtable (Dwarf_Die *vardie, Dwarf_Addr *addr)
{
const char *name = dwarf_linkage_name (vardie) ?: dwarf_diename (vardie);
if (sess.verbose > 2)
clog << _F("finding symtable address for %s\n", name);
*addr = 0;
int syms = dwfl_module_getsymtab (module);
DWFL_ASSERT (_("Getting symbols"), syms >= 0);
for (int i = 0; *addr == 0 && i < syms; i++)
{
GElf_Sym sym;
GElf_Word shndxp;
const char *symname = dwfl_module_getsym(module, i, &sym, &shndxp);
if (symname
&& ! strcmp (name, symname)
&& sym.st_shndx != SHN_UNDEF
&& (GELF_ST_TYPE (sym.st_info) == STT_NOTYPE || GELF_ST_TYPE (sym.st_info) == STT_OBJECT))
*addr = sym.st_value;
}
if (dwfl_module_relocations (module) == 1 && module_name != TOK_KERNEL)
dwfl_module_relocate_address (module, addr);
if (sess.verbose > 2)
clog << _F("found %s @%#" PRIx64 "\n", name, *addr);
return *addr;
}
string
dwflpp::literal_stmt_for_local (vector<Dwarf_Die>& scopes,
Dwarf_Addr pc,
string const & local,
const target_symbol *e,
bool lvalue,
Dwarf_Die *die_mem)
{
Dwarf_Die vardie;
Dwarf_Attribute fb_attr_mem, *fb_attr = NULL;
fb_attr = find_variable_and_frame_base (scopes, pc, local, e,
&vardie, &fb_attr_mem);
if (sess.verbose>2)
{
if (pc)
clog << _F("finding location for local '%s' near address %#" PRIx64
", module bias %#" PRIx64 "\n", local.c_str(), pc,
module_bias);
else
clog << _F("finding location for global '%s' in CU '%s'\n",
local.c_str(), cu_name().c_str());
}
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
struct obstack pool;
obstack_init (&pool);
struct location *tail = NULL;
struct location *head;
Dwarf_Attribute attr_mem;
if (dwarf_attr_integrate (&vardie, DW_AT_const_value, &attr_mem) == NULL
&& dwarf_attr_integrate (&vardie, DW_AT_location, &attr_mem) == NULL)
{
Dwarf_Op addr_loc;
memset(&addr_loc, 0, sizeof(Dwarf_Op));
addr_loc.atom = DW_OP_addr;
if (dwarf_attr_integrate (&vardie, DW_AT_external, &attr_mem) != NULL
&& vardie_from_symtable (&vardie, &addr_loc.number) != 0)
{
head = c_translate_location (&pool, &loc2c_error, this,
&loc2c_emit_address,
1, 0, pc,
NULL, &addr_loc, 1, &tail, NULL, NULL);
}
else
{
string msg = _F("failed to retrieve location attribute for '%s' [man error::dwarf]", local.c_str());
semantic_error err(ERR_SRC, msg, e->tok);
err.details.push_back(die_location_as_string(pc, &vardie));
err.details.push_back(die_location_as_function_string(pc, &vardie));
throw err;
}
}
else
head = translate_location (&pool, &attr_mem, &vardie, pc, fb_attr, &tail, e);
Dwarf_Die typedie;
if (dwarf_attr_die (&vardie, DW_AT_type, &typedie) == NULL)
{
string msg = _F("failed to retrieve type attribute for '%s' [man error::dwarf]", local.c_str());
semantic_error err(ERR_SRC, msg, e->tok);
err.details.push_back(die_location_as_string(pc, &vardie));
err.details.push_back(die_location_as_function_string(pc, &vardie));
throw err;
}
translate_components (&pool, &tail, pc, e, &vardie, &typedie);
string prelude, postlude;
translate_final_fetch_or_store (&pool, &tail, module_bias,
&vardie, &typedie, lvalue, e,
prelude, postlude, die_mem);
string result = express_as_string(prelude, postlude, head);
obstack_free (&pool, 0);
return result;
}
Dwarf_Die*
dwflpp::type_die_for_local (vector<Dwarf_Die>& scopes,
Dwarf_Addr pc,
string const & local,
const target_symbol *e,
Dwarf_Die *typedie)
{
Dwarf_Die vardie;
Dwarf_Attribute attr_mem;
find_variable_and_frame_base (scopes, pc, local, e, &vardie, &attr_mem);
if (dwarf_attr_die (&vardie, DW_AT_type, typedie) == NULL)
throw SEMANTIC_ERROR(_F("failed to retrieve type attribute for '%s' [man error::dwarf]", local.c_str()), e->tok);
translate_components (NULL, NULL, pc, e, &vardie, typedie);
return typedie;
}
string
dwflpp::literal_stmt_for_return (Dwarf_Die *scope_die,
Dwarf_Addr pc,
const target_symbol *e,
bool lvalue,
Dwarf_Die *die_mem)
{
if (sess.verbose>2)
clog << _F("literal_stmt_for_return: finding return value for %s (%s)\n",
(dwarf_diename(scope_die) ?: "<unknown>"), (dwarf_diename(cu) ?: "<unknown>"));
struct obstack pool;
obstack_init (&pool);
struct location *tail = NULL;
const Dwarf_Op *locops;
int nlocops = dwfl_module_return_value_location (module, scope_die,
&locops);
if (nlocops < 0)
{
throw SEMANTIC_ERROR(_F("failed to retrieve return value location for %s [man error::dwarf] (%s)",
(dwarf_diename(scope_die) ?: "<unknown>"),
(dwarf_diename(cu) ?: "<unknown>")), e->tok);
}
else if (nlocops == 0)
{
throw SEMANTIC_ERROR(_F("function %s (%s) has no return value",
(dwarf_diename(scope_die) ?: "<unknown>"),
(dwarf_diename(cu) ?: "<unknown>")), e->tok);
}
struct location *head = c_translate_location (&pool, &loc2c_error, this,
&loc2c_emit_address,
1, 0 ,
pc, NULL, locops, nlocops,
&tail, NULL, NULL);
Dwarf_Die vardie = *scope_die, typedie;
if (dwarf_attr_die (&vardie, DW_AT_type, &typedie) == NULL)
throw SEMANTIC_ERROR(_F("failed to retrieve return value type attribute for %s [man error::dwarf] (%s)",
(dwarf_diename(&vardie) ?: "<unknown>"),
(dwarf_diename(cu) ?: "<unknown>")), e->tok);
translate_components (&pool, &tail, pc, e, &vardie, &typedie);
string prelude, postlude;
translate_final_fetch_or_store (&pool, &tail, module_bias,
&vardie, &typedie, lvalue, e,
prelude, postlude, die_mem);
string result = express_as_string(prelude, postlude, head);
obstack_free (&pool, 0);
return result;
}
Dwarf_Die*
dwflpp::type_die_for_return (Dwarf_Die *scope_die,
Dwarf_Addr pc,
const target_symbol *e,
Dwarf_Die *typedie)
{
Dwarf_Die vardie = *scope_die;
if (dwarf_attr_die (&vardie, DW_AT_type, typedie) == NULL)
throw SEMANTIC_ERROR(_F("failed to retrieve return value type attribute for %s [man error::dwarf] (%s)",
(dwarf_diename(&vardie) ?: "<unknown>"),
(dwarf_diename(cu) ?: "<unknown>")), e->tok);
translate_components (NULL, NULL, pc, e, &vardie, typedie);
return typedie;
}
string
dwflpp::literal_stmt_for_pointer (Dwarf_Die *start_typedie,
const target_symbol *e,
bool lvalue,
Dwarf_Die *die_mem)
{
if (sess.verbose>2)
clog << _F("literal_stmt_for_pointer: finding value for %s (%s)\n",
dwarf_type_name(start_typedie).c_str(), (dwarf_diename(cu) ?: "<unknown>"));
struct obstack pool;
obstack_init (&pool);
struct location *head = c_translate_argument (&pool, &loc2c_error, this,
&loc2c_emit_address,
1, "STAP_ARG_pointer");
struct location *tail = head;
unsigned first = 0;
Dwarf_Die typedie = *start_typedie, vardie = typedie;
const target_symbol::component* c =
e->components.empty() ? NULL : &e->components[0];
if (c && (c->type == target_symbol::comp_literal_array_index ||
c->type == target_symbol::comp_expression_array_index))
{
resolve_unqualified_inner_typedie (&typedie, &typedie, e);
int typetag = dwarf_tag (&typedie);
if (typetag != DW_TAG_pointer_type &&
typetag != DW_TAG_array_type)
{
if (c->type == target_symbol::comp_literal_array_index)
c_translate_array_pointer (&pool, 1, &typedie, &tail, NULL, c->num_index);
else
c_translate_array_pointer (&pool, 1, &typedie, &tail, "STAP_ARG_index0", 0);
++first;
}
}
translate_components (&pool, &tail, 0, e, &vardie, &typedie, first);
string prelude, postlude;
translate_final_fetch_or_store (&pool, &tail, module_bias,
&vardie, &typedie, lvalue, e,
prelude, postlude, die_mem);
string result = express_as_string(prelude, postlude, head);
obstack_free (&pool, 0);
return result;
}
Dwarf_Die*
dwflpp::type_die_for_pointer (Dwarf_Die *start_typedie,
const target_symbol *e,
Dwarf_Die *typedie)
{
unsigned first = 0;
*typedie = *start_typedie;
Dwarf_Die vardie = *typedie;
const target_symbol::component* c =
e->components.empty() ? NULL : &e->components[0];
if (c && (c->type == target_symbol::comp_literal_array_index ||
c->type == target_symbol::comp_expression_array_index))
{
resolve_unqualified_inner_typedie (typedie, typedie, e);
int typetag = dwarf_tag (typedie);
if (typetag != DW_TAG_pointer_type &&
typetag != DW_TAG_array_type)
++first;
}
translate_components (NULL, NULL, 0, e, &vardie, typedie, first);
return typedie;
}
static bool
in_kprobes_function(systemtap_session& sess, Dwarf_Addr addr)
{
if (sess.sym_kprobes_text_start != 0 && sess.sym_kprobes_text_end != 0)
{
if (addr >= sess.sym_kprobes_text_start && addr < sess.sym_kprobes_text_end)
return true;
}
return false;
}
enum dwflpp::blacklisted_type
dwflpp::blacklisted_p(const string& funcname,
const string& filename,
int,
const string& module,
Dwarf_Addr addr,
bool has_return)
{
if (!blacklist_enabled)
return dwflpp::blacklisted_none;
enum dwflpp::blacklisted_type blacklisted = dwflpp::blacklisted_none;
string section = get_blacklist_section(addr);
if (module == TOK_KERNEL && !regexec (&blacklist_section, section.c_str(), 0, NULL, 0))
blacklisted = dwflpp::blacklisted_section;
else if (module == TOK_KERNEL && in_kprobes_function(sess, addr))
blacklisted = dwflpp::blacklisted_kprobes;
else if (!regexec(&blacklist_func, funcname.c_str(), 0, NULL, 0))
blacklisted = dwflpp::blacklisted_function;
else if (has_return && !regexec(&blacklist_func_ret, funcname.c_str(), 0, NULL, 0))
blacklisted = dwflpp::blacklisted_function_return;
else if (!regexec(&blacklist_file, filename.c_str(), 0, NULL, 0))
blacklisted = dwflpp::blacklisted_file;
if (blacklisted)
{
if (sess.verbose>1)
clog << _(" - blacklisted");
if (sess.guru_mode)
{
blacklisted = dwflpp::blacklisted_none;
if (sess.verbose>1)
clog << _(" but not skipped (guru mode enabled)");
}
}
return blacklisted;
}
void
dwflpp::build_kernel_blacklist()
{
string blfn = "^(";
string blfn_ret = "^(";
string blfile = "^(";
string blsection = "^(";
blsection += "\\.init\\."; blsection += "|\\.exit\\.";
blsection += "|\\.devinit\\.";
blsection += "|\\.devexit\\.";
blsection += "|\\.cpuinit\\.";
blsection += "|\\.cpuexit\\.";
blsection += "|\\.meminit\\.";
blsection += "|\\.memexit\\.";
blfile += "kernel/kprobes\\.c"; blfile += "|arch/.*/kernel/kprobes\\.c";
blfile += "|.*/include/asm/io\\.h";
blfile += "|.*/include/asm/io_64\\.h";
blfile += "|.*/include/asm/bitops\\.h";
blfile += "|drivers/ide/ide-iops\\.c";
blfile += "|arch/.*/kernel/paravirt\\.c";
blfile += "|.*/include/asm/paravirt\\.h";
XXX
blfn += "atomic_notifier_call_chain"; blfn += "|default_do_nmi";
blfn += "|__die";
blfn += "|die_nmi";
blfn += "|do_debug";
blfn += "|do_general_protection";
blfn += "|do_int3";
blfn += "|do_IRQ";
blfn += "|do_page_fault";
blfn += "|do_sparc64_fault";
blfn += "|do_trap";
blfn += "|dummy_nmi_callback";
blfn += "|flush_icache_range";
blfn += "|ia64_bad_break";
blfn += "|ia64_do_page_fault";
blfn += "|ia64_fault";
blfn += "|io_check_error";
blfn += "|mem_parity_error";
blfn += "|nmi_watchdog_tick";
blfn += "|notifier_call_chain";
blfn += "|oops_begin";
blfn += "|oops_end";
blfn += "|program_check_exception";
blfn += "|single_step_exception";
blfn += "|sync_regs";
blfn += "|unhandled_fault";
blfn += "|unknown_nmi_error";
blfn += "|xen_[gs]et_debugreg";
blfn += "|xen_irq_.*";
blfn += "|xen_.*_fl_direct.*";
blfn += "|check_events";
blfn += "|xen_adjust_exception_frame";
blfn += "|xen_iret.*";
blfn += "|xen_sysret64.*";
blfn += "|test_ti_thread_flag.*";
blfn += "|inat_get_opcode_attribute";
blfn += "|system_call_after_swapgs";
blfn += "|HYPERVISOR_[gs]et_debugreg";
blfn += "|HYPERVISOR_event_channel_op";
blfn += "|hash_64";
blfn += "|hash_ptr";
blfn += "|native_set_pte";
blfn += "|.*raw_.*_lock.*";
blfn += "|.*raw_.*_unlock.*";
blfn += "|.*raw_.*_trylock.*";
blfn += "|.*read_lock.*";
blfn += "|.*read_unlock.*";
blfn += "|.*read_trylock.*";
blfn += "|.*write_lock.*";
blfn += "|.*write_unlock.*";
blfn += "|.*write_trylock.*";
blfn += "|.*write_seqlock.*";
blfn += "|.*write_sequnlock.*";
blfn += "|.*spin_lock.*";
blfn += "|.*spin_unlock.*";
blfn += "|.*spin_trylock.*";
blfn += "|.*spin_is_locked.*";
blfn += "|rwsem_.*lock.*";
blfn += "|.*mutex_.*lock.*";
blfn += "|raw_.*";
blfn += "|atomic_.*";
blfn += "|atomic64_.*";
blfn += "|get_bh";
blfn += "|put_bh";
blfn += "|.*apic.*|.*APIC.*";
blfn += "|.*softirq.*";
blfn += "|.*IRQ.*";
blfn += "|.*_intr.*";
blfn += "|__delay";
blfn += "|.*kernel_text.*";
blfn += "|get_current";
blfn += "|current_.*";
blfn += "|.*exception_tables.*";
blfn += "|.*setup_rt_frame.*";
blfn += "|.*preempt_count.*";
blfn += "|preempt_schedule";
blfn_ret += "do_exit"; blfn_ret += "|sys_exit";
blfn_ret += "|sys_exit_group";
if (sess.architecture == "x86_64")
blfn += "|__switch_to";
if (sess.architecture == "i686")
blfn_ret += "|__switch_to";
blfn += "|special_mapping_.*";
blfn += "|.*_pte_.*"; blfile += "|fs/seq_file\\.c";
blfn += ")$";
blfn_ret += ")$";
blfile += ")$";
blsection += ")";
if (sess.verbose > 2)
{
clog << _("blacklist regexps:") << endl;
clog << "blfn: " << blfn << endl;
clog << "blfn_ret: " << blfn_ret << endl;
clog << "blfile: " << blfile << endl;
clog << "blsection: " << blsection << endl;
}
int rc = regcomp (& blacklist_func, blfn.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_func regcomp failed"));
rc = regcomp (& blacklist_func_ret, blfn_ret.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_func_ret regcomp failed"));
rc = regcomp (& blacklist_file, blfile.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_file regcomp failed"));
rc = regcomp (& blacklist_section, blsection.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_section regcomp failed"));
blacklist_enabled = true;
}
void
dwflpp::build_user_blacklist()
{
string blfn = "^(";
string blfn_ret = "^(";
string blfile = "^(";
string blsection = "^(";
blfn += ".^";
blfile += ".^";
blsection += ".^";
blfn_ret += "_start";
blfn += ")$";
blfn_ret += ")$";
blfile += ")$";
blsection += ")";
if (sess.verbose > 2)
{
clog << _("blacklist regexps:") << endl;
clog << "blfn: " << blfn << endl;
clog << "blfn_ret: " << blfn_ret << endl;
clog << "blfile: " << blfile << endl;
clog << "blsection: " << blsection << endl;
}
int rc = regcomp (& blacklist_func, blfn.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_func regcomp failed"));
rc = regcomp (& blacklist_func_ret, blfn_ret.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_func_ret regcomp failed"));
rc = regcomp (& blacklist_file, blfile.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_file regcomp failed"));
rc = regcomp (& blacklist_section, blsection.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw SEMANTIC_ERROR (_("blacklist_section regcomp failed"));
blacklist_enabled = true;
}
string
dwflpp::get_blacklist_section(Dwarf_Addr addr)
{
string blacklist_section;
Dwarf_Addr bias;
Elf* elf = (dwarf_getelf (dwfl_module_getdwarf (module, &bias))
?: dwfl_module_getelf (module, &bias));
Dwarf_Addr offset = addr - bias;
if (elf)
{
Elf_Scn* scn = 0;
size_t shstrndx;
DWFL_ASSERT ("getshdrstrndx", elf_getshdrstrndx (elf, &shstrndx));
while ((scn = elf_nextscn (elf, scn)) != NULL)
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (! shdr)
continue; XXX
if (!(shdr->sh_flags & SHF_ALLOC))
continue;
GElf_Addr start = shdr->sh_addr;
GElf_Addr end = start + shdr->sh_size;
if (! (offset >= start && offset < end))
continue;
blacklist_section = elf_strptr (elf, shstrndx, shdr->sh_name);
break;
}
}
return blacklist_section;
}
GElf_Shdr *
dwflpp::get_section(string section_name, GElf_Shdr *shdr_mem, Elf **elf_ret)
{
GElf_Shdr *shdr = NULL;
Elf* elf;
Dwarf_Addr bias;
size_t shstrndx;
elf = dwfl_module_getelf (module, &bias);
Elf_Scn *probe_scn = NULL;
DWFL_ASSERT ("getshdrstrndx", elf_getshdrstrndx (elf, &shstrndx));
bool have_section = false;
while ((probe_scn = elf_nextscn (elf, probe_scn)))
{
shdr = gelf_getshdr (probe_scn, shdr_mem);
assert (shdr != NULL);
if (elf_strptr (elf, shstrndx, shdr->sh_name) == section_name)
{
have_section = true;
break;
}
}
if (! have_section || (have_section && shdr->sh_type == SHT_NOBITS))
{
elf = dwarf_getelf (dwfl_module_getdwarf (module, &bias));
if (! elf)
return NULL;
DWFL_ASSERT ("getshdrstrndx", elf_getshdrstrndx (elf, &shstrndx));
probe_scn = NULL;
while ((probe_scn = elf_nextscn (elf, probe_scn)))
{
shdr = gelf_getshdr (probe_scn, shdr_mem);
if (elf_strptr (elf, shstrndx, shdr->sh_name) == section_name)
{
have_section = true;
break;
}
}
}
if (!have_section)
return NULL;
if (elf_ret)
*elf_ret = elf;
return shdr;
}
Dwarf_Addr
dwflpp::relocate_address(Dwarf_Addr dw_addr, string& reloc_section)
{
Dwarf_Addr reloc_addr = dw_addr + module_bias;
if (!module)
{
assert(module_name == TOK_KERNEL);
reloc_section = "";
}
else if (dwfl_module_relocations (module) > 0)
{
int idx = dwfl_module_relocate_address (module, &reloc_addr);
const char* r_s = dwfl_module_relocation_info (module, idx, NULL);
if (r_s)
reloc_section = r_s;
if (reloc_section == "" && dwfl_module_relocations (module) == 1)
reloc_section = ".dynamic";
}
else
reloc_section = ".absolute";
return reloc_addr;
}
Dwarf_Op *
dwflpp::get_cfa_ops (Dwarf_Addr pc)
{
Dwarf_Op *cfa_ops = NULL;
if (sess.verbose > 2)
clog << "get_cfa_ops @0x" << hex << pc << dec
<< ", module_start @0x" << hex << module_start << dec << endl;
size_t cfa_nops = 0;
Dwarf_Addr bias = 0;
Dwarf_Frame *frame = NULL;
Dwarf_CFI *cfi = dwfl_module_dwarf_cfi (module, &bias);
if (cfi != NULL)
{
if (sess.verbose > 3)
clog << "got dwarf cfi bias: 0x" << hex << bias << dec << endl;
if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
else if (sess.verbose > 3)
clog << "dwarf_cfi_addrframe failed: " << dwarf_errmsg(-1) << endl;
}
else if (sess.verbose > 3)
clog << "dwfl_module_dwarf_cfi failed: " << dwfl_errmsg(-1) << endl;
if (cfa_ops == NULL)
{
cfi = dwfl_module_eh_cfi (module, &bias);
if (cfi != NULL)
{
if (sess.verbose > 3)
clog << "got eh cfi bias: 0x" << hex << bias << dec << endl;
Dwarf_Frame *frame = NULL;
if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
else if (sess.verbose > 3)
clog << "dwarf_cfi_addrframe failed: " << dwarf_errmsg(-1) << endl;
}
else if (sess.verbose > 3)
clog << "dwfl_module_eh_cfi failed: " << dwfl_errmsg(-1) << endl;
}
if (sess.verbose > 2)
{
if (cfa_ops == NULL)
clog << _("not found cfa") << endl;
else
{
Dwarf_Addr frame_start, frame_end;
bool frame_signalp;
int info = dwarf_frame_info (frame, &frame_start, &frame_end,
&frame_signalp);
clog << _F("found cfa, info: %d [start: %#" PRIx64 ", end: %#" PRIx64
", nops: %zu", info, frame_start, frame_end, cfa_nops) << endl;
}
}
return cfa_ops;
}
int
dwflpp::add_module_build_id_to_hash (Dwfl_Module *m,
void **userdata __attribute__ ((unused)),
const char *name,
Dwarf_Addr base,
void *arg)
{
string modname = name;
systemtap_session * s = (systemtap_session *)arg;
if (pending_interrupts)
return DWARF_CB_ABORT;
const unsigned char *bits;
GElf_Addr vaddr;
int bits_length = dwfl_module_build_id(m, &bits, &vaddr);
if(bits_length > 0)
{
string hex = hex_dump(bits, bits_length);
s->build_ids.push_back(hex);
}
return DWARF_CB_OK;
}
Dwarf_Addr
dwflpp::pr15123_retry_addr (Dwarf_Addr pc, Dwarf_Die* die)
{
if (getenv ("PR15123_DISABLE"))
return 0;
if (!getenv ("PR15123_ASSUME_MFENTRY")) {
Dwarf_Die cudie;
string producer, version;
dwarf_diecu (die, &cudie, NULL, NULL);
if (!is_gcc_producer(&cudie, producer, version))
return 0;
if (producer.find("-mfentry") == string::npos)
return 0;
}
vector<Dwarf_Die> scopes = getscopes(pc);
if (scopes.size() == 0)
return 0;
Dwarf_Die outer_function_die = scopes[0];
Dwarf_Addr entrypc;
if (!die_entrypc(& outer_function_die, &entrypc) || entrypc != pc)
return 0;
if (sess.architecture == "i386" ||
sess.architecture == "x86_64") {
if (sess.verbose > 2)
clog << _("retrying variable location-list lookup at address pc+5\n");
return pc + 5;
}
return 0;
}
bool
dwflpp::has_gnu_debugdata ()
{
Dwarf_Addr load_addr;
Elf* elf = dwfl_module_getelf (module, &load_addr);
size_t shstrndx;
assert (elf_getshdrstrndx (elf, &shstrndx) >= 0);
Elf_Scn *scn = NULL;
GElf_Shdr *gnu_debugdata_shdr = NULL;
GElf_Shdr gnu_debugdata_shdr_mem;
while ((scn = elf_nextscn (elf, scn)))
{
gnu_debugdata_shdr = gelf_getshdr (scn, &gnu_debugdata_shdr_mem);
assert (gnu_debugdata_shdr != NULL);
if (strcmp (elf_strptr (elf, shstrndx, gnu_debugdata_shdr->sh_name), ".gnu_debugdata") == 0)
return true;
}
return false;
}
bool
dwflpp::is_gcc_producer(Dwarf_Die *cudie, string& producer, string& version)
{
Dwarf_Attribute producer_attr;
if (!dwarf_attr_integrate(cudie, DW_AT_producer, &producer_attr))
return false;
const char *cproducer = dwarf_formstring(&producer_attr);
if (!cproducer)
return false;
producer = cproducer;
vector<string> tokens;
tokenize(producer, tokens);
if (tokens.size() < 3
|| tokens[0] != "GNU"
|| tokens[2].find_first_not_of(".0123456789") != string::npos)
return false;
version = tokens[2];
return true;
}
static bool
die_has_loclist(Dwarf_Die *begin_die)
{
Dwarf_Die die;
Dwarf_Attribute loc;
if (dwarf_child(begin_die, &die) != 0)
return false;
do
{
switch (dwarf_tag(&die))
{
case DW_TAG_formal_parameter:
case DW_TAG_variable:
if (dwarf_attr_integrate(&die, DW_AT_location, &loc)
&& dwarf_whatform(&loc) == DW_FORM_sec_offset)
return true;
break;
default:
if (dwarf_haschildren (&die))
if (die_has_loclist(&die))
return true;
break;
}
}
while (dwarf_siblingof (&die, &die) == 0);
return false;
}
bool
dwflpp::has_valid_locs ()
{
assert(cu);
string prod, vers;
if (is_gcc_producer(cu, prod, vers)
&& strverscmp(vers.c_str(), "4.5") < 0)
return false;
if (!die_has_loclist(cu))
return false;
if (sess.verbose > 2)
clog << _F("CU '%s' in module '%s' has valid locs",
cu_name().c_str(), module_name.c_str()) << endl;
return true;
}