Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/symtabAPI/src/dwarfWalker.C
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

2597 lines
87 KiB
C++

/*
* See the dyninst/COPYRIGHT file for copyright information.
*
* We provide the Paradyn Tools (below described as "Paradyn")
* on an AS IS basis, and do not warrant its validity or performance.
* We reserve the right to update, modify, or discontinue this
* software at any time. We shall have no obligation to supply such
* updates or modifications or any other form of support to you.
*
* By your use of Paradyn, you understand and agree that we (or any
* other person or entity with proprietary rights in Paradyn) are
* under no obligation to provide either maintenance services,
* update services, notices of latent defects, or correction of
* defects for Paradyn.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <algorithm>
#include "common/src/vgannotations.h"
#include "compiler_diagnostics.h"
#include "dwarfWalker.h"
#include "headers.h"
#include "Module.h"
#include "Symtab.h"
#include "Collections.h"
#include "dwarf.h"
#include "Object.h"
#include "Object-elf.h"
#include "Function.h"
#include "debug.h"
#include "dwarfExprParser.h"
#include "pathName.h"
#include "debug_common.h"
#include "Type-mem.h"
#include <elfutils/libdw.h>
#include <dwarf/src/dwarf_subrange.h>
#include <dwarf_names.h>
#include <dwarf_cu_info.h>
#include <stack>
using namespace Dyninst;
using namespace SymtabAPI;
using namespace DwarfDyninst;
using namespace std;
#define DWARF_FAIL_RET_VAL(x, v) do { \
int dwarf_fail_ret_val_status = (x); \
if (dwarf_fail_ret_val_status != 0) { \
types_printf("[%s:%d]: libdwarf returned %d, ret false\n", \
FILE__, __LINE__, dwarf_fail_ret_val_status); \
return (v); \
} \
} while (0)
#define DWARF_FAIL_RET(x) DWARF_FAIL_RET_VAL(x, false)
#define DWARF_ERROR_RET_VAL(x, v) do { \
int dwarf_error_ret_val_status = (x); \
if (dwarf_error_ret_val_status == 1 /*DW_DLV_ERROR*/) { \
types_printf("[%s:%d]: parsing failure, ret false\n", \
FILE__, __LINE__); \
return (v); \
} \
} while (0)
#define DWARF_ERROR_RET(x) DWARF_ERROR_RET_VAL(x, false)
#define DWARF_CHECK_RET_VAL(x, v) do { \
if (x) { \
types_printf("[%s:%d]: parsing failure, ret false\n", \
FILE__, __LINE__); \
return (v); \
} \
} while (0)
#define DWARF_CHECK_RET(x) DWARF_CHECK_RET_VAL(x, false)
DwarfWalker::DwarfWalker(Symtab *symtab, ::Dwarf *dbg, std::shared_ptr<ParsedFuncs> pf) :
DwarfParseActions(symtab, dbg),
parsedFuncs(pf),
is_mangled_name_(false),
modLow(0),
modHigh(0),
cu_header_length(0),
abbrev_offset(0),
addr_size(0),
offset_size(0),
extension_size(0),
signature(),
typeoffset(0),
next_cu_header(0),
compile_offset(0)
{
if (!parsedFuncs) {
parsedFuncs = std::make_shared<ParsedFuncs>();
}
}
DwarfWalker::~DwarfWalker() {
}
static inline void ompc_leftmost(Module* &out, Module* &in) {
out = out == NULL ? out : in;
}
#pragma omp declare \
reduction(leftmost : Module* : ompc_leftmost(omp_out, omp_in)) \
initializer(omp_priv = NULL)
bool DwarfWalker::parse() {
dwarf_printf("In DwarfWalker::parse() Parsing DWARF for %s, dgb():0x%p\n",filename().c_str(), (void*)dbg());
/* Start the dwarven debugging. */
Module *fixUnknownMod = NULL;
mod() = NULL;
/* Prepopulate type signatures for DW_FORM_ref_sig8 */
findAllSig8Types();
/* First .debug_types (0), then .debug_info (1).
* In DWARF4, only .debug_types contains DW_TAG_type_unit,
* but DWARF5 is considering them for .debug_info too.*/
/* NB: parseModule used to compute compile_offset as 11 bytes before the
* first die offset, to account for the header. This would need 23 bytes
* instead for 64-bit format DWARF, and even more for type units.
* (See DWARF4 sections 7.4 & 7.5.1.)
* But more directly, we know the first CU is just at 0x0, and each
* following CU is already reported in next_cu_header.
*/
compile_offset = next_cu_header = 0;
/* Iterate over the compilation-unit headers for .debug_types. */
uint64_t type_signaturep;
std::vector<Dwarf_Die> module_dies;
for(Dwarf_Off cu_off = 0;
dwarf_next_unit(dbg(), cu_off, &next_cu_header, &cu_header_length,
NULL, &abbrev_offset, &addr_size, &offset_size,
&type_signaturep, NULL) == 0;
cu_off = next_cu_header)
{
if(!dwarf_offdie_types(dbg(), cu_off + cu_header_length, &current_cu_die))
continue;
module_dies.push_back(current_cu_die);
compile_offset = next_cu_header;
}
size_t total_from_unit = module_dies.size();
dwarf_printf("Modules from dwarf_next_unit: %zu\n", total_from_unit);
/* Iterate over the compilation-unit headers for .debug_info. */
for(Dwarf_Off cu_off = 0;
dwarf_nextcu(dbg(), cu_off, &next_cu_header, &cu_header_length,
&abbrev_offset, &addr_size, &offset_size) == 0;
cu_off = next_cu_header)
{
if(!dwarf_offdie(dbg(), cu_off + cu_header_length, &current_cu_die))
continue;
module_dies.push_back(current_cu_die);
compile_offset = next_cu_header;
}
compile_offset = 0;
dwarf_printf("Modules from dwarf_nextcu: %zu\n", module_dies.size() - total_from_unit);
if (dwarf_getalt(dbg()) != NULL) {
DwarfWalker w(symtab(), dbg());
for (unsigned int i = 0; i < module_dies.size(); i++) {
w.push();
w.parseModule(module_dies[i],fixUnknownMod);
w.pop();
}
} else {
#pragma omp parallel
{
DwarfWalker w(symtab(), dbg(), parsedFuncs);
#pragma omp for reduction(leftmost:fixUnknownMod) \
schedule(dynamic) nowait
for (unsigned int i = 0; i < module_dies.size(); i++) {
w.push();
w.parseModule(module_dies[i],fixUnknownMod);
w.pop();
}
}
}
if (!fixUnknownMod)
return true;
dwarf_printf("Fixing types for final module %s\n", fixUnknownMod->fileName().c_str());
/* Fix type list. */
typeCollection *moduleTypes = typeCollection::getModTypeCollection(fixUnknownMod);
if(!moduleTypes) return false;
auto typeIter = moduleTypes->typesByID.begin();
for (;typeIter!=moduleTypes->typesByID.end();typeIter++)
{
typeIter->second->fixupUnknowns(fixUnknownMod);
}
/* Fix the types of variables. */
std::string variableName;
auto variableIter = moduleTypes->globalVarsByName.begin();
for (;variableIter!=moduleTypes->globalVarsByName.end();variableIter++)
{
if (variableIter->second->getDataClass() == dataUnknownType &&
moduleTypes->findType( variableIter->second->getID(), Type::share ) != NULL )
{
variableIter->second = moduleTypes->findType(variableIter->second->getID(), Type::share);
}
}
moduleTypes->setDwarfParsed();
return true;
}
bool DwarfWalker::parseModule(Dwarf_Die moduleDIE, Module *&fixUnknownMod) {
// Make sure `moduleDIE` is actually a compilation unit
if (!DwarfDyninst::is_parseable_unit(moduleDIE)) {
dwarf_printf("(0x%lx) Attempting to parse unit that isn't parseable.\n", id());
return false;
}
std::string moduleName = DwarfDyninst::die_name(moduleDIE);
auto moduleTag = dwarf_tag(&moduleDIE);
if (moduleName.empty() && moduleTag == DW_TAG_type_unit) {
uint64_t sig8 = * reinterpret_cast<uint64_t*>(&signature);
char buf[20];
snprintf(buf, sizeof(buf), "{%016llx}", (unsigned long long)sig8);
moduleName = buf;
}
dwarf_printf("Next DWARF module: %s with DIE %p and tag %d\n", moduleName.c_str(), (void*)moduleDIE.addr, moduleTag);
/* Set the language, if any. */
Dwarf_Attribute languageAttribute;
dwarf_attr(&moduleDIE, DW_AT_language, &languageAttribute);
// Set low and high ranges; this can fail, so don't check return addr.
setEntry(moduleDIE);
// These may not be set.
Address tempModLow, tempModHigh;
modLow = modHigh = 0;
Dwarf_Die e;
bool found_entry = dwarf_offdie(dbg(),offset(), &e);
if(!found_entry) return false;
if (findConstant(DW_AT_low_pc, tempModLow, &e, dbg())) {
modLow = convertDebugOffset(tempModLow);
}
if (findConstant(DW_AT_high_pc, tempModHigh, &e, dbg())) {
modHigh = convertDebugOffset(tempModHigh);
}
// Find the Symtab Module corresponding to this CU
mod() = [this]() {
SymtabAPI::Module* m = symtab()->findModuleByOffset(offset());
if(m) return m;
return symtab()->getDefaultModule();
}();
dwarf_printf("Mapped to Symtab module %p from '%s' at offset %zx\n", (void*)mod(), mod()->fileName().c_str(), offset());
if(!fixUnknownMod){
fixUnknownMod = mod();
}
if (!parse_int(moduleDIE, true))
return false;
return true;
}
bool DwarfWalker::buildSrcFiles(::Dwarf * /*dbg*/, Dwarf_Die entry, StringTablePtr srcFiles) {
size_t cnt = 0;
Dwarf_Files * df;
int ret = dwarf_getsrcfiles(&entry, &df, &cnt);
if(ret==-1) return true;
dyncompat::unique_lock<dyn_mutex> l(srcFiles->lock);
if(!srcFiles->empty()) {
return true;
} // already parsed, the module had better be right.
// The CU name is always an absolute path
auto comp_dir = Dyninst::DwarfDyninst::cu_dirname(entry);
// store all file sources found by libdw
for (unsigned i = 0; i < cnt; ++i) {
auto filename = dwarf_filesrc(df, i, NULL, NULL);
if(!filename) continue;
srcFiles->emplace_back(DwarfDyninst::detail::absolute_path(filename, comp_dir),"");
}
return true;
}
// As mentioned in the header, this is separate from parse()
// so we can have a non-Context-creating parse method that reuses
// the Context from the parent. This allows us to pass in current
// function, etc. without the Context stack exploding
bool DwarfWalker::parse_int(Dwarf_Die e, bool parseSib, bool dissociate_context) {
dwarf_printf("PARSE_INT entry, context size %d\n", stack_size());
// We escape the loop by checking parseSibling() after
// parsing this DIE and its children, if any
while(1) {
ContextGuard cg(*this, dissociate_context);
setEntry(e);
setParseSibling(parseSib);
if (!findTag())
return false;
name_.clear();
setMangledName(false);
Dwarf * desc = dwarf_cu_getdwarf(e.cu);
dwarf_printf("(0x%lx) Parsing entry with context size %d, func %s, encl %p, (%s), mod:%s, tag: %x\n",
id(), stack_size(),
curFunc()?curFunc()->getName().c_str():"(N/A)",
(void*)curEnclosure().get(), (dbg()!=desc)?"sup":"not sup",
mod()->fileName().c_str(), (unsigned int)dwarf_tag(&e));
bool ret = false;
switch(dwarf_tag(&e)) {
case DW_TAG_subprogram:
case DW_TAG_entry_point:
ret = parseSubprogram(NormalFunc);
break;
case DW_TAG_inlined_subroutine:
ret = parseSubprogram(InlinedFunc);
break;
case DW_TAG_lexical_block:
ret = parseLexicalBlock();
break;
case DW_TAG_try_block:
ret = parseTryBlock();
break;
case DW_TAG_catch_block:
ret = parseCatchBlock();
break;
case DW_TAG_common_block:
ret = parseCommonBlock();
break;
case DW_TAG_constant:
ret = parseConstant();
break;
case DW_TAG_variable:
ret = parseVariable();
break;
case DW_TAG_formal_parameter:
ret = parseFormalParam();
break;
case DW_TAG_base_type:
ret = parseBaseType();
break;
case DW_TAG_typedef:
ret = parseTypedef();
break;
case DW_TAG_array_type:
ret = parseArray();
break;
case DW_TAG_subrange_type:
ret = parseSubrange();
break;
case DW_TAG_enumeration_type:
ret = parseEnum();
break;
case DW_TAG_inheritance:
ret = parseInheritance();
break;
case DW_TAG_structure_type:
case DW_TAG_union_type:
case DW_TAG_class_type:
ret = parseStructUnionClass();
break;
case DW_TAG_enumerator:
ret = parseEnumEntry();
break;
case DW_TAG_member:
ret = parseMember();
break;
case DW_TAG_const_type:
case DW_TAG_packed_type:
case DW_TAG_volatile_type:
ret = parseConstPackedVolatile();
break;
case DW_TAG_subroutine_type:
/* If the pointer specifies argument types, this DIE has
children of those types. */
case DW_TAG_ptr_to_member_type:
case DW_TAG_pointer_type:
case DW_TAG_reference_type:
case DW_TAG_rvalue_reference_type:
ret = parseTypeReferences();
break;
case DW_TAG_compile_unit:
dwarf_printf("(0x%lx) Compilation unit, parsing children\n", id());
// Parse child
ret = parseChild();
break;
case DW_TAG_partial_unit:
dwarf_printf("(0x%lx) Partial unit, parsing children\n", id());
// Parse child
ret = parseChild();
break;
case DW_TAG_type_unit:
dwarf_printf("(0x%lx) Type unit, parsing children\n", id());
// Parse child
ret = parseChild();
break;
case DW_TAG_imported_unit:
{
dwarf_printf("(0x%lx) Imported unit, parsing imported\n", id());
// Parse child
ret = parseChild();
// parse imported
Dwarf_Attribute importAttribute;
dwarf_attr(&e, DW_AT_import, &importAttribute);
Dwarf_Die importedDIE;
auto die_p = dwarf_formref_die(&importAttribute, &importedDIE);
Dwarf * desc1 = dwarf_cu_getdwarf(importedDIE.cu);
dwarf_printf("(0x%lx) Imported DIE dwarf_desc: %p\n", id(), (void*)desc1);
if(!die_p) break;
if (!parse_int(importedDIE, true))
return false;
break;
}
default:
dwarf_printf("(0x%lx) Warning: unparsed entry with tag 0x%x, dwarf_tag(): 0x%x\n",
id(), tag(), (unsigned int)dwarf_tag(&e));
ret = true;
break;
}
dwarf_printf("Finished parsing 0x%lx, ret %d, parseChild %d, parseSibling %d\n\n",
id(), ret, parseChild(), parseSibling());
if (ret && parseChild() ) {
// Parse children
Dwarf_Die childDwarf;
int status = dwarf_child(&e, &childDwarf);
if(status == -1)
return false;
if (status == 0) {
dwarf_printf("(0x%lx) Parsing child\n\n", id());
if (!parse_int(childDwarf, true))
return false;
}
}
if (!parseSibling()) {
dwarf_printf("(0x%lx) Skipping sibling parse\n", id());
break;
}
dwarf_printf("(0x%lx) Asking for sibling\n", id());
Dwarf_Die siblingDwarf;
int status = dwarf_siblingof(&e, &siblingDwarf);
DWARF_CHECK_RET(status == -1);
if (status == 1) {
break;
}
e = siblingDwarf;
}
dwarf_printf("PARSE_INT exit, context size %d\n", stack_size());
return true;
}
bool DwarfWalker::parseCallsite()
{
int has_line = 0, has_file = 0;
Dwarf_Die e = entry();
has_file = dwarf_hasattr_integrate(&e, DW_AT_call_file);
if (!has_file)
return true;
has_line = dwarf_hasattr_integrate(&e, DW_AT_call_line);
if (!has_line)
return true;
using opt_string = dyncompat::optional<std::string>;
opt_string inline_file = find_call_file();
if (!inline_file)
return false;
Dyninst::Offset inline_line;
bool result = findConstant(DW_AT_call_line, inline_line, &e, dbg());
if (!result)
return false;
InlinedFunction *ifunc = static_cast<InlinedFunction *>(curFunc());
ifunc->setFile(inline_file.get());
ifunc->callsite_line = inline_line;
return true;
}
bool DwarfWalker::setFunctionFromRange(inline_t func_type)
{
//Use the lowest range as an entry for symbol matching
Address lowest = 0x0;
bool set_lowest = false;
if (!hasRanges()) {
dwarf_printf("(0x%lx) setFunctionFromRange has no ranges, returning false\n", id());
return false;
}
for (range_set_t::iterator i = ranges_begin(); i != ranges_end(); i++) {
if (!set_lowest) {
lowest = i->first;
set_lowest = true;
continue;
}
if (lowest > i->first)
lowest = i->first;
}
if (!set_lowest) {
//No ranges. Don't panic, this is probably an abstract origin or specification
// we'll get to it latter if it's really used.
dwarf_printf("(0x%lx) setFunctionFromRange has ranges, but no lowest, returning false\n", id());
return false;
}
if (func_type == InlinedFunc) {
return createInlineFunc();
}
//Try to associate the function with existing symbols
setFuncFromLowest(lowest);
return true;
}
void DwarfWalker::setFuncFromLowest(Address lowest) {
Function *f = NULL;
bool result = symtab()->findFuncByEntryOffset(f, lowest);
if (result) {
setFunc(f);
dwarf_printf("(0x%lx) Lookup by offset 0x%lx identifies %s\n",
id(), lowest, curFunc()->getName().c_str());
} else {
dwarf_printf("(0x%lx) Lookup by offset 0x%lx failed\n", id(), lowest);
}
}
bool DwarfWalker::createInlineFunc() {
FunctionBase *parent = curFunc();
if (parent) {
InlinedFunction *ifunc = new InlinedFunction(parent);
setFunc(ifunc);
dwarf_printf("(0x%lx) Created new inline, parent is %s\n", id(), parent->getName().c_str());
return true;
} else {
//InlinedSubroutine without containing subprogram. Weird.
dwarf_printf("(0x%lx) setFunctionFromRange found inline without parent, returning false\n", id());
return false;
}
}
void DwarfParseActions::addMangledFuncName(std::string name)
{
curFunc()->addMangledName(name, true, true);
}
void DwarfParseActions::addPrettyFuncName(std::string name)
{
curFunc()->addPrettyName(name, true, true);
}
// helper class to set and restore currentSubprogramFunction
namespace {
class SetAndRestoreFunction
{
public:
SetAndRestoreFunction(FunctionBase* &v, FunctionBase* newFunc) : var(v)
{
savedFunc = var;
var = newFunc;
}
~SetAndRestoreFunction()
{
var = savedFunc;
}
private:
FunctionBase* &var;
FunctionBase* savedFunc;
};
}
bool DwarfWalker::parseSubprogram(DwarfWalker::inline_t func_type) {
bool name_result;
dwarf_printf("(0x%lx) parseSubprogram entry\n", id());
parseRangeTypes(entry());
setFunctionFromRange(func_type);
// Name first
FunctionBase *func = curFunc();
name_result = findFuncName();
if (curEnclosure() && !func) {
// This is a member function; create the type entry
// Since curFunc is false, we're not going back to reparse this
// entry with a function object.
dyncompat::shared_ptr<Type> ftype = NULL;
getReturnType(ftype);
addFuncToContainer(ftype);
dwarf_printf("(0x%lx) parseSubprogram not parsing member function's children\n", id());
setParseChild(false);
}
//curFunc will be set if we're parsing a defined object, or
// if we're recursively parsing a specification or abstract
// entry under a defined object. It'll be unset if we're
// parsing a specification or abstract entry at the top-level
//This keeps us from parsing abstracts or specifications until
// we need them.
if (!func) {
dwarf_printf("(0x%lx) parseSubprogram not parsing children b/c curFunc() NULL\n", id());
setParseChild(false);
return true;
}
// this function's accessor lock to check if parsed and/or parse it
ParsedFuncs::accessor funcParsed;
// recursive if parsing a specification or abstract origin,
// accessor is already locked
bool isRecursiveSubprogramParse = (func == currentSubprogramFunction);
if (!isRecursiveSubprogramParse) {
// check if function is parsed, hold mutex until scope is left
parsedFuncs->insert(funcParsed, func);
if (funcParsed->second) {
dwarf_printf("(0x%lx) parseSubprogram not parsing children b/c curFunc() marked parsed in parsedFuncs\n", id());
if(name_result) {
dwarf_printf("\tname is %s\n", curName().c_str());
}
setParseChild(false);
return true;
}
}
// set the current function being parsed and restore old value on return
SetAndRestoreFunction setAndRestore(currentSubprogramFunction, func);
if (name_result && !curName().empty()) {
dwarf_printf("(0x%lx) Identified function name as %s\n", id(), curName().c_str());
if (isMangledName()) {
func->addMangledName(curName(), true);
}
// Only keep pretty names around for inlines, which probably don't have mangled names
else {
dwarf_printf("(0x%lx) Adding as pretty name to inline\n", id());
func->addPrettyName(curName(), true);
}
}
//Collect callsite information for inlined functions.
if (func_type == InlinedFunc) {
parseCallsite();
}
// Get the return type
setFuncReturnType();
// Get range information
if (hasRanges()) {
setRanges(func);
}
// Dwarf outlines some function information. You have the base entry, which contains
// address ranges, frame base information, and optionally a "abstract origin"
// or "specification" entry that points to more information.
// We want to skip parsing specification or abstract entries until we have
// the base entry and can find/create the corresponding function object.
// Get the frame base if it exists
if (!getFrameBase())
return false;
// Parse parent nodes and their children but not their sibling
bool hasAbstractOrigin = false;
if (!handleAbstractOrigin(hasAbstractOrigin))
return false;
if (hasAbstractOrigin) {
dwarf_printf("(0x%lx) Parsing abstract parent\n", id());
if (!parse_int(abstractEntry(), false))
return false;
}
// An abstract origin will point to a specification if it exists
// This can actually be three-layered, so backchain again
bool hasSpecification = false;
if (!handleSpecification(hasSpecification))
return false;
if ( hasSpecification ) {
dwarf_printf("(0x%lx) Parsing specification entry\n", id());
if (!parse_int(specEntry(), false))
return false;
}
if (!isRecursiveSubprogramParse) {
// the function is now parsed
funcParsed->second = true;
}
if (func_type == InlinedFunc) {
dwarf_printf("End parseSubprogram for inlined func at 0x%p\n", (void*)func->getOffset());
}
return true;
}
void DwarfWalker::setRanges(FunctionBase *func) {
if(func->ranges.empty()) {
Address last_low = 0, last_high = 0;
for (auto i = ranges_begin(); i != ranges_end(); i++) {
Address low = i->first;
Address high = i->second;
if (last_low == low && last_high == high)
continue;
last_low = low;
last_high = high;
func->ranges.push_back(FuncRange(low, high - low, curFunc()));
}
}
}
bool DwarfWalker::parseRangeTypes(Dwarf_Die die) {
dwarf_printf("(0x%lx) Parsing ranges\n", id());
clearRanges();
std::vector<AddressRange> newRanges = getDieRanges(die);
for(auto r = newRanges.begin();
r != newRanges.end();
++r)
{
setRange(*r);
}
return !newRanges.empty();
}
vector<AddressRange> DwarfWalker::getDieRanges(Dwarf_Die die)
{
std::vector<AddressRange> newRanges;
Dwarf_Addr base;
Dwarf_Addr start, end;
ptrdiff_t offset = 0;
while(1)
{
offset = dwarf_ranges(&die, offset, &base, &start, &end);
if(offset < 0) return std::vector<AddressRange>();
if(offset == 0) break;
dwarf_printf("Lexical block from 0x%lx to 0x%lx\n", start, end);
newRanges.push_back(AddressRange(start, end));
}
return newRanges;
}
bool DwarfWalker::parseLexicalBlock() {
dwarf_printf("(0x%lx) Parsing lexical block\n", id());
return parseRangeTypes(entry());
}
bool DwarfWalker::parseTryBlock() {
dwarf_printf("(0x%lx) Parsing try block ranges\n", id());
return parseRangeTypes(entry());
}
bool DwarfWalker::parseCatchBlock() {
dwarf_printf("(0x%lx) Parsing catch block ranges\n", id());
return parseRangeTypes(entry());
}
bool DwarfWalker::parseCommonBlock() {
dwarf_printf("(0x%lx) Parsing common block\n", id());
std::string commonBlockName = die_name();
Symbol* commonBlockVar = findSymbolForCommonBlock(commonBlockName);
if(!commonBlockVar)
{
return false;
}
setCommon(getCommonBlockType(commonBlockName));
return true;
}
Symbol *DwarfWalker::findSymbolForCommonBlock(const string &commonBlockName) {
return findSymbolByName(commonBlockName, Symbol::ST_OBJECT);
}
dyncompat::shared_ptr<Type> DwarfWalker::getCommonBlockType(string &commonBlockName) {
auto commonBlockType = tc()->findVariableType(commonBlockName, Type::share);
if(!commonBlockType || !commonBlockType->isCommonType()) {
commonBlockType = Type::make_shared<typeCommon>( type_id(), commonBlockName );
tc()->addGlobalVariable(commonBlockType);
}
return commonBlockType;
}
bool DwarfWalker::parseConstant() {
// Right now we don't handle these
dwarf_printf("(0x%lx) Skipping named constant/variable with constant value\n", id());
return true;
}
bool DwarfWalker::parseVariable() {
dwarf_printf("(0x%lx) ParseVariable entry\n", id());
/* Acquire the name, type, and line number. */
/* A variable may occur inside a function, as either static or local.
A variable may occur inside a container, as C++ static member.
A variable may not occur in either, as a global.
For the first two cases, we need the variable's name, its type,
its line number, and its offset or address in order to tell
Dyninst about it. Dyninst only needs to know the name and type
of a global. (Actually, it already knows the names of the globals;
we're really just telling it the types.)
Variables may have two entries, the second, with a _specification,
being the only one with the location. */
dyncompat::shared_ptr<Type> type;
if (!findType(type, false))
return false;
if(!type)
return false;
/* If this DIE has a _specification, use that for the rest of our inquiries. */
bool hasSpecification = false;
if (!handleSpecification(hasSpecification))
return false;
curName() = die_name();
removeFortranUnderscore(curName());
/* We'll start with the location, since that's most likely to
require the _specification. */
std::vector<VariableLocation> locs;
if (!decodeLocationList(DW_AT_location, NULL, locs))
return false;
if (locs.empty()) return true;
for (unsigned i=0; i<locs.size(); i++) {
if (locs[i].lowPC) {
locs[i].lowPC = convertDebugOffset(locs[i].lowPC);
}
if (locs[i].hiPC) {
locs[i].hiPC = convertDebugOffset(locs[i].hiPC);
}
}
/* If the DIE has an _abstract_origin, we'll use that for the
remainder of our inquiries. */
bool hasAbstractOrigin;
if (!handleAbstractOrigin(hasAbstractOrigin)) return false;
if (hasAbstractOrigin) {
// Clone to spec entry too
setSpecEntry(abstractEntry());
}
Dwarf_Word variableLineNo;
bool hasLineNumber = false;
std::string fileName;
if (!curFunc() && !curEnclosure()) {
createGlobalVariable(locs, type);
}
else
{
if (!getLineInformation(variableLineNo, hasLineNumber, fileName))
return false;
if (!nameDefined()) return true;
if (curFunc()) {
/* We now have the variable name, type, offset, and line number.
Tell Dyninst about it. */
createLocalVariable(locs, type, variableLineNo, fileName);
}
else {
auto ret = addStaticClassVariable(locs, type);
return ret;
}
}
return true;
}
bool DwarfWalker::addStaticClassVariable(const vector<VariableLocation> &locs, dyncompat::shared_ptr<Type> type) {
if( locs[0].stClass != storageRegOffset )
{
dwarf_printf("(0x%lx) Adding variable to an enclosure\n", id());
curEnclosure()->asFieldListType().addField(curName(), type, locs[0].frameOffset);
return true;
}
return false;
}
void DwarfWalker::createGlobalVariable(const vector<VariableLocation> &locs, dyncompat::shared_ptr<Type> type) {
/* The typeOffset forms a module-unique type identifier,
so the Type look-ups by it rather than name. */
dwarf_printf("(0x%lx) Adding global variable\n", id());
Offset addr = 0;
if (locs.size() && locs[0].stClass == storageAddr)
addr = locs[0].frameOffset;
std::vector<Variable *> vars;
bool result = symtab()->findVariablesByOffset(vars, addr);
if (result) {
for (auto v: vars) {
v->setType(type);
}
}
tc()->addGlobalVariable(type);
}
void DwarfWalker::createLocalVariable(const vector<VariableLocation> &locs, dyncompat::shared_ptr<Type> type,
Dwarf_Word variableLineNo,
const string &fileName) {
localVar * newVariable = new localVar(curName(),
type,
fileName,
(int) variableLineNo,
curFunc());
dwarf_printf("(0x%lx) Created localVariable '%s' (%p), currentFunction %p\n",
id(), curName().c_str(), (void*)newVariable, (void*)curFunc());
for (unsigned int i = 0; i < locs.size(); ++i) {
dwarf_printf("(0x%lx) (%s) Adding location %u of %d: (0x%lx - 0x%lx): %s, %s, %s, %ld\n",
id(), newVariable->getName().c_str(), i + 1, (int) locs.size(), locs[i].lowPC, locs[i].hiPC,
storageClass2Str(locs[i].stClass),
storageRefClass2Str(locs[i].refClass),
locs[i].mr_reg.name().c_str(),
locs[i].frameOffset);
newVariable->addLocation(locs[i]);
}
curFunc()->addLocalVar(newVariable);
}
bool DwarfWalker::parseFormalParam() {
dwarf_printf("(0x%lx) Parsing formal parameter\n", id());
/* A formal parameter must occur in the context of a function.
(That is, we can't do anything with a formal parameter to a
function we don't know about.) */
/* It's probably worth noting that a formal parameter may have a
default value. Since, AFAIK, Dyninst does nothing with this information,
neither will we. */
if (!curFunc()) {
dwarf_printf("(0x%lx) No function defined, returning\n", id());
return true;
}
/* We need the formal parameter's name, its type, its line number,
and its offset from the frame base in order to tell the
rest of Dyninst about it. A single _formal_parameter
DIE may not have all of this information; if it does not,
we will ignore it, hoping to catch it when it is later
referenced as an _abstract_origin from another _formal_parameter
DIE. If we never find such a DIE, than there is not enough
information to introduce it to Dyninst. */
/* We begin with the location, since this is the attribute
most likely to require using the _abstract_origin. */
std::vector<VariableLocation> locs;
if (!decodeLocationList(DW_AT_location, NULL, locs)) return false;
if (locs.empty()) {
dwarf_printf("(0x%lx) No locations associated with formal, returning\n", id());
return true;
}
/* If the DIE has an _abstract_origin, we'll use that for the
remainder of our inquiries. */
bool hasAbstractOrigin;
if (!handleAbstractOrigin(hasAbstractOrigin)) return false;
if (hasAbstractOrigin) {
// Clone to spec entry too
setSpecEntry(abstractEntry());
}
/* Acquire the parameter's type. */
dyncompat::shared_ptr<Type> paramType;
if (!findType(paramType, false))
{
dwarf_printf("(0x%lx) param type not acquired\n", id());
return false;
}
curName() = die_name();
/* We can't do anything with anonymous parameters. */
if (!nameDefined()) {
dwarf_printf("(0x%lx) No name associated with formal, returning\n", id());
return true;
}
Dwarf_Word lineNo = 0;
bool hasLineNumber = false;
std::string fileName;
if (!getLineInformation(lineNo, hasLineNumber, fileName)) return false;
createParameter(locs, paramType, lineNo, fileName);
return true;
}
void DwarfWalker::createParameter(const vector<VariableLocation> &locs,
dyncompat::shared_ptr<Type> paramType, Dwarf_Word lineNo, const string &fileName)
{
localVar * newParameter = new localVar(curName(),
paramType,
fileName, (int) lineNo,
curFunc());
dwarf_printf("(0x%lx) Creating new formal parameter %s/%p (%s) (%p)\n",
id(),
curName().c_str(),
(void*)paramType.get(), paramType->getName().c_str(),
(void*)curFunc());
for (unsigned int i = 0; i < locs.size(); ++i)
{
newParameter->addLocation(locs[i]);
}
/* This is just brutally ugly. Why don't we take care of this invariant automatically? */
curFunc()->addParam(newParameter);
}
bool DwarfWalker::parseBaseType() {
if(!tc()) return false;
dwarf_printf("(0x%lx) parseBaseType entry\n", id());
curName() = die_name();
if (!nameDefined()) {
dwarf_printf("(0x%lx) No name for type, returning early\n", id());
return true;
}
unsigned size = 0;
if (!findSize(size)) {
dwarf_printf("(0x%lx) No size for type '%s', returning early\n", id(), curName().c_str());
return false;
}
// Fetch the encoding
Dwarf_Die e = entry();
Dwarf_Attribute encoding_attr{};
if(!dwarf_attr(&e, DW_AT_encoding, &encoding_attr)) {
dwarf_printf("(0x%lx) Unable to determine encoding for type '%s'\n", id(), curName().c_str());
return false;
}
Dwarf_Sword encoding{};
DWARF_FAIL_RET(dwarf_formsdata(&encoding_attr, &encoding));
// linear machine address (for segmented addressing modes)
const bool is_address = encoding == DW_ATE_address;
// Integral types
// NB: We explicitly don't support DW_ATE_[un]signed_fixed and DW_ATE_packed_decimal
// because we don't support languages that use those (e.g., COBOL)
const bool is_boolean = encoding == DW_ATE_boolean;
const bool is_signed_int = encoding == DW_ATE_signed;
const bool is_signed_char = encoding == DW_ATE_signed_char;
const bool is_unsigned_int = encoding == DW_ATE_unsigned;
const bool is_unsigned_char = encoding == DW_ATE_unsigned_char;
// Float types
const bool is_complex_float = encoding == DW_ATE_complex_float;
const bool is_float = encoding == DW_ATE_float;
const bool is_imaginary_float = encoding == DW_ATE_imaginary_float;
const bool is_decimal_float = encoding == DW_ATE_decimal_float;
// Stringy types
// NB: We explicitly don't support DW_ATE_edited and DW_ATE_numeric_string
// because we don't support languages that use those (e.g., COBOL)
const bool is_UTF = encoding == DW_ATE_UTF;
const bool is_signed = is_signed_int || is_signed_char;
const bool is_unsigned = is_unsigned_int || is_unsigned_char;
const bool is_integral = is_boolean || is_signed || is_unsigned;
const bool is_floating_point = is_float || is_complex_float || is_imaginary_float || is_decimal_float;
const bool is_string = is_UTF;
typeScalar::properties_t p = {
is_integral, is_floating_point, is_string,
is_address, is_boolean,
is_complex_float, is_float, is_imaginary_float, is_decimal_float,
is_signed, is_signed_char, is_unsigned, is_unsigned_char,
is_UTF
};
/* Generate the appropriate built-in type; since there's no
reliable way to distinguish between a built-in and a scalar,
we don't bother to try. */
auto baseType = Type::make_shared<typeScalar>( type_id(), size, curName(), p);
/* Add the basic type to our collection. */
typeScalar *debug = baseType.get();
auto baseTy = tc()->addOrUpdateType( baseType );
dwarf_printf("(0x%lx) Created type %p / %s (pre add %p / %s) for id %d, size %u, in TC %p\n", id(),
(void*)baseTy.get(), baseTy->getName().c_str(),
(void*)debug, debug->getName().c_str(),
(int) offset(), size,
(void*)tc());
return true;
}
bool DwarfWalker::parseTypedef() {
dwarf_printf("(0x%lx) parseTypedef entry\n", id());
dyncompat::shared_ptr<Type> referencedType;
if (!findType(referencedType, true)) return false;
curName() = die_name();
if (!nameDefined()) {
if (!fixName(curName(), referencedType)) return false;
}
auto typeDef = tc()->addOrUpdateType( Type::make_shared<typeTypedef>( type_id(), referencedType, curName()) );
dwarf_printf("(0x%lx) Created type %p / %s for type_id %d, offset 0x%lx, size %u, in TC %p, mod:%s\n", id(),
(void*)typeDef.get(), typeDef->getName().c_str(), type_id(),
offset(), typeDef->getSize(), (void*)tc(), mod()->fileName().c_str());
return true;
}
bool DwarfWalker::parseArray() {
dwarf_printf("(0x%lx) Parsing array\n", id());
dyncompat::shared_ptr<Type> elementType;
if (!findType(elementType, false))
return false;
if (!elementType)
return false;
curName() = die_name();
Dwarf_Die e = entry();
// The dimensions of an array are stored as subranges in the child DIE
Dwarf_Die child;
int result = dwarf_child(&e, &child);
if (result < 0) {
dwarf_printf("(0x%lx) Error calling dwarf_child\n", id());
return false;
}
if (result == 1) {
dwarf_printf("(0x%lx) dwarf_child found no subranges for array\n", id());
return false;
}
// Find the subranges
// A multidimensional array will have a subrange for each dimension
std::stack<dyncompat::shared_ptr<typeSubrange>> subranges;
do {
auto subrange = parseSubrange(&child);
if (!subrange) {
return false;
}
// Register the subrange with the type collection
tc()->addOrUpdateType(subrange);
subranges.push(std::move(subrange));
} while (dwarf_siblingof(&child, &child) == 0);
// This should never happen, but it's good to be paranoid
if (subranges.size() == 0) {
dwarf_printf("(0x%lx) No subranges found for array\n", id());
return false;
}
std::string name{"__array" + std::to_string(offset())};
auto convert = [this, &name](dyncompat::shared_ptr<typeSubrange> t,
dyncompat::shared_ptr<Type> base_t) {
auto arr_t =
Type::make_shared<typeArray>(base_t, t->getLow(), t->getHigh(), name);
auto type = tc()->addOrUpdateType(arr_t);
dwarf_printf("(0x%lx) Creating array dimension. ID: %d, %s[%lu:%lu]\n",
id(), type->getID(), name.c_str(), t->getLow(), t->getHigh());
return type;
};
auto make_base_t = [this, &name](dyncompat::shared_ptr<typeSubrange> t,
dyncompat::shared_ptr<Type> base) {
auto arr_t =
Type::make_shared<typeArray>(type_id(), base, t->getLow(),
t->getHigh(), name + "[]");
auto type = tc()->addOrUpdateType(arr_t);
dwarf_printf("(0x%lx) Creating array. ID: %d, %s[%lu:%lu]\n",
id(), type->getID(), arr_t->getName().c_str(), t->getLow(), t->getHigh());
return type;
};
// Convert the subranges to a Type sequence
dyncompat::shared_ptr<Type> base_t;
if(subranges.size() == 1) {
auto cur_subrange = subranges.top();
subranges.pop();
base_t = make_base_t(cur_subrange, elementType);
} else {
// The last subrange (i.e., the highest dimension) gets the element type as
// its base type
auto cur_subrange = subranges.top();
subranges.pop();
auto base = convert(cur_subrange, elementType);
// The rest get the subsequent dimension's type as their base types
while (subranges.size() > 1) {
cur_subrange = subranges.top();
subranges.pop();
base = convert(cur_subrange, base);
}
// The first subrange acts as the entry point for the array's type, so it
// needs an explicit `type_id` and different name.
cur_subrange = subranges.top();
subranges.pop();
base_t = make_base_t(cur_subrange, base);
}
/* Don't parse the children again. */
setParseChild(false);
return true;
}
bool DwarfWalker::parseSubrange() {
Dwarf_Die e = entry();
auto subrange = parseSubrange(&e);
dyncompat::shared_ptr<Type> rangeType = tc()->addOrUpdateType(subrange);
dwarf_printf("(0x%lx) Created subrange type: ID 0x%d, pointer %p (tc %p)\n", id(),
rangeType->getID(), (void *)rangeType.get(), (void *)tc());
return true;
}
bool DwarfWalker::parseEnum() {
if(!tc()) return false;
dwarf_printf("(0x%lx) parseEnum entry\n", id());
dyncompat::shared_ptr<Type> underlying_type{};
if (!findType(underlying_type, false)) {
dwarf_printf("(0x%lx) type not found\n", id());
return false;
}
curName() = die_name();
// Handle C++ scoped enums (i.e., 'enum class')
auto type = [this, &underlying_type]() {
Dwarf_Attribute valueAttr{};
Dwarf_Die e = entry();
if(dwarf_attr(&e, DW_AT_enum_class, & valueAttr)) {
return Type::make_shared<typeEnum>(underlying_type, curName(), type_id(), typeEnum::scoped_t{});
}
return Type::make_shared<typeEnum>(underlying_type, curName(), type_id());
}();
setEnum(tc()->addOrUpdateType(type));
dwarf_printf("(0x%lx) end parseEnum\n", id());
return true;
}
bool DwarfWalker::parseInheritance() {
dwarf_printf("(0x%lx) parseInheritance entry\n", id());
/* Acquire the super class's type. */
dyncompat::shared_ptr<Type> superClass = NULL;
if (!findType(superClass, false)) return false;
if (!superClass) return false;
dwarf_printf("(0x%lx) Found %p as superclass\n", id(), (void*)superClass.get());
visibility_t visibility = visUnknown;
if (!findVisibility(visibility)) return false;
/* Add a readily-recognizable 'bad' field to represent the superclass.
Type::getComponents() will Do the Right Thing. */
std::string fName = "{superclass}";
curEnclosure()->asFieldListType().addField( fName, superClass, -1, visibility );
dwarf_printf("(0x%lx) Added type %p as %s to %p\n", id(), (void*)superClass.get(), fName.c_str(), (void*)curEnclosure().get());
return true;
}
bool DwarfWalker::parseStructUnionClass() {
if(!tc()) return false;
dwarf_printf("(0x%lx) parseStructUnionClass entry\n", id());
if(tag() != DW_TAG_structure_type &&
tag() != DW_TAG_union_type &&
tag() != DW_TAG_class_type)
{
dwarf_printf("WARNING: parseStructUnionClass called on non-aggregate tagged entry\n");
return false;
}
curName() = die_name();
dwarf_printf("(0x%lx) Struct/Union/Class name from dwarf: %s\n", id(), curName().c_str());
if (!nameDefined()) {
/* anonymous unions, structs and classes are explicitely labelled */
Dwarf_Word lineNumber;
bool hasLineNumber = false;
std::string fileName;
if (!getLineInformation(lineNumber, hasLineNumber, fileName)) return false;
stringstream ss;
ss << "{anonymous ";
switch (tag()) {
case DW_TAG_structure_type:
ss << "struct";
break;
case DW_TAG_union_type:
ss << "union";
break;
case DW_TAG_class_type:
ss << "class";
break;
}
if (fileName.length() && hasLineNumber)
ss << " at " << fileName << ":" << lineNumber;
ss << "}";
curName() = ss.str();
}
unsigned size=0;
findSize(size);
dyncompat::shared_ptr<Type> containingType;
switch ( tag() ) {
case DW_TAG_structure_type:
case DW_TAG_class_type:
{
auto ts = Type::make_shared<typeStruct>( type_id(), curName());
ts->setSize(size);
containingType = tc()->addOrUpdateType(ts);
dwarf_printf("(0x%lx) Created type %p / %s for type_id %d, offset 0x%lx, size %u, in TC %p, mod:%s\n", id(),
(void*)containingType.get(), containingType->getName().c_str(), type_id(),
offset(), containingType->getSize(), (void*)tc(), mod()->fileName().c_str());
break;
}
case DW_TAG_union_type:
{
auto tu = Type::make_shared<typeUnion>( type_id(), curName());
tu->setSize(size);
containingType = tc()->addOrUpdateType(tu);
dwarf_printf("(0x%lx) Created type %p / %s for type_id %d, offset 0x%lx, size %u, in TC %p, mod:%s\n", id(),
(void*)containingType.get(), containingType->getName().c_str(), type_id(),
offset(), containingType->getSize(), (void*)tc(), mod()->fileName().c_str());
break;
}
default:
{
Dwarf_Die e = entry();
dwarf_printf("(0x%lx) Warning: type not created tag 0x%x, dwarf_tag(): 0x%x\n",
id(), tag(), (unsigned int)dwarf_tag(&e));
}
}
setEnclosure(containingType);
dwarf_printf("(0x%lx) Started class, union, or struct: %p\n",
id(), (void*)containingType.get());
return true;
}
bool DwarfWalker::parseEnumEntry() {
dwarf_printf("(0x%lx) parseEnumEntry entry\n", id());
std::string name = die_name();
auto value = findConstValue();
if(!value) { return false; }
curEnum()->asEnumType().addConstant(name, *value);
dwarf_printf("(0x%lx) end parseEnumEntry\n", id());
return true;
}
bool DwarfWalker::parseMember() {
dwarf_printf("(0x%lx) parseMember entry\n", id());
if (!curEnclosure()) return false;
dyncompat::shared_ptr<Type> memberType = NULL;
if (!findType(memberType, false)) return false;
if (!memberType) return false;
curName() = die_name();
if (findConstValue()) {
if(!nameDefined()) return false;
dwarf_printf("(0x%lx) member is a named constant, forwarding to parseConstant\n", id());
return parseConstant();
}
std::vector<VariableLocation> locs;
Address initialStackValue = 0;
if (!decodeLocationList(DW_AT_data_member_location, &initialStackValue, locs)) return false;
if (locs.empty()) {
if (curEnclosure()->getUnionType()) {
/* GCC generates DW_TAG_union_type without DW_AT_data_member_location */
if (!constructConstantVariableLocation((Address) 0, locs)) return false;
} else {
dwarf_printf("(0x%lx) Skipping member as no location is given.\n", id());
return true;
}
}
/* DWARF stores offsets in bytes unless the member is a bit field.
Correct memberOffset as indicated. Also, memberSize is in bytes
from the underlying type, not the # of bits used from it, so
correct that as necessary as well. */
long int memberSize = memberType->getSize();
// This code changes memberSize, which is then discarded. I'm not sure why...
if (!fixBitFields(locs, memberSize)) return false;
if(locs.empty()) return false;
int offset_to_use = locs[0].frameOffset;
dwarf_printf("(0x%lx) Using offset of 0x%x\n", id(), (unsigned int)offset_to_use);
if (nameDefined()) {
curEnclosure()->asFieldListType().addField(curName(), memberType, offset_to_use);
}
else {
curEnclosure()->asFieldListType().addField("[anonymous union]", memberType, offset_to_use);
}
return true;
}
// TODO: this looks a lot like parseTypedef. Collapse?
bool DwarfWalker::parseConstPackedVolatile() {
dwarf_printf("(0x%lx) parseConstPackedVolatile entry\n", id());
dyncompat::shared_ptr<Type> type = NULL;
if (!findType(type, true)) return false;
curName() = die_name();
if (!nameDefined()) {
dwarf_printf("(0x%lx) parseConstPackedVolatile fixName\n", id());
if (!fixName(curName(), type)) return false;
}
tc()->addOrUpdateType( Type::make_shared<typeTypedef>(type_id(), type, curName()));
return true;
}
bool DwarfWalker::parseTypeReferences() {
dwarf_printf("(0x%lx) parseTypeReferences entry\n", id());
dyncompat::shared_ptr<Type> typePointedTo = NULL;
if (!findType(typePointedTo, true))
{
dwarf_printf("(0x%lx) type not found\n", id());
return false;
}
curName() = die_name();
Dwarf_Die e = entry();
dyncompat::shared_ptr<Type> indirectType;
switch ( tag() ) {
case DW_TAG_subroutine_type:
indirectType = tc()->addOrUpdateType(Type::make_shared<typeFunction>(
type_id(), typePointedTo, curName()));
break;
case DW_TAG_ptr_to_member_type:
case DW_TAG_pointer_type:
indirectType = tc()->addOrUpdateType(Type::make_shared<typePointer>(
type_id(), typePointedTo, curName()));
dwarf_printf("(0x%lx) Created type %p / %s for type_id %d, offset 0x%lx, size %u, in TC %p, mod:%s\n", id(),
(void*)indirectType.get(), indirectType->getName().c_str(), type_id(),
offset(), indirectType->getSize(), (void*)tc(), mod()->fileName().c_str());
break;
case DW_TAG_reference_type:
if(!nameDefined()){
curName() = "&";
}
indirectType = tc()->addOrUpdateType(Type::make_shared<typeRef>(
type_id(), typePointedTo, curName()));
dwarf_printf("(0x%lx) Created type %p / %s for type_id %d, offset 0x%lx, size %u, in TC %p\n", id(),
(void*)indirectType.get(), indirectType->getName().c_str(), type_id(),
offset(), indirectType->getSize(), (void*)tc());
break;
case DW_TAG_rvalue_reference_type:
if(!nameDefined()) {
curName() = "&&";
}
indirectType = tc()->addOrUpdateType(Type::make_shared<typeRef>(
type_id(), typePointedTo, curName(), typeRef::rvalue_t{}));
dwarf_printf("(0x%lx) Created type %p / %s for type_id %d, offset 0x%lx, size %u, in TC %p\n", id(),
(void*)indirectType.get(), indirectType->getName().c_str(), type_id(),
offset(), indirectType->getSize(), (void*)tc());
break;
default:
dwarf_printf("(0x%lx) Warning: nothing done for tag 0x%x, dwarf_tag(): 0x%x\n",
id(), tag(), (unsigned int)dwarf_tag(&e));
return false;
}
dwarf_printf("(0x%lx) end parseTypeReferences\n", id());
return indirectType ? true : false;
}
bool DwarfWalker::hasDeclaration(bool &isDecl) {
Dwarf_Die e = entry();
isDecl = dwarf_hasattr(&e, DW_AT_declaration);
return true;
}
bool DwarfWalker::findTag() {
Dwarf_Die e = entry();
Dwarf_Half dieTag = dwarf_tag(&e);
setTag(dieTag);
return true;
}
bool DwarfWalker::handleAbstractOrigin(bool &isAbstract) {
Dwarf_Die absE, e;
e = entry();
dwarf_printf("(0x%lx) Checking for abstract origin\n", id());
isAbstract = false;
// check if current DIE has abstract origin
bool hasAbstractOrigin;
hasAbstractOrigin = dwarf_hasattr(&e, DW_AT_abstract_origin);
if (!hasAbstractOrigin) return true;
// get abstract origin attribute
Dwarf_Attribute abstractAttribute, *ret_p;
ret_p = dwarf_attr(&e, DW_AT_abstract_origin, &abstractAttribute);
if(ret_p == NULL) return false;
// check if form of attribute DW_AT_abstract_origin is DW_FORM_GNU_ref_alt
if(dwarf_whatform(&abstractAttribute)==DW_FORM_GNU_ref_alt)
return true; //handled by libdw, meaning ignoring it here
isAbstract = true;
dwarf_printf("(0x%lx) abstract_origin is true, looking up reference\n", id());
auto die_p = dwarf_formref_die(&abstractAttribute, &absE);
if(!die_p) return false;
setAbstractEntry(absE);
return true;
}
bool DwarfWalker::handleSpecification(bool &hasSpec) {
Dwarf_Die specE, e;
e = entry();
dwarf_printf("(0x%lx) Checking for separate specification\n", id());
hasSpec = false;
bool hasSpecification = dwarf_hasattr_integrate(&e, DW_AT_specification);
if (!hasSpecification) return true;
hasSpec = true;
dwarf_printf("(0x%lx) Entry has separate specification, retrieving\n", id());
Dwarf_Attribute specAttribute, *ret_p;
ret_p = dwarf_attr_integrate(&e, DW_AT_specification, &specAttribute);
if(ret_p == NULL) return false;
auto die_p = dwarf_formref_die(&specAttribute, &specE);
if(!die_p) return false;
setSpecEntry(specE);
return true;
}
std::string DwarfWalker::die_name() {
auto name = DwarfDyninst::die_name(specEntry());
dwarf_printf("(0x%lx) Found name %s.\n", id(), name.c_str());
return name;
}
bool DwarfWalker::findFuncName() {
dwarf_printf("(0x%lx) Checking for function name\n", id());
Dwarf_Die e = entry();
// Does this function have a linkage name?
{
Dwarf_Attribute linkageNameAttr{};
Dwarf_Attribute *attr = dwarf_attr_integrate(&e, DW_AT_linkage_name, &linkageNameAttr);
if (attr) {
char const* dwarfName = dwarf_formstring(attr);
if(!dwarfName) {
dwarf_printf("(0x%lx) Found 'DW_AT_linkage_name', but formstring is empty\n", id());
return false;
}
curName() = dwarfName;
setMangledName(true);
dwarf_printf("(0x%lx) Found DW_AT_linkage_name of %s\n", id(), curName().c_str());
return true;
}
}
// Is this an inlined function?
{
if(dwarf_hasattr(&e, DW_AT_inline)) {
// Find the 'DW_AT_name' for this DIE. Do not traverse this as an abstract
// instance root (if it is one)- i.e., don't use dwarf_attr_integrate here.
Dwarf_Attribute nameAttr{};
Dwarf_Attribute *res = dwarf_attr(&e, DW_AT_name, &nameAttr);
if(!res) {
dwarf_printf("(0x%lx) Found an inlined subroutine, but has no 'DW_AT_name'\n", id());
return false;
}
char const* dwarfName = dwarf_formstring(&nameAttr);
if(!dwarfName) {
dwarf_printf("(0x%lx) Found an inlined subroutine, but formstring is empty\n", id());
return false;
}
curName() = dwarfName;
// Section 2.15 of the DWARF5 spec suggests that DW_AT_name should be the name as it
// appears in the source- not mangled.
setMangledName(false);
dwarf_printf("(0x%lx) Found inline DW_AT_name '%s'\n", id(), curName().c_str());
return true;
}
}
// Assume the name is the unmangled name associated with the current DIE, if any
curName() = die_name();
setMangledName(false);
dwarf_printf("(0x%lx) No explicit function name found; using most-recently found name '%s'\n", id(), curName().c_str());
return true;
}
std::vector<VariableLocation>& DwarfParseActions::getFramePtrRefForInit()
{
return curFunc()->getFramePtrRefForInit();
}
// getFrameBase is a helper function to parseSubprogram
// inlined functions will return its parent's frame info
bool DwarfWalker::getFrameBase() {
dwarf_printf("(0x%lx) Checking for frame pointer information\n", id());
dyncompat::unique_lock<dyn_mutex> l(curFunc()->getFramePtrLock());
std::vector<VariableLocation> &funlocs = getFramePtrRefForInit();
if (!funlocs.empty()) {
DWARF_CHECK_RET(false);
}
if (!decodeLocationList(DW_AT_frame_base, NULL, funlocs))
return true;
dwarf_printf("(0x%lx) After frame base decode, %d entries\n", id(), (int) funlocs.size());
return true || !funlocs.empty(); // johnmc added true
}
bool DwarfWalker::getReturnType(dyncompat::shared_ptr<Type>&returnType) {
dwarf_printf("(0x%lx) In getReturnType().\n", id());
Dwarf_Attribute typeAttribute;
Dwarf_Attribute* status = 0;
bool is_info = true;
Dwarf_Die e = entry();
is_info = !dwarf_hasattr_integrate(&e, DW_TAG_type_unit);
status = dwarf_attr(&e, DW_AT_type, &typeAttribute);
if ( status == 0) {
dwarf_printf("(0x%lx) Return type is void\n", id());
return false;
}
/* There's a return type attribute. */
dwarf_printf("(0x%lx) Return type is not void\n", id());
bool ret = findAnyType( typeAttribute, is_info, returnType );
return ret;
}
// I'm not sure how the provided fieldListType is different from curEnclosure(),
// but that's the way the code was structured and it was working.
bool DwarfWalker::addFuncToContainer(dyncompat::shared_ptr<Type> returnType) {
/* Using the mangled name allows us to distinguish between overridden
functions, but confuses the tests. Since Type uses vectors
to hold field names, however, duplicate -- demangled names -- are OK. */
std::string demangledName = P_cplus_demangle( curName() );
// Strip everything left of the rightmost ':' off to get rid of the class names
// rfind finds the last occurrence of ':'; add 1 to get past it.
size_t offset = demangledName.rfind(':');
if (offset != demangledName.npos) {
demangledName.erase(0, offset+1);
}
curEnclosure()->asFieldListType().addField( demangledName, Type::make_shared<typeFunction>(
type_id(), returnType, demangledName));
return true;
}
bool DwarfWalker::isStaticStructMember(std::vector<VariableLocation> &locs, bool &isStatic) {
isStatic = false;
// if parsing a struct-member which is not a regular member (i.e. not with an offset)
if (curEnclosure()->getDataClass() == dataStructure && locs.size() == 0) {
// and it is not a constant, then it must be a static field member
isStatic = !findConstValue();
}
return true;
}
bool DwarfWalker::findType(dyncompat::shared_ptr<Type>&type, bool defaultToVoid) {
if(!tc()) return false;
// Do *not* return true unless type is actually usable.
/* Acquire the parameter's type. */
Dwarf_Attribute typeAttribute;
Dwarf_Die e = specEntry();
auto attr_p = dwarf_attr_integrate( &e, DW_AT_type, &typeAttribute);
if (attr_p == 0) {
if (defaultToVoid) {
type = tc()->findType("void", Type::share);
return type ? true : false;
}
return false;
}
bool is_info = !dwarf_hasattr_integrate(&e, DW_TAG_type_unit);
bool ret = findAnyType( typeAttribute, is_info, type );
return ret;
}
bool DwarfWalker::findDieOffset(Dwarf_Attribute attr, Dwarf_Off &offset) {
Dwarf_Half form = dwarf_whatform(&attr);
switch (form) {
/* These forms are suitable as direct DIE offsets */
case DW_FORM_ref1:
case DW_FORM_ref2:
case DW_FORM_ref4:
case DW_FORM_ref8:
case DW_FORM_ref_udata:
case DW_FORM_ref_addr:
case DW_FORM_data4:
case DW_FORM_data8:
case DW_FORM_GNU_ref_alt:
{
//TODO debug to compare values of offset
Dwarf_Die die;
auto ret_p = dwarf_formref_die(&attr, &die);
if(!ret_p) return false;
offset = dwarf_dieoffset(&die);
return true;
}
/* Then there's DW_FORM_sec_offset which refer other sections, or
* DW_FORM_GNU_ref_alt that refers to a whole different file. We can't
* use such forms as a die offset, even if dwarf_global_formref is
* willing to decode it. */
default:
dwarf_printf("(0x%lx) error Can't use form 0x%x as a die offset\n", id(), (int) form);
return false;
}
}
bool DwarfWalker::findAnyType(Dwarf_Attribute typeAttribute,
bool is_info, dyncompat::shared_ptr<Type>&type)
{
/* If this is a ref_sig8, look for the type elsewhere. */
Dwarf_Half form = dwarf_whatform(&typeAttribute);
if (form == DW_FORM_ref_sig8) {
Dwarf_Sig8 sig8;
const char * sig = dwarf_formstring(&typeAttribute);
if(!sig) return false;
memcpy(sig8.signature, sig, 8);
return findSig8Type(&sig8, type);
}
Dwarf_Off typeOffset;
if (!findDieOffset( typeAttribute, typeOffset)) return false;
/* NB: It's possible for an incomplete type to have a DW_AT_signature
* reference to a complete definition. For example, GCC may output just the
* subprograms for a struct's methods in one CU, with the full struct
* defined in a type unit.
*
* No DW_AT_type has been found referencing an incomplete type this way,
* but if it did, here's the place to look for that DW_AT_signature and
* recurse into findAnyType again.
*/
Dwarf_Die dieType;
auto ret_p = dwarf_formref_die(&typeAttribute, &dieType);
if(!ret_p) return false;
Dwarf * desc = dwarf_cu_getdwarf(dieType.cu);
bool is_sup = dbg()!=desc;
typeId_t type_id = get_type_id(typeOffset, is_info, is_sup);
dwarf_printf("(0x%lx) type offset 0x%lx, is_supplemental=%s\n", id(), (unsigned long)typeOffset, is_sup?"true":"false");
/* The typeOffset forms a module-unique type identifier,
so the Type look-ups by it rather than name. */
type = tc()->findOrCreateType( type_id, Type::share );
// parse this referenced by type_id die type in case it hasn't yet
if(type->getDataClass()==dataUnknownType){
dwarf_printf("(0x%lx) type not parsed yet, calling parse_int() \n", id());
// call parse_int creating new context by context_dissociation = true;
parse_int(dieType, false, true);
}
dwarf_printf("(0x%lx) type pointer %p / name:%s, type_id %d, tc():%p, mod: %s, specificType:%s\n",
id(), (void*)type.get(), type->getName().c_str(), type_id, (void*)tc(), mod()->fileName().c_str(), type->specificType().c_str());
return true;
}
bool DwarfWalker::getLineInformation(Dwarf_Word &variableLineNo,
bool &hasLineNumber,
std::string &fileName)
{
Dwarf_Attribute fileDeclAttribute;
Dwarf_Die specE = specEntry();
auto status = dwarf_attr(&specE, DW_AT_decl_file, &fileDeclAttribute);
if (status != 0) {
StringTablePtr files = srcFiles();
dyncompat::unique_lock<dyn_mutex> l(files->lock);
Dwarf_Word fileNameDeclVal;
DWARF_FAIL_RET(dwarf_formudata(&fileDeclAttribute, &fileNameDeclVal));
if (fileNameDeclVal >= files->size() || fileNameDeclVal <= 0) {
dwarf_printf("Dwarf error reading line index %lu from srcFiles of size %lu\n",
fileNameDeclVal, files->size());
return false;
}
fileName = (*files)[fileNameDeclVal].str;
}
else {
return true;
}
Dwarf_Attribute lineNoAttribute;
status = dwarf_attr(&specE, DW_AT_decl_line, &lineNoAttribute);
if (status == 0) return true;
/* We don't need to tell Dyninst a line number for C++ static variables,
so it's OK if there isn't one. */
hasLineNumber = true;
DWARF_FAIL_RET(dwarf_formudata(&lineNoAttribute, &variableLineNo));
return true;
}
bool DwarfWalker::decodeLocationList(Dwarf_Half attr,
Address *initialStackValue,
std::vector<VariableLocation> &locs)
{
Dwarf_Die e = entry();
if (!dwarf_hasattr(&e, attr)) {
dwarf_printf("(0x%lx): no such attribute 0x%x\n", id(), attr);
return true;
}
locs.clear();
/* Acquire the location of this formal parameter. */
Dwarf_Attribute locationAttribute;
auto ret_p = dwarf_attr(&e, attr, &locationAttribute);
if(!ret_p)
return false;
bool isExpr = false;
bool isConstant = false;
Dwarf_Half form;
if (!checkForConstantOrExpr(attr, locationAttribute, isConstant, isExpr, form))
return false;
dwarf_printf("(0x%lx) After checkForConstantOrExpr, form class is 0x%x\n",id(), form);
if (isConstant) {
dwarf_printf("(0x%lx) Decoding constant location\n", id());
if (!decodeConstantLocation(locationAttribute, form, locs))
return false;
}
else if (isExpr) {
dwarf_printf("(0x%lx) Decoding expression without location list\n", id());
if (!decodeExpression(locationAttribute, locs))
return false;
}
else {
dwarf_printf("(0x%lx) Decoding loclist location\n", id());
Dwarf_Op * exprs = NULL;
size_t exprlen = 0;
std::vector<LocDesc> locDescs;
ptrdiff_t offset = 0;
Dwarf_Addr basep, start, end;
do {
offset = dwarf_getlocations(&locationAttribute, offset, &basep,
&start, &end, &exprs, &exprlen);
if(offset==-1){
dwarf_printf("err message: %s\n", dwarf_errmsg(dwarf_errno()));
return false;
}
if(offset==0) break;
LocDesc ld;
ld.ld_lopc = start;
ld.ld_hipc = end;
ld.dwarfOp = exprs;
ld.opLen = exprlen;
locDescs.push_back(ld);
}while(offset > 0);
if (locDescs.size() <= 0) {
dwarf_printf("(0x%lx) Failed loclist decode\n", id());
return true;
}
if(!decodeLocationListForStaticOffsetOrAddress(locDescs, locDescs.size(),
locs, initialStackValue))
{
return false;
}
}
return true;
}
bool DwarfWalker::checkForConstantOrExpr(Dwarf_Half /*attr*/,
Dwarf_Attribute &locationAttribute,
bool &constant,
bool & expr,
Dwarf_Half &form)
{
constant = false;
// Get the form (datatype) for this particular attribute
form = dwarf_whatform(&locationAttribute);
constant = dwarf_hasform(&locationAttribute, DW_FORM_data1);
expr = dwarf_hasform(&locationAttribute, DW_FORM_exprloc);
return true;
}
dyncompat::optional<std::string> DwarfWalker::find_call_file() {
Dwarf_Die e = entry();
unsigned long line_index;
bool result = findConstant(DW_AT_call_file, line_index, &e, dbg());
if (!result)
return {};
StringTablePtr strs = mod()->getStrings();
dyncompat::unique_lock<dyn_mutex> l(strs->lock);
if (line_index >= strs->size()) {
dwarf_printf("Dwarf error reading line index %lu from srcFiles(%p) of size %lu\n",
line_index, (void*)strs.get(), strs->size());
return {};
}
return (*srcFiles())[line_index].str;
}
bool DwarfWalker::findConstant(Dwarf_Half attr, Address &value, Dwarf_Die *entry, Dwarf * /*dbg*/) {
bool has = dwarf_hasattr(entry, attr);
if (!has) return false;
// Get the attribute
Dwarf_Attribute d_attr;
auto ret_p = dwarf_attr(entry, attr, &d_attr);
if(!ret_p) return false;
// Get the form (datatype) for this particular attribute
Dwarf_Half form = dwarf_whatform(&d_attr);
bool ret = findConstantWithForm(d_attr, form, value);
return ret;
}
bool DwarfWalker::findConstantWithForm(Dwarf_Attribute &locationAttribute,
Dwarf_Half form, Address &value)
{
value = 0;
switch(form) {
case DW_FORM_addr:
Dwarf_Addr addr;
DWARF_FAIL_RET(dwarf_formaddr(&locationAttribute, &addr));
value = (Address) addr;
return true;
case DW_FORM_sdata:
Dwarf_Sword s_tmp;
DWARF_FAIL_RET(dwarf_formsdata(&locationAttribute, &s_tmp));
value = (Address) s_tmp;
dwarf_printf("Decoded data of form %x to 0x%lx\n",
form, value);
return true;
case DW_FORM_data1:
case DW_FORM_data2:
case DW_FORM_data4:
case DW_FORM_data8:
case DW_FORM_udata:
case DW_FORM_sec_offset:
Dwarf_Word u_tmp;
DWARF_FAIL_RET(dwarf_formudata(&locationAttribute, &u_tmp));
value = (Address) u_tmp;
return true;
default:
dwarf_printf("Warning: Unhandled form 0x%x for constant decode\n", (unsigned) form);
return false;
}
}
bool DwarfWalker::decodeConstantLocation(Dwarf_Attribute &attr, Dwarf_Half form,
std::vector<VariableLocation> &locs) {
Address value;
if (!findConstantWithForm(attr, form, value)) return false;
if (!constructConstantVariableLocation(value, locs)) return false;
return true;
}
bool DwarfWalker::constructConstantVariableLocation(Address value,
std::vector<VariableLocation> &locs) {
VariableLocation loc;
loc.stClass = storageAddr;
loc.refClass = storageNoRef;
loc.frameOffset = value;
if (hasRanges()) {
for (range_set_t::iterator i = ranges_begin(); i != ranges_end(); i++) {
pair<Address, Address> range = *i;
loc.lowPC = range.first;
loc.hiPC = range.second;
locs.push_back(loc);
}
}
else {
loc.lowPC = (Address) 0;
loc.hiPC = (Address) -1;
locs.push_back(loc);
}
return true;
}
bool DwarfWalker::findSize(unsigned &size) {
bool hasSize;
Dwarf_Die e = entry();
if(!e.addr) return false;
hasSize = dwarf_hasattr(&e, DW_AT_byte_size);
if (!hasSize) return false;
Dwarf_Attribute byteSizeAttr;
Dwarf_Word byteSize;
e = specEntry();
auto ret_p = dwarf_attr(&e, DW_AT_byte_size, &byteSizeAttr);
if(!ret_p) return false;
DWARF_FAIL_RET(dwarf_formudata(&byteSizeAttr, &byteSize));
size = (unsigned) byteSize;
return true;
}
bool DwarfWalker::findVisibility(visibility_t &visibility) {
/* Acquire the visibility, if any. DWARF calls it accessibility
to distinguish it from symbol table visibility. */
Dwarf_Attribute visAttr;
Dwarf_Die e = entry();
auto status = dwarf_attr(&e, DW_AT_accessibility, &visAttr);
if (status != 0) {
visibility = visPrivate;
return true;
}
Dwarf_Word visValue;
dwarf_formudata( &visAttr, &visValue);
switch( visValue ) {
case DW_ACCESS_public: visibility = visPublic; break;
case DW_ACCESS_protected: visibility = visProtected; break;
case DW_ACCESS_private: visibility = visPrivate; break;
default:
break;
}
return true;
}
dyncompat::optional<long> DwarfWalker::findConstValue() {
dwarf_printf("(0x%lx) findConstValue entry\n", id());
Dwarf_Attribute valueAttr{};
Dwarf_Die e = entry();
// This also applies to constexpr objects (i.e., DW_AT_const_expr)
if(!dwarf_attr(&e, DW_AT_const_value, & valueAttr)) {
dwarf_printf("No const value found\n");
return {};
}
Dwarf_Sword enumValue;
if(dwarf_formsdata(&valueAttr, &enumValue) != 0) {
dwarf_printf("ERROR: dwarf_formsdata error for entry %p\n", static_cast<void*>(&e));
return {};
}
dwarf_printf("Found value '%ld'\n", static_cast<long>(enumValue));
dwarf_printf("(0x%lx) end findConstValue\n", id());
return enumValue;
}
bool DwarfWalker::fixBitFields(std::vector<VariableLocation> &locs,
long &size) {
Dwarf_Attribute bitOffset;
Dwarf_Die e(entry());
auto status = dwarf_attr( &e, DW_AT_bit_offset, & bitOffset);
if ( status != 0 && locs.size() )
{
Dwarf_Word memberOffset_du = locs[0].frameOffset;
DWARF_FAIL_RET(dwarf_formudata( &bitOffset, &memberOffset_du));
Dwarf_Attribute bitSize;
auto ret_p = dwarf_attr( &e, DW_AT_bit_size, & bitSize);
if(!ret_p) return false;
Dwarf_Word memberSize_du = size;
DWARF_FAIL_RET(dwarf_formudata( &bitSize, &memberSize_du));
/* If the DW_AT_byte_size field exists, there's some padding.
FIXME? We ignore padding for now. (We also don't seem to handle
bitfields right in getComponents() anyway...) */
}
else
{
if (locs.size())
locs[0].frameOffset *= 8;
size *= 8;
}
return true;
}
bool DwarfWalker::fixName(std::string &name, dyncompat::shared_ptr<Type> type) {
switch(tag()){
case DW_TAG_const_type:
name = std::string("const ") + type->getName();
break;
case DW_TAG_packed_type:
name = std::string("packed ") + type->getName();
break;
case DW_TAG_volatile_type:
name = std::string("volatile ") + type->getName();
break;
default:
return false;
}
return true;
}
void DwarfWalker::removeFortranUnderscore(std::string &name) {
/* If we're fortran, get rid of the trailing _ */
if (!curFunc() && !curEnclosure()) return;
supportedLanguages lang = mod()->language();
if ((lang != lang_Fortran) &&
(lang != lang_CMFortran)) return;
if (name[name.length()-1] == '_') {
name = name.substr(0, name.length()-1);
}
}
dyncompat::shared_ptr<typeSubrange> DwarfWalker::parseSubrange(Dwarf_Die *entry) {
const auto subrange_id = dwarf_dieoffset(entry) - this->compile_offset;
dwarf_printf("(0x%lx) parseSubrange entry for <0x%lx>\n", id(), subrange_id);
dyncompat::optional<Dwarf_Word> upper_bound, lower_bound;
/* An array can have DW_TAG_subrange_type or DW_TAG_enumeration_type
children instead that give the size of each dimension. */
switch (dwarf_tag(entry)) {
case DW_TAG_subrange_type: {
namespace dw = Dyninst::DwarfDyninst;
auto bounds = dwarf_subrange_bounds(entry);
if (!bounds.lower || !bounds.upper) {
dwarf_printf("parseSubrange failed, error finding range bounds\n");
return nullptr;
}
if (!bounds.lower.value) {
// dwarf_subrange_bounds will try dwarf_default_lower_bound for the
// current DIE. If we got here, that didn't work, so try using the one
// Dyninst parsed.
switch (mod()->language()) {
case lang_Fortran:
case lang_CMFortran:
bounds.lower.value = 1;
break;
default:
// Assume all non-Fortran languages use 0
bounds.lower.value = 0;
break;
}
}
upper_bound = bounds.upper.value;
lower_bound = bounds.lower.value;
} break;
case DW_TAG_enumeration_type: {
// If there is an enum value, then it represents the total number of
// elements in the array. We take the bounds to be [0, upper-1)
auto upper = dwarf_subrange_length_from_enum(entry);
if (!upper) {
dwarf_printf("parseSubrange failed, error finding length from enum\n");
return nullptr;
}
if (upper) {
upper.value.get() -= 1;
}
lower_bound = 0;
upper_bound = upper.value;
}
}
// Don't set the name until we're guaranteed a subrange was found
curName() = die_name();
if (!nameDefined()) {
curName() = "{anonymousRange}";
}
Dwarf_Off subrangeOffset = dwarf_dieoffset(entry);
bool is_info = !dwarf_hasattr_integrate(entry, DW_TAG_type_unit);
typeId_t type_id = get_type_id(subrangeOffset, is_info, false);
// `typeSubrange` expects numeric values for the bounds
DYNINST_DIAGNOSTIC_BEGIN_SUPPRESS_MAYBE_UNINITIALIZED
auto range = Type::make_shared<typeSubrange>(
type_id, 0, lower_bound.value_or(LONG_MIN),
upper_bound.value_or(LONG_MAX), curName());
DYNINST_DIAGNOSTIC_END_SUPPRESS_MAYBE_UNINITIALIZED
dwarf_printf(
"(0x%lx) Parsed subrange: id %d, low %lu, high %lu, named %s\n",
id(), type_id, range->getLow(), range->getHigh(), curName().c_str());
return range;
}
bool DwarfWalker::decodeExpression(Dwarf_Attribute &locationAttribute,
std::vector<VariableLocation> &locs)
{
Dwarf_Op * exprs = NULL;
size_t exprlen = 0;
std::vector<LocDesc> locDescs;
ptrdiff_t offset = 0;
Dwarf_Addr basep, start, end;
do {
offset = dwarf_getlocations(&locationAttribute, offset, &basep,
&start, &end, &exprs, &exprlen);
if(offset==-1) return false;
LocDesc ld;
ld.ld_lopc = start;
ld.ld_hipc = end;
ld.dwarfOp = exprs;
ld.opLen = exprlen;
locDescs.push_back(ld);
}while(offset > 0);
bool ret = decodeLocationListForStaticOffsetOrAddress(locDescs, 1, locs);
return ret;
}
bool DwarfWalker::decodeLocationListForStaticOffsetOrAddress(
std::vector<LocDesc>& locationList,
Dwarf_Sword listLength,
std::vector<VariableLocation>& locs,
Address * initialStackValue)
{
locs.clear();
/* We make a few heroic assumptions about locations in this decoder.
We assume that all locations are either frame base-relative offsets,
encoded with DW_OP_fbreg, or are absolute addresses. We assume these
locations are invariant with respect to the PC, which implies that all
location lists have a single entry. We assume that no location is
calculated at run-time.
We make these assumptions to match the assumptions of the rest of
Dyninst, which makes no provision for pc-variant or run-time calculated
locations, aside from the frame pointer. However, it assumes that a frame
pointer is readily available, which, on IA-64, it is not. For that reason,
when we encounter a function with a DW_AT_frame_base (effectively all of them),
we do NOT use this decoder; we decode the location into an AST, which we
will use to calculate the frame pointer when asked to do frame-relative operations.
(These calculations will be invalid until the frame pointer is established,
which may require some fiddling with the location of the 'entry' instpoint.) */
/* We now parse the complete location list for variables and parameters within a
* function. We still ignore the location list defined for DW_AT_frame_base of the
* function as the frame pointer is readily available on all platforms(except for IA64)
* May be we would need to parse the location list for IA64 functions to store the
* register numbers and offsets and use it based on the pc value.
*/
uint64_t max_addr = (addr_size == 4) ? 0xffffffff : 0xffffffffffffffff;
Address base = modLow;
for (unsigned locIndex = 0 ; locIndex < listLength; locIndex++) {
/* There is only one location. */
LocDesc * location = &locationList[locIndex];
// If location == 0..-1, it's "unset" and we keep the big range unless
// we're in a lexical block construct.
//
dwarf_printf("(0x%lx) Decoding entry %u of %d over range 0x%lx - 0x%lx, mod 0x%lx - 0x%lx, base 0x%lx\n",
id(), locIndex+1, (int) listLength,
location->ld_lopc,
location->ld_hipc,
modLow, modHigh, base);
if (location->ld_lopc == max_addr) {
//This is a base address selection entry, which changes the base address of
// subsequent entries
base = location->ld_hipc;
continue;
}
VariableLocation loc;
// Initialize location values.
loc.stClass = storageAddr;
loc.refClass = storageNoRef;
long int *tmp = (long int *)initialStackValue;
bool result = decodeDwarfExpression(location->dwarfOp, location->opLen, tmp, loc,
symtab()->getArchitecture());
if (!result) {
dwarf_printf("(0x%lx): dwarf expression not decoded\n", id());
continue;
}
if (location->ld_lopc == 0 &&
location->ld_hipc == (Dwarf_Addr) ~0) {
// Unset low and high. Use the lexical block info if present, otherwise
// pass through.
if (hasRanges()) {
dwarf_printf("(0x%lx) Using lexical range\n", id());
for (range_set_t::iterator i = ranges_begin(); i != ranges_end(); i++) {
pair<Address, Address> range = *i;
loc.lowPC = range.first;
loc.hiPC = range.second;
dwarf_printf("(0x%lx) valid over range 0x%lx to 0x%lx\n",
id(), loc.lowPC, loc.hiPC);
locs.push_back(loc);
}
}
else {
dwarf_printf("(0x%lx) Using open location range\n", id());
loc.lowPC = location->ld_lopc;
loc.hiPC = location->ld_hipc;
dwarf_printf("(0x%lx) valid over range 0x%lx to 0x%lx\n",
id(), loc.lowPC, loc.hiPC);
locs.push_back(loc);
}
}
else {
dwarf_printf("(0x%lx) Using lexical range\n", id());
loc.lowPC = location->ld_lopc;
loc.hiPC = location->ld_hipc;
dwarf_printf("(0x%lx) valid over range 0x%lx to 0x%lx\n",
id(), loc.lowPC, loc.hiPC);
locs.push_back(loc);
}
}
/* decode successful */
return !locs.empty();
}
void DwarfWalker::setEntry(Dwarf_Die entry) {
DwarfParseActions::setEntry(entry);
DwarfParseActions::setSpecEntry(entry);
DwarfParseActions::setAbstractEntry(entry);
}
void DwarfParseActions::push(bool dissociate_context) {
if (c.empty() || dissociate_context) {
c.push(Context());
}
else {
c.push(Context(c.top()));
}
}
void DwarfParseActions::pop() {
if(!c.empty())
{
c.pop();
}
}
void DwarfParseActions::setFunc(FunctionBase *f) {
// Bug workaround; if we're setting a function, ignore
// any preceding lexical information since we probably
// nested.
c.top().func = f;
}
void DwarfParseActions::clearFunc() {
// We can't edit in the middle of the stack...
std::stack<Context> repl;
while (!c.empty()) {
repl.push(c.top());
c.pop();
}
while (!repl.empty()) {
c.push(repl.top());
c.top().func = NULL;
repl.pop();
}
}
unsigned int DwarfWalker::getNextTypeId(){
static dyncompat::atomic<unsigned int> next_type_id(1);
unsigned int val = next_type_id.fetch_add(1);
return val;
}
typeId_t DwarfWalker::get_type_id(Dwarf_Off offset, bool is_info, bool is_sup)
{
auto& type_ids = is_info ? info_type_ids_ : types_type_ids_;
typeId_t type_id = 0;
{
type_map::const_accessor a;
if(type_ids.find(a, {offset,is_sup,mod()})) type_id = a->second;
}
if(type_id) return type_id;
unsigned int val = getNextTypeId();
{
type_map::accessor a;
type_key tk{offset, is_sup, mod()};
type_ids.insert(a, make_pair(tk, val));
dwarf_printf("(0x%lx) type_id %u, key created {0x%lx,%s,mod: %s}\n", id(),val,offset,is_sup?"sup":"not sup", mod()->fileName().c_str());
}
return val;
}
typeId_t DwarfWalker::type_id()
{
Dwarf_Die e(entry());
bool is_info = !dwarf_hasattr_integrate(&e, DW_TAG_type_unit);
Dwarf * desc = dwarf_cu_getdwarf(e.cu);
return get_type_id(offset(), is_info, dbg()!=desc);
}
void DwarfWalker::findAllSig8Types()
{
/* First .debug_types (0), then .debug_info (1).
* In DWARF4, only .debug_types contains DW_TAG_type_unit,
* but DWARF5 is considering them for .debug_info too.*/
compile_offset = next_cu_header = 0;
/* Iterate over the compilation-unit headers for .debug_types. */
uint64_t type_signaturep;
for(Dwarf_Off cu_off = 0;
dwarf_next_unit(dbg(), cu_off, &next_cu_header, &cu_header_length,
NULL, &abbrev_offset, &addr_size, &offset_size,
&type_signaturep, NULL) == 0;
cu_off = next_cu_header)
{
if(!dwarf_offdie_types(dbg(), cu_off + cu_header_length, &current_cu_die))
continue;
if (DwarfDyninst::is_partial_unit(current_cu_die)) {
continue;
}
parseModuleSig8(false);
compile_offset = next_cu_header;
}
/* Iterate over the compilation-unit headers for .debug_info. */
for(Dwarf_Off cu_off = 0;
dwarf_nextcu(dbg(), cu_off, &next_cu_header, &cu_header_length,
&abbrev_offset, &addr_size, &offset_size) == 0;
cu_off = next_cu_header)
{
if(!dwarf_offdie(dbg(), cu_off + cu_header_length, &current_cu_die))
continue;
if (DwarfDyninst::is_partial_unit(current_cu_die)) {
continue;
}
parseModuleSig8(true);
compile_offset = next_cu_header;
}
}
bool DwarfWalker::parseModuleSig8(bool is_info)
{
if (!DwarfDyninst::is_type_unit(current_cu_die))
return false;
uint64_t sig8 = * reinterpret_cast<uint64_t*>(&signature);
typeId_t type_id = get_type_id(typeoffset, is_info, false);
{
dyn_c_hash_map<uint64_t, typeId_t>::accessor a;
sig8_type_ids_.insert(a, std::make_pair(sig8, type_id));
}
dwarf_printf("Mapped Sig8 {%016llx} to type id 0x%x\n", (unsigned long long) sig8, (unsigned int)type_id);
return true;
}
bool DwarfWalker::findSig8Type(Dwarf_Sig8 * s, dyncompat::shared_ptr<Type>&returnType)
{
uint64_t sig8 = * reinterpret_cast<uint64_t*>(s);
typeId_t type_id = 0;
{
dyn_c_hash_map<uint64_t, typeId_t>::const_accessor a;
if(sig8_type_ids_.find(a, sig8)) type_id = a->second;
}
if(type_id){
returnType = tc()->findOrCreateType(type_id, Type::share);
dwarf_printf("Found Sig8 {%016llx} as type id 0x%x\n", (unsigned long long) sig8, (unsigned int)type_id);
return true;
}
dwarf_printf("Couldn't find Sig8 {%016llx}!\n", (unsigned long long) sig8);
return false;
}
Object *DwarfParseActions::obj() const {
return symtab()->getObject();
}
Offset DwarfParseActions::convertDebugOffset(Offset from) {
Offset to;
obj()->convertDebugOffset(from, to);
return to;
}
void DwarfWalker::setFuncReturnType() {
dwarf_printf("(0x%lx) In setFuncReturnType().\n", id());
dyncompat::shared_ptr<Type> returnType;
getReturnType(returnType);
if (returnType)
curFunc()->setReturnType(returnType);
}