2522 lines
62 KiB
C
2522 lines
62 KiB
C
/*
|
|
SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com>
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <dwarf.h>
|
|
#include <elfutils/libdwfl.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <fnmatch.h>
|
|
#include <libelf.h>
|
|
#include <obstack.h>
|
|
#include <search.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "config.h"
|
|
#include "list.h"
|
|
#include "dwarves.h"
|
|
#include "dutil.h"
|
|
#include "strings.h"
|
|
#include "hash.h"
|
|
|
|
struct strings *strings;
|
|
|
|
#ifndef DW_AT_GNU_vector
|
|
#define DW_AT_GNU_vector 0x2107
|
|
#endif
|
|
|
|
#ifndef DW_TAG_GNU_call_site
|
|
#define DW_TAG_GNU_call_site 0x4109
|
|
#define DW_TAG_GNU_call_site_parameter 0x410a
|
|
#endif
|
|
|
|
#define hashtags__fn(key) hash_64(key, HASHTAGS__BITS)
|
|
|
|
bool no_bitfield_type_recode = true;
|
|
|
|
static void __tag__print_not_supported(uint32_t tag, const char *func)
|
|
{
|
|
static bool dwarf_tags_warned[DW_TAG_GNU_call_site_parameter + 64];
|
|
|
|
if (tag < sizeof(dwarf_tags_warned)) {
|
|
if (dwarf_tags_warned[tag])
|
|
return;
|
|
dwarf_tags_warned[tag] = true;
|
|
}
|
|
|
|
fprintf(stderr, "%s: tag not supported %#x (%s)!\n", func,
|
|
tag, dwarf_tag_name(tag));
|
|
}
|
|
|
|
#define tag__print_not_supported(tag) \
|
|
__tag__print_not_supported(tag, __func__)
|
|
|
|
struct dwarf_off_ref {
|
|
unsigned int from_types : 1;
|
|
Dwarf_Off off;
|
|
};
|
|
|
|
typedef struct dwarf_off_ref dwarf_off_ref;
|
|
|
|
struct dwarf_tag {
|
|
struct hlist_node hash_node;
|
|
dwarf_off_ref type;
|
|
Dwarf_Off id;
|
|
union {
|
|
dwarf_off_ref abstract_origin;
|
|
dwarf_off_ref containing_type;
|
|
};
|
|
struct tag *tag;
|
|
uint32_t small_id;
|
|
strings_t decl_file;
|
|
uint16_t decl_line;
|
|
};
|
|
|
|
static dwarf_off_ref dwarf_tag__spec(struct dwarf_tag *dtag)
|
|
{
|
|
return *(dwarf_off_ref *)(dtag + 1);
|
|
}
|
|
|
|
static void dwarf_tag__set_spec(struct dwarf_tag *dtag, dwarf_off_ref spec)
|
|
{
|
|
*(dwarf_off_ref *)(dtag + 1) = spec;
|
|
}
|
|
|
|
#define HASHTAGS__BITS 8
|
|
#define HASHTAGS__SIZE (1UL << HASHTAGS__BITS)
|
|
|
|
#define obstack_chunk_alloc malloc
|
|
#define obstack_chunk_free free
|
|
|
|
static void *obstack_zalloc(struct obstack *obstack, size_t size)
|
|
{
|
|
void *o = obstack_alloc(obstack, size);
|
|
|
|
if (o)
|
|
memset(o, 0, size);
|
|
return o;
|
|
}
|
|
|
|
struct dwarf_cu {
|
|
struct hlist_head hash_tags[HASHTAGS__SIZE];
|
|
struct hlist_head hash_types[HASHTAGS__SIZE];
|
|
struct obstack obstack;
|
|
struct cu *cu;
|
|
struct dwarf_cu *type_unit;
|
|
};
|
|
|
|
static void dwarf_cu__init(struct dwarf_cu *dcu)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < HASHTAGS__SIZE; ++i) {
|
|
INIT_HLIST_HEAD(&dcu->hash_tags[i]);
|
|
INIT_HLIST_HEAD(&dcu->hash_types[i]);
|
|
}
|
|
obstack_init(&dcu->obstack);
|
|
dcu->type_unit = NULL;
|
|
}
|
|
|
|
static void hashtags__hash(struct hlist_head *hashtable,
|
|
struct dwarf_tag *dtag)
|
|
{
|
|
struct hlist_head *head = hashtable + hashtags__fn(dtag->id);
|
|
hlist_add_head(&dtag->hash_node, head);
|
|
}
|
|
|
|
static struct dwarf_tag *hashtags__find(const struct hlist_head *hashtable,
|
|
const Dwarf_Off id)
|
|
{
|
|
if (id == 0)
|
|
return NULL;
|
|
|
|
struct dwarf_tag *tpos;
|
|
struct hlist_node *pos;
|
|
uint16_t bucket = hashtags__fn(id);
|
|
const struct hlist_head *head = hashtable + bucket;
|
|
|
|
hlist_for_each_entry(tpos, pos, head, hash_node) {
|
|
if (tpos->id == id)
|
|
return tpos;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void cu__hash(struct cu *cu, struct tag *tag)
|
|
{
|
|
struct dwarf_cu *dcu = cu->priv;
|
|
struct hlist_head *hashtable = tag__is_tag_type(tag) ?
|
|
dcu->hash_types :
|
|
dcu->hash_tags;
|
|
hashtags__hash(hashtable, tag->priv);
|
|
}
|
|
|
|
static struct dwarf_tag *dwarf_cu__find_tag_by_ref(const struct dwarf_cu *cu,
|
|
const struct dwarf_off_ref *ref)
|
|
{
|
|
if (cu == NULL)
|
|
return NULL;
|
|
if (ref->from_types) {
|
|
return NULL;
|
|
}
|
|
return hashtags__find(cu->hash_tags, ref->off);
|
|
}
|
|
|
|
static struct dwarf_tag *dwarf_cu__find_type_by_ref(const struct dwarf_cu *dcu,
|
|
const struct dwarf_off_ref *ref)
|
|
{
|
|
if (dcu == NULL)
|
|
return NULL;
|
|
if (ref->from_types) {
|
|
dcu = dcu->type_unit;
|
|
if (dcu == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
return hashtags__find(dcu->hash_types, ref->off);
|
|
}
|
|
|
|
extern struct strings *strings;
|
|
|
|
static void *memdup(const void *src, size_t len, struct cu *cu)
|
|
{
|
|
void *s = obstack_alloc(&cu->obstack, len);
|
|
if (s != NULL)
|
|
memcpy(s, src, len);
|
|
return s;
|
|
}
|
|
|
|
/* Number decoding macros. See 7.6 Variable Length Data. */
|
|
|
|
#define get_uleb128_step(var, addr, nth, break) \
|
|
__b = *(addr)++; \
|
|
var |= (uintmax_t) (__b & 0x7f) << (nth * 7); \
|
|
if ((__b & 0x80) == 0) \
|
|
break
|
|
|
|
#define get_uleb128_rest_return(var, i, addrp) \
|
|
do { \
|
|
for (; i < 10; ++i) { \
|
|
get_uleb128_step(var, *addrp, i, \
|
|
return var); \
|
|
} \
|
|
/* Other implementations set VALUE to UINT_MAX in this \
|
|
case. So we better do this as well. */ \
|
|
return UINT64_MAX; \
|
|
} while (0)
|
|
|
|
static uint64_t __libdw_get_uleb128(uint64_t acc, uint32_t i,
|
|
const uint8_t **addrp)
|
|
{
|
|
uint8_t __b;
|
|
get_uleb128_rest_return (acc, i, addrp);
|
|
}
|
|
|
|
#define get_uleb128(var, addr) \
|
|
do { \
|
|
uint8_t __b; \
|
|
var = 0; \
|
|
get_uleb128_step(var, addr, 0, break); \
|
|
var = __libdw_get_uleb128 (var, 1, &(addr)); \
|
|
} while (0)
|
|
|
|
static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name)
|
|
{
|
|
Dwarf_Attribute attr;
|
|
uint32_t form;
|
|
|
|
if (dwarf_attr(die, name, &attr) == NULL)
|
|
return 0;
|
|
|
|
form = dwarf_whatform(&attr);
|
|
|
|
switch (form) {
|
|
case DW_FORM_addr: {
|
|
Dwarf_Addr addr;
|
|
if (dwarf_formaddr(&attr, &addr) == 0)
|
|
return addr;
|
|
}
|
|
break;
|
|
case DW_FORM_data1:
|
|
case DW_FORM_data2:
|
|
case DW_FORM_data4:
|
|
case DW_FORM_data8:
|
|
case DW_FORM_sdata:
|
|
case DW_FORM_udata: {
|
|
Dwarf_Word value;
|
|
if (dwarf_formudata(&attr, &value) == 0)
|
|
return value;
|
|
}
|
|
break;
|
|
case DW_FORM_flag:
|
|
case DW_FORM_flag_present: {
|
|
bool value;
|
|
if (dwarf_formflag(&attr, &value) == 0)
|
|
return value;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "DW_AT_<0x%x>=0x%x\n", name, form);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t dwarf_expr(const uint8_t *expr, uint32_t len __unused)
|
|
{
|
|
/* Common case: offset from start of the class */
|
|
if (expr[0] == DW_OP_plus_uconst ||
|
|
expr[0] == DW_OP_constu) {
|
|
uint64_t result;
|
|
++expr;
|
|
get_uleb128(result, expr);
|
|
return result;
|
|
}
|
|
|
|
fprintf(stderr, "%s: unhandled %#x DW_OP_ operation\n",
|
|
__func__, *expr);
|
|
return UINT64_MAX;
|
|
}
|
|
|
|
static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name)
|
|
{
|
|
Dwarf_Attribute attr;
|
|
Dwarf_Block block;
|
|
|
|
if (dwarf_attr(die, name, &attr) == NULL)
|
|
return 0;
|
|
|
|
switch (dwarf_whatform(&attr)) {
|
|
case DW_FORM_data1:
|
|
case DW_FORM_data2:
|
|
case DW_FORM_data4:
|
|
case DW_FORM_data8:
|
|
case DW_FORM_sdata:
|
|
case DW_FORM_udata: {
|
|
Dwarf_Word value;
|
|
if (dwarf_formudata(&attr, &value) == 0)
|
|
return value;
|
|
break;
|
|
}
|
|
default:
|
|
if (dwarf_formblock(&attr, &block) == 0)
|
|
return dwarf_expr(block.data, block.length);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *attr_string(Dwarf_Die *die, uint32_t name)
|
|
{
|
|
Dwarf_Attribute attr;
|
|
if (dwarf_attr(die, name, &attr) != NULL)
|
|
return dwarf_formstring(&attr);
|
|
return NULL;
|
|
}
|
|
|
|
static struct dwarf_off_ref attr_type(Dwarf_Die *die, uint32_t attr_name)
|
|
{
|
|
Dwarf_Attribute attr;
|
|
struct dwarf_off_ref ref;
|
|
if (dwarf_attr(die, attr_name, &attr) != NULL) {
|
|
Dwarf_Die type_die;
|
|
if (dwarf_formref_die(&attr, &type_die) != NULL) {
|
|
ref.from_types = attr.form == DW_FORM_ref_sig8;
|
|
ref.off = dwarf_dieoffset(&type_die);
|
|
return ref;
|
|
}
|
|
}
|
|
memset(&ref, 0, sizeof(ref));
|
|
return ref;
|
|
}
|
|
|
|
static int attr_location(Dwarf_Die *die, Dwarf_Op **expr, size_t *exprlen)
|
|
{
|
|
Dwarf_Attribute attr;
|
|
if (dwarf_attr(die, DW_AT_location, &attr) != NULL) {
|
|
if (dwarf_getlocation(&attr, expr, exprlen) == 0)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void *__tag__alloc(struct dwarf_cu *dcu, size_t size, bool spec)
|
|
{
|
|
struct dwarf_tag *dtag = obstack_zalloc(&dcu->obstack,
|
|
(sizeof(*dtag) +
|
|
(spec ? sizeof(dwarf_off_ref) : 0)));
|
|
if (dtag == NULL)
|
|
return NULL;
|
|
|
|
struct tag *tag = obstack_zalloc(&dcu->cu->obstack, size);
|
|
|
|
if (tag == NULL)
|
|
return NULL;
|
|
|
|
dtag->tag = tag;
|
|
tag->priv = dtag;
|
|
tag->type = 0;
|
|
tag->top_level = 0;
|
|
|
|
return tag;
|
|
}
|
|
|
|
static void *tag__alloc(struct cu *cu, size_t size)
|
|
{
|
|
return __tag__alloc(cu->priv, size, false);
|
|
}
|
|
|
|
static void *tag__alloc_with_spec(struct cu *cu, size_t size)
|
|
{
|
|
return __tag__alloc(cu->priv, size, true);
|
|
}
|
|
|
|
static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
|
|
{
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
|
|
tag->tag = dwarf_tag(die);
|
|
|
|
dtag->id = dwarf_dieoffset(die);
|
|
|
|
if (tag->tag == DW_TAG_imported_module ||
|
|
tag->tag == DW_TAG_imported_declaration)
|
|
dtag->type = attr_type(die, DW_AT_import);
|
|
else
|
|
dtag->type = attr_type(die, DW_AT_type);
|
|
|
|
dtag->abstract_origin = attr_type(die, DW_AT_abstract_origin);
|
|
tag->recursivity_level = 0;
|
|
|
|
if (cu->extra_dbg_info) {
|
|
int32_t decl_line;
|
|
const char *decl_file = dwarf_decl_file(die);
|
|
static const char *last_decl_file;
|
|
static uint32_t last_decl_file_idx;
|
|
|
|
if (decl_file != last_decl_file) {
|
|
last_decl_file_idx = strings__add(strings, decl_file);
|
|
last_decl_file = decl_file;
|
|
}
|
|
|
|
dtag->decl_file = last_decl_file_idx;
|
|
dwarf_decl_line(die, &decl_line);
|
|
dtag->decl_line = decl_line;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&tag->node);
|
|
}
|
|
|
|
static struct tag *tag__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct tag *tag = tag__alloc(cu, sizeof(*tag));
|
|
|
|
if (tag != NULL)
|
|
tag__init(tag, cu, die);
|
|
|
|
return tag;
|
|
}
|
|
|
|
static struct ptr_to_member_type *ptr_to_member_type__new(Dwarf_Die *die,
|
|
struct cu *cu)
|
|
{
|
|
struct ptr_to_member_type *ptr = tag__alloc(cu, sizeof(*ptr));
|
|
|
|
if (ptr != NULL) {
|
|
tag__init(&ptr->tag, cu, die);
|
|
struct dwarf_tag *dtag = ptr->tag.priv;
|
|
dtag->containing_type = attr_type(die, DW_AT_containing_type);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static struct base_type *base_type__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct base_type *bt = tag__alloc(cu, sizeof(*bt));
|
|
|
|
if (bt != NULL) {
|
|
tag__init(&bt->tag, cu, die);
|
|
bt->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
bt->bit_size = attr_numeric(die, DW_AT_byte_size) * 8;
|
|
uint64_t encoding = attr_numeric(die, DW_AT_encoding);
|
|
bt->is_bool = encoding == DW_ATE_boolean;
|
|
bt->is_signed = encoding == DW_ATE_signed;
|
|
bt->is_varargs = false;
|
|
bt->name_has_encoding = true;
|
|
}
|
|
|
|
return bt;
|
|
}
|
|
|
|
static struct array_type *array_type__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct array_type *at = tag__alloc(cu, sizeof(*at));
|
|
|
|
if (at != NULL) {
|
|
tag__init(&at->tag, cu, die);
|
|
at->dimensions = 0;
|
|
at->nr_entries = NULL;
|
|
at->is_vector = dwarf_hasattr(die, DW_AT_GNU_vector);
|
|
}
|
|
|
|
return at;
|
|
}
|
|
|
|
static void namespace__init(struct namespace *namespace, Dwarf_Die *die,
|
|
struct cu *cu)
|
|
{
|
|
tag__init(&namespace->tag, cu, die);
|
|
INIT_LIST_HEAD(&namespace->tags);
|
|
namespace->sname = 0;
|
|
namespace->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
namespace->nr_tags = 0;
|
|
namespace->shared_tags = 0;
|
|
}
|
|
|
|
static struct namespace *namespace__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct namespace *namespace = tag__alloc(cu, sizeof(*namespace));
|
|
|
|
if (namespace != NULL)
|
|
namespace__init(namespace, die, cu);
|
|
|
|
return namespace;
|
|
}
|
|
|
|
static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
namespace__init(&type->namespace, die, cu);
|
|
INIT_LIST_HEAD(&type->node);
|
|
type->size = attr_numeric(die, DW_AT_byte_size);
|
|
type->alignment = attr_numeric(die, DW_AT_alignment);
|
|
type->declaration = attr_numeric(die, DW_AT_declaration);
|
|
dwarf_tag__set_spec(type->namespace.tag.priv,
|
|
attr_type(die, DW_AT_specification));
|
|
type->definition_emitted = 0;
|
|
type->fwd_decl_emitted = 0;
|
|
type->resized = 0;
|
|
type->nr_members = 0;
|
|
type->nr_static_members = 0;
|
|
}
|
|
|
|
static struct type *type__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct type *type = tag__alloc_with_spec(cu, sizeof(*type));
|
|
|
|
if (type != NULL)
|
|
type__init(type, die, cu);
|
|
|
|
return type;
|
|
}
|
|
|
|
static struct enumerator *enumerator__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct enumerator *enumerator = tag__alloc(cu, sizeof(*enumerator));
|
|
|
|
if (enumerator != NULL) {
|
|
tag__init(&enumerator->tag, cu, die);
|
|
enumerator->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
enumerator->value = attr_numeric(die, DW_AT_const_value);
|
|
}
|
|
|
|
return enumerator;
|
|
}
|
|
|
|
static enum vscope dwarf__location(Dwarf_Die *die, uint64_t *addr, struct location *location)
|
|
{
|
|
enum vscope scope = VSCOPE_UNKNOWN;
|
|
|
|
if (attr_location(die, &location->expr, &location->exprlen) != 0)
|
|
scope = VSCOPE_OPTIMIZED;
|
|
else if (location->exprlen != 0) {
|
|
Dwarf_Op *expr = location->expr;
|
|
switch (expr->atom) {
|
|
case DW_OP_addr:
|
|
scope = VSCOPE_GLOBAL;
|
|
*addr = expr[0].number;
|
|
break;
|
|
case DW_OP_reg1 ... DW_OP_reg31:
|
|
case DW_OP_breg0 ... DW_OP_breg31:
|
|
scope = VSCOPE_REGISTER; break;
|
|
case DW_OP_fbreg:
|
|
scope = VSCOPE_LOCAL; break;
|
|
}
|
|
}
|
|
|
|
return scope;
|
|
}
|
|
|
|
enum vscope variable__scope(const struct variable *var)
|
|
{
|
|
return var->scope;
|
|
}
|
|
|
|
const char *variable__scope_str(const struct variable *var)
|
|
{
|
|
switch (var->scope) {
|
|
case VSCOPE_LOCAL: return "local";
|
|
case VSCOPE_GLOBAL: return "global";
|
|
case VSCOPE_REGISTER: return "register";
|
|
case VSCOPE_OPTIMIZED: return "optimized";
|
|
default: break;
|
|
};
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
static struct variable *variable__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct variable *var = tag__alloc(cu, sizeof(*var));
|
|
|
|
if (var != NULL) {
|
|
tag__init(&var->ip.tag, cu, die);
|
|
var->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
/* variable is visible outside of its enclosing cu */
|
|
var->external = dwarf_hasattr(die, DW_AT_external);
|
|
/* non-defining declaration of an object */
|
|
var->declaration = dwarf_hasattr(die, DW_AT_declaration);
|
|
var->scope = VSCOPE_UNKNOWN;
|
|
var->ip.addr = 0;
|
|
if (!var->declaration && cu->has_addr_info)
|
|
var->scope = dwarf__location(die, &var->ip.addr, &var->location);
|
|
}
|
|
|
|
return var;
|
|
}
|
|
|
|
static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t bit_size)
|
|
{
|
|
int id;
|
|
type_id_t short_id;
|
|
struct tag *recoded;
|
|
/* in all the cases the name is at the same offset */
|
|
strings_t name = tag__namespace(tag)->name;
|
|
|
|
switch (tag->tag) {
|
|
case DW_TAG_typedef: {
|
|
const struct dwarf_tag *dtag = tag->priv;
|
|
struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(cu->priv,
|
|
&dtag->type);
|
|
struct tag *type = dtype->tag;
|
|
|
|
id = tag__recode_dwarf_bitfield(type, cu, bit_size);
|
|
if (id < 0)
|
|
return id;
|
|
|
|
struct type *new_typedef = obstack_zalloc(&cu->obstack,
|
|
sizeof(*new_typedef));
|
|
if (new_typedef == NULL)
|
|
return -ENOMEM;
|
|
|
|
recoded = (struct tag *)new_typedef;
|
|
recoded->tag = DW_TAG_typedef;
|
|
recoded->type = id;
|
|
new_typedef->namespace.name = tag__namespace(tag)->name;
|
|
}
|
|
break;
|
|
|
|
case DW_TAG_const_type:
|
|
case DW_TAG_volatile_type: {
|
|
const struct dwarf_tag *dtag = tag->priv;
|
|
struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type);
|
|
struct tag *type = dtype->tag;
|
|
|
|
id = tag__recode_dwarf_bitfield(type, cu, bit_size);
|
|
if (id == tag->type)
|
|
return id;
|
|
|
|
recoded = obstack_zalloc(&cu->obstack, sizeof(*recoded));
|
|
if (recoded == NULL)
|
|
return -ENOMEM;
|
|
|
|
recoded->tag = DW_TAG_volatile_type;
|
|
recoded->type = id;
|
|
}
|
|
break;
|
|
|
|
case DW_TAG_base_type:
|
|
/*
|
|
* Here we must search on the final, core cu, not on
|
|
* the dwarf_cu as in dwarf there are no such things
|
|
* as base_types of less than 8 bits, etc.
|
|
*/
|
|
recoded = cu__find_base_type_by_sname_and_size(cu, name, bit_size, &short_id);
|
|
if (recoded != NULL)
|
|
return short_id;
|
|
|
|
struct base_type *new_bt = obstack_zalloc(&cu->obstack,
|
|
sizeof(*new_bt));
|
|
if (new_bt == NULL)
|
|
return -ENOMEM;
|
|
|
|
recoded = (struct tag *)new_bt;
|
|
recoded->tag = DW_TAG_base_type;
|
|
recoded->top_level = 1;
|
|
new_bt->name = name;
|
|
new_bt->bit_size = bit_size;
|
|
break;
|
|
|
|
case DW_TAG_enumeration_type:
|
|
/*
|
|
* Here we must search on the final, core cu, not on
|
|
* the dwarf_cu as in dwarf there are no such things
|
|
* as enumeration_types of less than 8 bits, etc.
|
|
*/
|
|
recoded = cu__find_enumeration_by_sname_and_size(cu, name, bit_size, &short_id);
|
|
if (recoded != NULL)
|
|
return short_id;
|
|
|
|
struct type *alias = tag__type(tag);
|
|
struct type *new_enum = obstack_zalloc(&cu->obstack, sizeof(*new_enum));
|
|
if (new_enum == NULL)
|
|
return -ENOMEM;
|
|
|
|
recoded = (struct tag *)new_enum;
|
|
recoded->tag = DW_TAG_enumeration_type;
|
|
recoded->top_level = 1;
|
|
new_enum->nr_members = alias->nr_members;
|
|
/*
|
|
* Share the tags
|
|
*/
|
|
new_enum->namespace.tags.next = &alias->namespace.tags;
|
|
new_enum->namespace.shared_tags = 1;
|
|
new_enum->namespace.name = name;
|
|
new_enum->size = bit_size;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: tag=%s, name=%s, bit_size=%d\n",
|
|
__func__, dwarf_tag_name(tag->tag),
|
|
strings__ptr(strings, name), bit_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
uint32_t new_id;
|
|
if (cu__add_tag(cu, recoded, &new_id) == 0)
|
|
return new_id;
|
|
|
|
obstack_free(&cu->obstack, recoded);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
int class_member__dwarf_recode_bitfield(struct class_member *member,
|
|
struct cu *cu)
|
|
{
|
|
struct dwarf_tag *dtag = member->tag.priv;
|
|
struct dwarf_tag *type = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type);
|
|
int recoded_type_id;
|
|
|
|
if (type == NULL)
|
|
return -ENOENT;
|
|
|
|
recoded_type_id = tag__recode_dwarf_bitfield(type->tag, cu, member->bitfield_size);
|
|
if (recoded_type_id < 0)
|
|
return recoded_type_id;
|
|
|
|
member->tag.type = recoded_type_id;
|
|
return 0;
|
|
}
|
|
|
|
static struct class_member *class_member__new(Dwarf_Die *die, struct cu *cu,
|
|
bool in_union)
|
|
{
|
|
struct class_member *member = tag__alloc(cu, sizeof(*member));
|
|
|
|
if (member != NULL) {
|
|
tag__init(&member->tag, cu, die);
|
|
member->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
member->is_static = !in_union && !dwarf_hasattr(die, DW_AT_data_member_location);
|
|
member->const_value = attr_numeric(die, DW_AT_const_value);
|
|
member->alignment = attr_numeric(die, DW_AT_alignment);
|
|
member->byte_offset = attr_offset(die, DW_AT_data_member_location);
|
|
/*
|
|
* Bit offset calculated here is valid only for byte-aligned
|
|
* fields. For bitfields on little-endian archs we need to
|
|
* adjust them taking into account byte size of the field,
|
|
* which might not be yet known. So we'll re-calculate bit
|
|
* offset later, in class_member__cache_byte_size.
|
|
*/
|
|
member->bit_offset = member->byte_offset * 8;
|
|
/*
|
|
* If DW_AT_byte_size is not present, byte size will be
|
|
* determined later in class_member__cache_byte_size using
|
|
* base integer/enum type
|
|
*/
|
|
member->byte_size = attr_numeric(die, DW_AT_byte_size);
|
|
member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset);
|
|
member->bitfield_size = attr_numeric(die, DW_AT_bit_size);
|
|
member->bit_hole = 0;
|
|
member->bitfield_end = 0;
|
|
member->visited = 0;
|
|
member->accessibility = attr_numeric(die, DW_AT_accessibility);
|
|
member->virtuality = attr_numeric(die, DW_AT_virtuality);
|
|
member->hole = 0;
|
|
}
|
|
|
|
return member;
|
|
}
|
|
|
|
static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct parameter *parm = tag__alloc(cu, sizeof(*parm));
|
|
|
|
if (parm != NULL) {
|
|
tag__init(&parm->tag, cu, die);
|
|
parm->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
}
|
|
|
|
return parm;
|
|
}
|
|
|
|
static struct inline_expansion *inline_expansion__new(Dwarf_Die *die,
|
|
struct cu *cu)
|
|
{
|
|
struct inline_expansion *exp = tag__alloc(cu, sizeof(*exp));
|
|
|
|
if (exp != NULL) {
|
|
struct dwarf_tag *dtag = exp->ip.tag.priv;
|
|
|
|
tag__init(&exp->ip.tag, cu, die);
|
|
dtag->decl_file =
|
|
strings__add(strings, attr_string(die, DW_AT_call_file));
|
|
dtag->decl_line = attr_numeric(die, DW_AT_call_line);
|
|
dtag->type = attr_type(die, DW_AT_abstract_origin);
|
|
exp->ip.addr = 0;
|
|
exp->high_pc = 0;
|
|
|
|
if (!cu->has_addr_info)
|
|
goto out;
|
|
|
|
if (dwarf_lowpc(die, &exp->ip.addr))
|
|
exp->ip.addr = 0;
|
|
if (dwarf_lowpc(die, &exp->high_pc))
|
|
exp->high_pc = 0;
|
|
|
|
exp->size = exp->high_pc - exp->ip.addr;
|
|
if (exp->size == 0) {
|
|
Dwarf_Addr base, start;
|
|
ptrdiff_t offset = 0;
|
|
|
|
while (1) {
|
|
offset = dwarf_ranges(die, offset, &base, &start,
|
|
&exp->high_pc);
|
|
start = (unsigned long)start;
|
|
exp->high_pc = (unsigned long)exp->high_pc;
|
|
if (offset <= 0)
|
|
break;
|
|
exp->size += exp->high_pc - start;
|
|
if (exp->ip.addr == 0)
|
|
exp->ip.addr = start;
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
return exp;
|
|
}
|
|
|
|
static struct label *label__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct label *label = tag__alloc(cu, sizeof(*label));
|
|
|
|
if (label != NULL) {
|
|
tag__init(&label->ip.tag, cu, die);
|
|
label->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
if (!cu->has_addr_info || dwarf_lowpc(die, &label->ip.addr))
|
|
label->ip.addr = 0;
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
static struct class *class__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct class *class = tag__alloc_with_spec(cu, sizeof(*class));
|
|
|
|
if (class != NULL) {
|
|
type__init(&class->type, die, cu);
|
|
INIT_LIST_HEAD(&class->vtable);
|
|
class->nr_vtable_entries =
|
|
class->nr_holes =
|
|
class->nr_bit_holes =
|
|
class->padding =
|
|
class->bit_padding = 0;
|
|
class->priv = NULL;
|
|
}
|
|
|
|
return class;
|
|
}
|
|
|
|
static void lexblock__init(struct lexblock *block, struct cu *cu,
|
|
Dwarf_Die *die)
|
|
{
|
|
Dwarf_Off high_pc;
|
|
|
|
if (!cu->has_addr_info || dwarf_lowpc(die, &block->ip.addr)) {
|
|
block->ip.addr = 0;
|
|
block->size = 0;
|
|
} else if (dwarf_highpc(die, &high_pc))
|
|
block->size = 0;
|
|
else
|
|
block->size = high_pc - block->ip.addr;
|
|
|
|
INIT_LIST_HEAD(&block->tags);
|
|
|
|
block->size_inline_expansions =
|
|
block->nr_inline_expansions =
|
|
block->nr_labels =
|
|
block->nr_lexblocks =
|
|
block->nr_variables = 0;
|
|
}
|
|
|
|
static struct lexblock *lexblock__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct lexblock *block = tag__alloc(cu, sizeof(*block));
|
|
|
|
if (block != NULL) {
|
|
tag__init(&block->ip.tag, cu, die);
|
|
lexblock__init(block, cu, die);
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
static void ftype__init(struct ftype *ftype, Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
const uint16_t tag = dwarf_tag(die);
|
|
assert(tag == DW_TAG_subprogram || tag == DW_TAG_subroutine_type);
|
|
|
|
tag__init(&ftype->tag, cu, die);
|
|
INIT_LIST_HEAD(&ftype->parms);
|
|
ftype->nr_parms = 0;
|
|
ftype->unspec_parms = 0;
|
|
}
|
|
|
|
static struct ftype *ftype__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct ftype *ftype = tag__alloc(cu, sizeof(*ftype));
|
|
|
|
if (ftype != NULL)
|
|
ftype__init(ftype, die, cu);
|
|
|
|
return ftype;
|
|
}
|
|
|
|
static struct function *function__new(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct function *func = tag__alloc_with_spec(cu, sizeof(*func));
|
|
|
|
if (func != NULL) {
|
|
ftype__init(&func->proto, die, cu);
|
|
lexblock__init(&func->lexblock, cu, die);
|
|
func->name = strings__add(strings, attr_string(die, DW_AT_name));
|
|
func->linkage_name = strings__add(strings, attr_string(die, DW_AT_MIPS_linkage_name));
|
|
func->inlined = attr_numeric(die, DW_AT_inline);
|
|
func->declaration = dwarf_hasattr(die, DW_AT_declaration);
|
|
func->external = dwarf_hasattr(die, DW_AT_external);
|
|
func->abstract_origin = dwarf_hasattr(die, DW_AT_abstract_origin);
|
|
dwarf_tag__set_spec(func->proto.tag.priv,
|
|
attr_type(die, DW_AT_specification));
|
|
func->accessibility = attr_numeric(die, DW_AT_accessibility);
|
|
func->virtuality = attr_numeric(die, DW_AT_virtuality);
|
|
INIT_LIST_HEAD(&func->vtable_node);
|
|
INIT_LIST_HEAD(&func->tool_node);
|
|
func->vtable_entry = -1;
|
|
if (dwarf_hasattr(die, DW_AT_vtable_elem_location))
|
|
func->vtable_entry = attr_offset(die, DW_AT_vtable_elem_location);
|
|
func->cu_total_size_inline_expansions = 0;
|
|
func->cu_total_nr_inline_expansions = 0;
|
|
func->priv = NULL;
|
|
}
|
|
|
|
return func;
|
|
}
|
|
|
|
static uint64_t attr_upper_bound(Dwarf_Die *die)
|
|
{
|
|
Dwarf_Attribute attr;
|
|
|
|
if (dwarf_attr(die, DW_AT_upper_bound, &attr) != NULL) {
|
|
Dwarf_Word num;
|
|
|
|
if (dwarf_formudata(&attr, &num) == 0) {
|
|
return (uintmax_t)num + 1;
|
|
}
|
|
} else if (dwarf_attr(die, DW_AT_count, &attr) != NULL) {
|
|
Dwarf_Word num;
|
|
|
|
if (dwarf_formudata(&attr, &num) == 0) {
|
|
return (uintmax_t)num;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __cu__tag_not_handled(Dwarf_Die *die, const char *fn)
|
|
{
|
|
uint32_t tag = dwarf_tag(die);
|
|
|
|
fprintf(stderr, "%s: DW_TAG_%s (%#x) @ <%#llx> not handled!\n",
|
|
fn, dwarf_tag_name(tag), tag,
|
|
(unsigned long long)dwarf_dieoffset(die));
|
|
}
|
|
|
|
static struct tag unsupported_tag;
|
|
|
|
#define cu__tag_not_handled(die) __cu__tag_not_handled(die, __FUNCTION__)
|
|
|
|
static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
|
|
int toplevel, const char *fn);
|
|
|
|
#define die__process_tag(die, cu, toplevel) \
|
|
__die__process_tag(die, cu, toplevel, __FUNCTION__)
|
|
|
|
static struct tag *die__create_new_tag(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct tag *tag = tag__new(die, cu);
|
|
|
|
if (tag != NULL) {
|
|
if (dwarf_haschildren(die))
|
|
fprintf(stderr, "%s: %s WITH children!\n", __func__,
|
|
dwarf_tag_name(tag->tag));
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
|
|
static struct tag *die__create_new_ptr_to_member_type(Dwarf_Die *die,
|
|
struct cu *cu)
|
|
{
|
|
struct ptr_to_member_type *ptr = ptr_to_member_type__new(die, cu);
|
|
|
|
return ptr ? &ptr->tag : NULL;
|
|
}
|
|
|
|
static int die__process_class(Dwarf_Die *die,
|
|
struct type *class, struct cu *cu);
|
|
|
|
static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
struct class *class = class__new(die, cu);
|
|
|
|
if (class != NULL &&
|
|
dwarf_haschildren(die) != 0 &&
|
|
dwarf_child(die, &child) == 0) {
|
|
if (die__process_class(&child, &class->type, cu) != 0) {
|
|
class__delete(class, cu);
|
|
class = NULL;
|
|
}
|
|
}
|
|
|
|
return class ? &class->type.namespace.tag : NULL;
|
|
}
|
|
|
|
static int die__process_namespace(Dwarf_Die *die, struct namespace *namespace,
|
|
struct cu *cu);
|
|
|
|
static struct tag *die__create_new_namespace(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
struct namespace *namespace = namespace__new(die, cu);
|
|
|
|
if (namespace != NULL &&
|
|
dwarf_haschildren(die) != 0 &&
|
|
dwarf_child(die, &child) == 0) {
|
|
if (die__process_namespace(&child, namespace, cu) != 0) {
|
|
namespace__delete(namespace, cu);
|
|
namespace = NULL;
|
|
}
|
|
}
|
|
|
|
return namespace ? &namespace->tag : NULL;
|
|
}
|
|
|
|
static struct tag *die__create_new_union(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
struct type *utype = type__new(die, cu);
|
|
|
|
if (utype != NULL &&
|
|
dwarf_haschildren(die) != 0 &&
|
|
dwarf_child(die, &child) == 0) {
|
|
if (die__process_class(&child, utype, cu) != 0) {
|
|
type__delete(utype, cu);
|
|
utype = NULL;
|
|
}
|
|
}
|
|
|
|
return utype ? &utype->namespace.tag : NULL;
|
|
}
|
|
|
|
static struct tag *die__create_new_base_type(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct base_type *base = base_type__new(die, cu);
|
|
|
|
if (base == NULL)
|
|
return NULL;
|
|
|
|
if (dwarf_haschildren(die))
|
|
fprintf(stderr, "%s: DW_TAG_base_type WITH children!\n",
|
|
__func__);
|
|
|
|
return &base->tag;
|
|
}
|
|
|
|
static struct tag *die__create_new_typedef(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct type *tdef = type__new(die, cu);
|
|
|
|
if (tdef == NULL)
|
|
return NULL;
|
|
|
|
if (dwarf_haschildren(die)) {
|
|
struct dwarf_tag *dtag = tdef->namespace.tag.priv;
|
|
fprintf(stderr, "%s: DW_TAG_typedef %llx WITH children!\n",
|
|
__func__, (unsigned long long)dtag->id);
|
|
}
|
|
|
|
return &tdef->namespace.tag;
|
|
}
|
|
|
|
static struct tag *die__create_new_array(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
/* "64 dimensions will be enough for everybody." acme, 2006 */
|
|
const uint8_t max_dimensions = 64;
|
|
uint32_t nr_entries[max_dimensions];
|
|
struct array_type *array = array_type__new(die, cu);
|
|
|
|
if (array == NULL)
|
|
return NULL;
|
|
|
|
if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
|
|
return &array->tag;
|
|
|
|
die = &child;
|
|
do {
|
|
if (dwarf_tag(die) == DW_TAG_subrange_type) {
|
|
nr_entries[array->dimensions++] = attr_upper_bound(die);
|
|
if (array->dimensions == max_dimensions) {
|
|
fprintf(stderr, "%s: only %u dimensions are "
|
|
"supported!\n",
|
|
__FUNCTION__, max_dimensions);
|
|
break;
|
|
}
|
|
} else
|
|
cu__tag_not_handled(die);
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
|
|
array->nr_entries = memdup(nr_entries,
|
|
array->dimensions * sizeof(uint32_t), cu);
|
|
if (array->nr_entries == NULL)
|
|
goto out_free;
|
|
|
|
return &array->tag;
|
|
out_free:
|
|
obstack_free(&cu->obstack, array);
|
|
return NULL;
|
|
}
|
|
|
|
static struct tag *die__create_new_parameter(Dwarf_Die *die,
|
|
struct ftype *ftype,
|
|
struct lexblock *lexblock,
|
|
struct cu *cu)
|
|
{
|
|
struct parameter *parm = parameter__new(die, cu);
|
|
|
|
if (parm == NULL)
|
|
return NULL;
|
|
|
|
if (ftype != NULL)
|
|
ftype__add_parameter(ftype, parm);
|
|
else {
|
|
/*
|
|
* DW_TAG_formal_parameters on a non DW_TAG_subprogram nor
|
|
* DW_TAG_subroutine_type tag happens sometimes, likely due to
|
|
* compiler optimizing away a inline expansion (at least this
|
|
* was observed in some cases, such as in the Linux kernel
|
|
* current_kernel_time function circa 2.6.20-rc5), keep it in
|
|
* the lexblock tag list because it can be referenced as an
|
|
* DW_AT_abstract_origin in another DW_TAG_formal_parameter.
|
|
*/
|
|
lexblock__add_tag(lexblock, &parm->tag);
|
|
}
|
|
|
|
return &parm->tag;
|
|
}
|
|
|
|
static struct tag *die__create_new_label(Dwarf_Die *die,
|
|
struct lexblock *lexblock,
|
|
struct cu *cu)
|
|
{
|
|
struct label *label = label__new(die, cu);
|
|
|
|
if (label == NULL)
|
|
return NULL;
|
|
|
|
lexblock__add_label(lexblock, label);
|
|
return &label->ip.tag;
|
|
}
|
|
|
|
static struct tag *die__create_new_variable(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct variable *var = variable__new(die, cu);
|
|
|
|
return var ? &var->ip.tag : NULL;
|
|
}
|
|
|
|
static struct tag *die__create_new_subroutine_type(Dwarf_Die *die,
|
|
struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
struct ftype *ftype = ftype__new(die, cu);
|
|
struct tag *tag;
|
|
|
|
if (ftype == NULL)
|
|
return NULL;
|
|
|
|
if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
|
|
goto out;
|
|
|
|
die = &child;
|
|
do {
|
|
uint32_t id;
|
|
|
|
switch (dwarf_tag(die)) {
|
|
case DW_TAG_formal_parameter:
|
|
tag = die__create_new_parameter(die, ftype, NULL, cu);
|
|
break;
|
|
case DW_TAG_unspecified_parameters:
|
|
ftype->unspec_parms = 1;
|
|
continue;
|
|
default:
|
|
tag = die__process_tag(die, cu, 0);
|
|
if (tag == NULL)
|
|
goto out_delete;
|
|
|
|
if (cu__add_tag(cu, tag, &id) < 0)
|
|
goto out_delete_tag;
|
|
|
|
goto hash;
|
|
}
|
|
|
|
if (tag == NULL)
|
|
goto out_delete;
|
|
|
|
if (cu__table_add_tag(cu, tag, &id) < 0)
|
|
goto out_delete_tag;
|
|
hash:
|
|
cu__hash(cu, tag);
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
dtag->small_id = id;
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
out:
|
|
return &ftype->tag;
|
|
out_delete_tag:
|
|
tag__delete(tag, cu);
|
|
out_delete:
|
|
ftype__delete(ftype, cu);
|
|
return NULL;
|
|
}
|
|
|
|
static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
struct type *enumeration = type__new(die, cu);
|
|
|
|
if (enumeration == NULL)
|
|
return NULL;
|
|
|
|
if (enumeration->size == 0)
|
|
enumeration->size = sizeof(int) * 8;
|
|
else
|
|
enumeration->size *= 8;
|
|
|
|
if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) {
|
|
/* Seen on libQtCore.so.4.3.4.debug,
|
|
* class QAbstractFileEngineIterator, enum EntryInfoType */
|
|
goto out;
|
|
}
|
|
|
|
die = &child;
|
|
do {
|
|
struct enumerator *enumerator;
|
|
|
|
if (dwarf_tag(die) != DW_TAG_enumerator) {
|
|
cu__tag_not_handled(die);
|
|
continue;
|
|
}
|
|
enumerator = enumerator__new(die, cu);
|
|
if (enumerator == NULL)
|
|
goto out_delete;
|
|
|
|
enumeration__add(enumeration, enumerator);
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
out:
|
|
return &enumeration->namespace.tag;
|
|
out_delete:
|
|
enumeration__delete(enumeration, cu);
|
|
return NULL;
|
|
}
|
|
|
|
static int die__process_class(Dwarf_Die *die, struct type *class,
|
|
struct cu *cu)
|
|
{
|
|
const bool is_union = tag__is_union(&class->namespace.tag);
|
|
|
|
do {
|
|
switch (dwarf_tag(die)) {
|
|
#ifdef STB_GNU_UNIQUE
|
|
case DW_TAG_GNU_formal_parameter_pack:
|
|
case DW_TAG_GNU_template_parameter_pack:
|
|
case DW_TAG_GNU_template_template_param:
|
|
#endif
|
|
case DW_TAG_template_type_parameter:
|
|
case DW_TAG_template_value_parameter:
|
|
/*
|
|
* FIXME: probably we'll have to attach this as a list of
|
|
* template parameters to use at class__fprintf time...
|
|
*
|
|
* See:
|
|
* https://gcc.gnu.org/wiki/TemplateParmsDwarf
|
|
*/
|
|
tag__print_not_supported(dwarf_tag(die));
|
|
continue;
|
|
case DW_TAG_inheritance:
|
|
case DW_TAG_member: {
|
|
struct class_member *member = class_member__new(die, cu, is_union);
|
|
|
|
if (member == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (cu__is_c_plus_plus(cu)) {
|
|
uint32_t id;
|
|
|
|
if (cu__table_add_tag(cu, &member->tag, &id) < 0) {
|
|
class_member__delete(member, cu);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
struct dwarf_tag *dtag = member->tag.priv;
|
|
dtag->small_id = id;
|
|
}
|
|
|
|
type__add_member(class, member);
|
|
cu__hash(cu, &member->tag);
|
|
}
|
|
continue;
|
|
default: {
|
|
struct tag *tag = die__process_tag(die, cu, 0);
|
|
|
|
if (tag == NULL)
|
|
return -ENOMEM;
|
|
|
|
uint32_t id;
|
|
|
|
if (cu__table_add_tag(cu, tag, &id) < 0) {
|
|
tag__delete(tag, cu);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
dtag->small_id = id;
|
|
|
|
namespace__add_tag(&class->namespace, tag);
|
|
cu__hash(cu, tag);
|
|
if (tag__is_function(tag)) {
|
|
struct function *fself = tag__function(tag);
|
|
|
|
if (fself->vtable_entry != -1)
|
|
class__add_vtable_entry(type__class(class), fself);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int die__process_namespace(Dwarf_Die *die, struct namespace *namespace,
|
|
struct cu *cu)
|
|
{
|
|
struct tag *tag;
|
|
do {
|
|
tag = die__process_tag(die, cu, 0);
|
|
if (tag == NULL)
|
|
goto out_enomem;
|
|
|
|
uint32_t id;
|
|
if (cu__table_add_tag(cu, tag, &id) < 0)
|
|
goto out_delete_tag;
|
|
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
dtag->small_id = id;
|
|
|
|
namespace__add_tag(namespace, tag);
|
|
cu__hash(cu, tag);
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
|
|
return 0;
|
|
out_delete_tag:
|
|
tag__delete(tag, cu);
|
|
out_enomem:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
|
|
struct lexblock *lexblock, struct cu *cu);
|
|
|
|
static int die__create_new_lexblock(Dwarf_Die *die,
|
|
struct cu *cu, struct lexblock *father)
|
|
{
|
|
struct lexblock *lexblock = lexblock__new(die, cu);
|
|
|
|
if (lexblock != NULL) {
|
|
if (die__process_function(die, NULL, lexblock, cu) != 0)
|
|
goto out_delete;
|
|
}
|
|
if (father != NULL)
|
|
lexblock__add_lexblock(father, lexblock);
|
|
return 0;
|
|
out_delete:
|
|
lexblock__delete(lexblock, cu);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static struct tag *die__create_new_inline_expansion(Dwarf_Die *die,
|
|
struct lexblock *lexblock,
|
|
struct cu *cu);
|
|
|
|
static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
struct tag *tag;
|
|
|
|
if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
|
|
return 0;
|
|
|
|
die = &child;
|
|
do {
|
|
uint32_t id;
|
|
|
|
switch (dwarf_tag(die)) {
|
|
case DW_TAG_GNU_call_site:
|
|
case DW_TAG_GNU_call_site_parameter:
|
|
/*
|
|
* FIXME: read http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open
|
|
* and write proper support.
|
|
*
|
|
* From a quick read there is not much we can use in
|
|
* the existing dwarves tools, so just stop warning the user,
|
|
* developers will find these notes if wanting to use in a
|
|
* new tool.
|
|
*/
|
|
continue;
|
|
case DW_TAG_lexical_block:
|
|
if (die__create_new_lexblock(die, cu, lexblock) != 0)
|
|
goto out_enomem;
|
|
continue;
|
|
case DW_TAG_formal_parameter:
|
|
/*
|
|
* FIXME:
|
|
* So far DW_TAG_inline_routine had just an
|
|
* abstract origin, but starting with
|
|
* /usr/lib/openoffice.org/basis3.0/program/libdbalx.so
|
|
* I realized it really has to be handled as a
|
|
* DW_TAG_function... Lets just get the types
|
|
* for 1.8, then fix this properly.
|
|
*
|
|
* cu__tag_not_handled(die);
|
|
*/
|
|
continue;
|
|
case DW_TAG_inlined_subroutine:
|
|
tag = die__create_new_inline_expansion(die, lexblock, cu);
|
|
break;
|
|
case DW_TAG_label:
|
|
tag = die__create_new_label(die, lexblock, cu);
|
|
break;
|
|
default:
|
|
tag = die__process_tag(die, cu, 0);
|
|
if (tag == NULL)
|
|
goto out_enomem;
|
|
|
|
if (tag == &unsupported_tag)
|
|
continue;
|
|
|
|
if (cu__add_tag(cu, tag, &id) < 0)
|
|
goto out_delete_tag;
|
|
goto hash;
|
|
}
|
|
|
|
if (tag == NULL)
|
|
goto out_enomem;
|
|
|
|
if (cu__table_add_tag(cu, tag, &id) < 0)
|
|
goto out_delete_tag;
|
|
hash:
|
|
cu__hash(cu, tag);
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
dtag->small_id = id;
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
|
|
return 0;
|
|
out_delete_tag:
|
|
tag__delete(tag, cu);
|
|
out_enomem:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static struct tag *die__create_new_inline_expansion(Dwarf_Die *die,
|
|
struct lexblock *lexblock,
|
|
struct cu *cu)
|
|
{
|
|
struct inline_expansion *exp = inline_expansion__new(die, cu);
|
|
|
|
if (exp == NULL)
|
|
return NULL;
|
|
|
|
if (die__process_inline_expansion(die, lexblock, cu) != 0) {
|
|
obstack_free(&cu->obstack, exp);
|
|
return NULL;
|
|
}
|
|
|
|
if (lexblock != NULL)
|
|
lexblock__add_inline_expansion(lexblock, exp);
|
|
return &exp->ip.tag;
|
|
}
|
|
|
|
static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
|
|
struct lexblock *lexblock, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
struct tag *tag;
|
|
|
|
if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
|
|
return 0;
|
|
|
|
die = &child;
|
|
do {
|
|
uint32_t id;
|
|
|
|
switch (dwarf_tag(die)) {
|
|
case DW_TAG_GNU_call_site:
|
|
case DW_TAG_GNU_call_site_parameter:
|
|
/*
|
|
* XXX: read http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open
|
|
* and write proper support.
|
|
*
|
|
* From a quick read there is not much we can use in
|
|
* the existing dwarves tools, so just stop warning the user,
|
|
* developers will find these notes if wanting to use in a
|
|
* new tool.
|
|
*/
|
|
continue;
|
|
case DW_TAG_dwarf_procedure:
|
|
/*
|
|
* Ignore it, just scope expressions, that we have no use for (so far).
|
|
*/
|
|
continue;
|
|
#ifdef STB_GNU_UNIQUE
|
|
case DW_TAG_GNU_formal_parameter_pack:
|
|
case DW_TAG_GNU_template_parameter_pack:
|
|
case DW_TAG_GNU_template_template_param:
|
|
#endif
|
|
case DW_TAG_template_type_parameter:
|
|
case DW_TAG_template_value_parameter:
|
|
/* FIXME: probably we'll have to attach this as a list of
|
|
* template parameters to use at class__fprintf time...
|
|
* See die__process_class */
|
|
tag__print_not_supported(dwarf_tag(die));
|
|
continue;
|
|
case DW_TAG_formal_parameter:
|
|
tag = die__create_new_parameter(die, ftype, lexblock, cu);
|
|
break;
|
|
case DW_TAG_variable:
|
|
tag = die__create_new_variable(die, cu);
|
|
if (tag == NULL)
|
|
goto out_enomem;
|
|
lexblock__add_variable(lexblock, tag__variable(tag));
|
|
break;
|
|
case DW_TAG_unspecified_parameters:
|
|
if (ftype != NULL)
|
|
ftype->unspec_parms = 1;
|
|
continue;
|
|
case DW_TAG_label:
|
|
tag = die__create_new_label(die, lexblock, cu);
|
|
break;
|
|
case DW_TAG_inlined_subroutine:
|
|
tag = die__create_new_inline_expansion(die, lexblock, cu);
|
|
break;
|
|
case DW_TAG_lexical_block:
|
|
if (die__create_new_lexblock(die, cu, lexblock) != 0)
|
|
goto out_enomem;
|
|
continue;
|
|
default:
|
|
tag = die__process_tag(die, cu, 0);
|
|
|
|
if (tag == NULL)
|
|
goto out_enomem;
|
|
|
|
if (tag == &unsupported_tag)
|
|
continue;
|
|
|
|
if (cu__add_tag(cu, tag, &id) < 0)
|
|
goto out_delete_tag;
|
|
|
|
goto hash;
|
|
}
|
|
|
|
if (tag == NULL)
|
|
goto out_enomem;
|
|
|
|
if (cu__table_add_tag(cu, tag, &id) < 0)
|
|
goto out_delete_tag;
|
|
hash:
|
|
cu__hash(cu, tag);
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
dtag->small_id = id;
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
|
|
return 0;
|
|
out_delete_tag:
|
|
tag__delete(tag, cu);
|
|
out_enomem:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
struct function *function = function__new(die, cu);
|
|
|
|
if (function != NULL &&
|
|
die__process_function(die, &function->proto,
|
|
&function->lexblock, cu) != 0) {
|
|
function__delete(function, cu);
|
|
function = NULL;
|
|
}
|
|
|
|
return function ? &function->proto.tag : NULL;
|
|
}
|
|
|
|
static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
|
|
int top_level, const char *fn)
|
|
{
|
|
struct tag *tag;
|
|
|
|
switch (dwarf_tag(die)) {
|
|
case DW_TAG_array_type:
|
|
tag = die__create_new_array(die, cu); break;
|
|
case DW_TAG_base_type:
|
|
tag = die__create_new_base_type(die, cu); break;
|
|
case DW_TAG_const_type:
|
|
case DW_TAG_imported_declaration:
|
|
case DW_TAG_imported_module:
|
|
case DW_TAG_pointer_type:
|
|
case DW_TAG_reference_type:
|
|
case DW_TAG_restrict_type:
|
|
case DW_TAG_unspecified_type:
|
|
case DW_TAG_volatile_type:
|
|
tag = die__create_new_tag(die, cu); break;
|
|
case DW_TAG_ptr_to_member_type:
|
|
tag = die__create_new_ptr_to_member_type(die, cu); break;
|
|
case DW_TAG_enumeration_type:
|
|
tag = die__create_new_enumeration(die, cu); break;
|
|
case DW_TAG_namespace:
|
|
tag = die__create_new_namespace(die, cu); break;
|
|
case DW_TAG_class_type:
|
|
case DW_TAG_interface_type:
|
|
case DW_TAG_structure_type:
|
|
tag = die__create_new_class(die, cu); break;
|
|
case DW_TAG_subprogram:
|
|
tag = die__create_new_function(die, cu); break;
|
|
case DW_TAG_subroutine_type:
|
|
tag = die__create_new_subroutine_type(die, cu); break;
|
|
case DW_TAG_rvalue_reference_type:
|
|
case DW_TAG_typedef:
|
|
tag = die__create_new_typedef(die, cu); break;
|
|
case DW_TAG_union_type:
|
|
tag = die__create_new_union(die, cu); break;
|
|
case DW_TAG_variable:
|
|
tag = die__create_new_variable(die, cu); break;
|
|
default:
|
|
__cu__tag_not_handled(die, fn);
|
|
/* fall thru */
|
|
case DW_TAG_dwarf_procedure:
|
|
/*
|
|
* Ignore it, just scope expressions, that we have no use for (so far).
|
|
*/
|
|
tag = &unsupported_tag;
|
|
break;
|
|
}
|
|
|
|
if (tag != NULL)
|
|
tag->top_level = top_level;
|
|
|
|
return tag;
|
|
}
|
|
|
|
static int die__process_unit(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
do {
|
|
struct tag *tag = die__process_tag(die, cu, 1);
|
|
if (tag == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (tag == &unsupported_tag)
|
|
continue;
|
|
|
|
uint32_t id;
|
|
cu__add_tag(cu, tag, &id);
|
|
cu__hash(cu, tag);
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
dtag->small_id = id;
|
|
} while (dwarf_siblingof(die, die) == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __tag__print_type_not_found(struct tag *tag, const char *func)
|
|
{
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
fprintf(stderr, "%s: couldn't find %#llx type for %#llx (%s)!\n", func,
|
|
(unsigned long long)dtag->type.off, (unsigned long long)dtag->id,
|
|
dwarf_tag_name(tag->tag));
|
|
}
|
|
|
|
#define tag__print_type_not_found(tag) \
|
|
__tag__print_type_not_found(tag, __func__)
|
|
|
|
static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu);
|
|
|
|
static int namespace__recode_dwarf_types(struct tag *tag, struct cu *cu)
|
|
{
|
|
struct tag *pos;
|
|
struct dwarf_cu *dcu = cu->priv;
|
|
struct namespace *ns = tag__namespace(tag);
|
|
|
|
namespace__for_each_tag(ns, pos) {
|
|
struct dwarf_tag *dtype;
|
|
struct dwarf_tag *dpos = pos->priv;
|
|
|
|
if (tag__has_namespace(pos)) {
|
|
if (namespace__recode_dwarf_types(pos, cu))
|
|
return -1;
|
|
continue;
|
|
}
|
|
|
|
switch (pos->tag) {
|
|
case DW_TAG_member: {
|
|
struct class_member *member = tag__class_member(pos);
|
|
/*
|
|
* We may need to recode the type, possibly creating a
|
|
* suitably sized new base_type
|
|
*/
|
|
if (member->bitfield_size != 0 && !no_bitfield_type_recode) {
|
|
if (class_member__dwarf_recode_bitfield(member, cu))
|
|
return -1;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
case DW_TAG_subroutine_type:
|
|
case DW_TAG_subprogram:
|
|
ftype__recode_dwarf_types(pos, cu);
|
|
break;
|
|
case DW_TAG_imported_module:
|
|
dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->type);
|
|
goto check_type;
|
|
/* Can be for both types and non types */
|
|
case DW_TAG_imported_declaration:
|
|
dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->type);
|
|
if (dtype != NULL)
|
|
goto next;
|
|
goto find_type;
|
|
}
|
|
|
|
if (dpos->type.off == 0) /* void */
|
|
continue;
|
|
find_type:
|
|
dtype = dwarf_cu__find_type_by_ref(dcu, &dpos->type);
|
|
check_type:
|
|
if (dtype == NULL) {
|
|
tag__print_type_not_found(pos);
|
|
continue;
|
|
}
|
|
next:
|
|
pos->type = dtype->small_id;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void type__recode_dwarf_specification(struct tag *tag, struct cu *cu)
|
|
{
|
|
struct dwarf_tag *dtype;
|
|
struct type *t = tag__type(tag);
|
|
dwarf_off_ref specification = dwarf_tag__spec(tag->priv);
|
|
|
|
if (t->namespace.name != 0 || specification.off == 0)
|
|
return;
|
|
|
|
dtype = dwarf_cu__find_type_by_ref(cu->priv, &specification);
|
|
if (dtype != NULL)
|
|
t->namespace.name = tag__namespace(dtype->tag)->name;
|
|
else {
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
|
|
fprintf(stderr,
|
|
"%s: couldn't find name for "
|
|
"class %#llx, specification=%#llx\n", __func__,
|
|
(unsigned long long)dtag->id,
|
|
(unsigned long long)specification.off);
|
|
}
|
|
}
|
|
|
|
static void __tag__print_abstract_origin_not_found(struct tag *tag,
|
|
const char *func)
|
|
{
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
fprintf(stderr,
|
|
"%s: couldn't find %#llx abstract_origin for %#llx (%s)!\n",
|
|
func, (unsigned long long)dtag->abstract_origin.off,
|
|
(unsigned long long)dtag->id,
|
|
dwarf_tag_name(tag->tag));
|
|
}
|
|
|
|
#define tag__print_abstract_origin_not_found(tag ) \
|
|
__tag__print_abstract_origin_not_found(tag, __func__)
|
|
|
|
static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu)
|
|
{
|
|
struct parameter *pos;
|
|
struct dwarf_cu *dcu = cu->priv;
|
|
struct ftype *type = tag__ftype(tag);
|
|
|
|
ftype__for_each_parameter(type, pos) {
|
|
struct dwarf_tag *dpos = pos->tag.priv;
|
|
struct dwarf_tag *dtype;
|
|
|
|
if (dpos->type.off == 0) {
|
|
if (dpos->abstract_origin.off == 0) {
|
|
/* Function without parameters */
|
|
pos->tag.type = 0;
|
|
continue;
|
|
}
|
|
dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->abstract_origin);
|
|
if (dtype == NULL) {
|
|
tag__print_abstract_origin_not_found(&pos->tag);
|
|
continue;
|
|
}
|
|
pos->name = tag__parameter(dtype->tag)->name;
|
|
pos->tag.type = dtype->tag->type;
|
|
continue;
|
|
}
|
|
|
|
dtype = dwarf_cu__find_type_by_ref(dcu, &dpos->type);
|
|
if (dtype == NULL) {
|
|
tag__print_type_not_found(&pos->tag);
|
|
continue;
|
|
}
|
|
pos->tag.type = dtype->small_id;
|
|
}
|
|
}
|
|
|
|
static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
|
|
{
|
|
struct tag *pos;
|
|
struct dwarf_cu *dcu = cu->priv;
|
|
|
|
list_for_each_entry(pos, &tag->tags, node) {
|
|
struct dwarf_tag *dpos = pos->priv;
|
|
struct dwarf_tag *dtype;
|
|
|
|
switch (pos->tag) {
|
|
case DW_TAG_lexical_block:
|
|
lexblock__recode_dwarf_types(tag__lexblock(pos), cu);
|
|
continue;
|
|
case DW_TAG_inlined_subroutine:
|
|
dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->type);
|
|
if (dtype == NULL) {
|
|
tag__print_type_not_found(pos);
|
|
continue;
|
|
}
|
|
ftype__recode_dwarf_types(dtype->tag, cu);
|
|
continue;
|
|
|
|
case DW_TAG_formal_parameter:
|
|
if (dpos->type.off != 0)
|
|
break;
|
|
|
|
struct parameter *fp = tag__parameter(pos);
|
|
dtype = dwarf_cu__find_tag_by_ref(dcu,
|
|
&dpos->abstract_origin);
|
|
if (dtype == NULL) {
|
|
tag__print_abstract_origin_not_found(pos);
|
|
continue;
|
|
}
|
|
fp->name = tag__parameter(dtype->tag)->name;
|
|
pos->type = dtype->tag->type;
|
|
continue;
|
|
|
|
case DW_TAG_variable:
|
|
if (dpos->type.off != 0)
|
|
break;
|
|
|
|
struct variable *var = tag__variable(pos);
|
|
|
|
if (dpos->abstract_origin.off == 0) {
|
|
/*
|
|
* DW_TAG_variable completely empty was
|
|
* found on libQtGui.so.4.3.4.debug
|
|
* <3><d6ea1>: Abbrev Number: 164 (DW_TAG_variable)
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
dtype = dwarf_cu__find_tag_by_ref(dcu,
|
|
&dpos->abstract_origin);
|
|
if (dtype == NULL) {
|
|
tag__print_abstract_origin_not_found(pos);
|
|
continue;
|
|
}
|
|
var->name = tag__variable(dtype->tag)->name;
|
|
pos->type = dtype->tag->type;
|
|
continue;
|
|
|
|
case DW_TAG_label: {
|
|
struct label *l = tag__label(pos);
|
|
|
|
if (dpos->abstract_origin.off == 0)
|
|
continue;
|
|
|
|
dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->abstract_origin);
|
|
if (dtype != NULL)
|
|
l->name = tag__label(dtype->tag)->name;
|
|
else
|
|
tag__print_abstract_origin_not_found(pos);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
dtype = dwarf_cu__find_type_by_ref(dcu, &dpos->type);
|
|
if (dtype == NULL) {
|
|
tag__print_type_not_found(pos);
|
|
continue;
|
|
}
|
|
pos->type = dtype->small_id;
|
|
}
|
|
}
|
|
|
|
static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
|
|
{
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
struct dwarf_tag *dtype;
|
|
|
|
/* Check if this is an already recoded bitfield */
|
|
if (dtag == NULL)
|
|
return 0;
|
|
|
|
if (tag__is_type(tag))
|
|
type__recode_dwarf_specification(tag, cu);
|
|
|
|
if (tag__has_namespace(tag))
|
|
return namespace__recode_dwarf_types(tag, cu);
|
|
|
|
switch (tag->tag) {
|
|
case DW_TAG_subprogram: {
|
|
struct function *fn = tag__function(tag);
|
|
|
|
if (fn->name == 0) {
|
|
dwarf_off_ref specification = dwarf_tag__spec(dtag);
|
|
if (dtag->abstract_origin.off == 0 &&
|
|
specification.off == 0) {
|
|
/*
|
|
* Found on libQtGui.so.4.3.4.debug
|
|
* <3><1423de>: Abbrev Number: 209 (DW_TAG_subprogram)
|
|
* <1423e0> DW_AT_declaration : 1
|
|
*/
|
|
return 0;
|
|
}
|
|
dtype = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->abstract_origin);
|
|
if (dtype == NULL)
|
|
dtype = dwarf_cu__find_tag_by_ref(cu->priv, &specification);
|
|
if (dtype != NULL)
|
|
fn->name = tag__function(dtype->tag)->name;
|
|
else {
|
|
fprintf(stderr,
|
|
"%s: couldn't find name for "
|
|
"function %#llx, abstract_origin=%#llx,"
|
|
" specification=%#llx\n", __func__,
|
|
(unsigned long long)dtag->id,
|
|
(unsigned long long)dtag->abstract_origin.off,
|
|
(unsigned long long)specification.off);
|
|
}
|
|
}
|
|
lexblock__recode_dwarf_types(&fn->lexblock, cu);
|
|
}
|
|
/* Fall thru */
|
|
|
|
case DW_TAG_subroutine_type:
|
|
ftype__recode_dwarf_types(tag, cu);
|
|
/* Fall thru, for the function return type */
|
|
break;
|
|
|
|
case DW_TAG_lexical_block:
|
|
lexblock__recode_dwarf_types(tag__lexblock(tag), cu);
|
|
return 0;
|
|
|
|
case DW_TAG_ptr_to_member_type: {
|
|
struct ptr_to_member_type *pt = tag__ptr_to_member_type(tag);
|
|
|
|
dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->containing_type);
|
|
if (dtype != NULL)
|
|
pt->containing_type = dtype->small_id;
|
|
else {
|
|
fprintf(stderr,
|
|
"%s: couldn't find type for "
|
|
"containing_type %#llx, containing_type=%#llx\n",
|
|
__func__,
|
|
(unsigned long long)dtag->id,
|
|
(unsigned long long)dtag->containing_type.off);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DW_TAG_namespace:
|
|
return namespace__recode_dwarf_types(tag, cu);
|
|
/* Damn, DW_TAG_inlined_subroutine is an special case
|
|
as dwarf_tag->id is in fact an abtract origin, i.e. must be
|
|
looked up in the tags_table, not in the types_table.
|
|
The others also point to routines, so are in tags_table */
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_imported_module:
|
|
dtype = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->type);
|
|
goto check_type;
|
|
/* Can be for both types and non types */
|
|
case DW_TAG_imported_declaration:
|
|
dtype = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->type);
|
|
if (dtype != NULL)
|
|
goto out;
|
|
goto find_type;
|
|
}
|
|
|
|
if (dtag->type.off == 0) {
|
|
tag->type = 0; /* void */
|
|
return 0;
|
|
}
|
|
|
|
find_type:
|
|
dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type);
|
|
check_type:
|
|
if (dtype == NULL) {
|
|
tag__print_type_not_found(tag);
|
|
return 0;
|
|
}
|
|
out:
|
|
tag->type = dtype->small_id;
|
|
return 0;
|
|
}
|
|
|
|
static int cu__recode_dwarf_types_table(struct cu *cu,
|
|
struct ptr_table *pt,
|
|
uint32_t i)
|
|
{
|
|
for (; i < pt->nr_entries; ++i) {
|
|
struct tag *tag = pt->entries[i];
|
|
|
|
if (tag != NULL) /* void, see cu__new */
|
|
if (tag__recode_dwarf_type(tag, cu))
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int cu__recode_dwarf_types(struct cu *cu)
|
|
{
|
|
if (cu__recode_dwarf_types_table(cu, &cu->types_table, 1) ||
|
|
cu__recode_dwarf_types_table(cu, &cu->tags_table, 0) ||
|
|
cu__recode_dwarf_types_table(cu, &cu->functions_table, 0))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static const char *dwarf_tag__decl_file(const struct tag *tag,
|
|
const struct cu *cu)
|
|
{
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
return cu->extra_dbg_info ?
|
|
strings__ptr(strings, dtag->decl_file) : NULL;
|
|
}
|
|
|
|
static uint32_t dwarf_tag__decl_line(const struct tag *tag,
|
|
const struct cu *cu)
|
|
{
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
return cu->extra_dbg_info ? dtag->decl_line : 0;
|
|
}
|
|
|
|
static unsigned long long dwarf_tag__orig_id(const struct tag *tag,
|
|
const struct cu *cu)
|
|
{
|
|
struct dwarf_tag *dtag = tag->priv;
|
|
return cu->extra_dbg_info ? dtag->id : 0;
|
|
}
|
|
|
|
static const char *dwarf__strings_ptr(const struct cu *cu __unused,
|
|
strings_t s)
|
|
{
|
|
return strings__ptr(strings, s);
|
|
}
|
|
|
|
struct debug_fmt_ops dwarf__ops;
|
|
|
|
static int die__process(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
Dwarf_Die child;
|
|
const uint16_t tag = dwarf_tag(die);
|
|
|
|
if (tag != DW_TAG_compile_unit && tag != DW_TAG_type_unit && tag != DW_TAG_partial_unit) {
|
|
fprintf(stderr, "%s: DW_TAG_compile_unit, DW_TAG_type_unit or DW_TAG_partial_unit expected got %s!\n",
|
|
__FUNCTION__, dwarf_tag_name(tag));
|
|
return -EINVAL;
|
|
}
|
|
|
|
cu->language = attr_numeric(die, DW_AT_language);
|
|
|
|
if (dwarf_child(die, &child) == 0) {
|
|
int err = die__process_unit(&child, cu);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (dwarf_siblingof(die, die) == 0)
|
|
fprintf(stderr, "%s: got %s unexpected tag after "
|
|
"DW_TAG_compile_unit!\n",
|
|
__FUNCTION__, dwarf_tag_name(tag));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int die__process_and_recode(Dwarf_Die *die, struct cu *cu)
|
|
{
|
|
int ret = die__process(die, cu);
|
|
if (ret != 0)
|
|
return ret;
|
|
return cu__recode_dwarf_types(cu);
|
|
}
|
|
|
|
static int class_member__cache_byte_size(struct tag *tag, struct cu *cu,
|
|
void *cookie)
|
|
{
|
|
struct class_member *member = tag__class_member(tag);
|
|
struct conf_load *conf_load = cookie;
|
|
|
|
if (tag__is_class_member(tag)) {
|
|
if (member->is_static)
|
|
return 0;
|
|
} else if (tag->tag != DW_TAG_inheritance) {
|
|
return 0;
|
|
}
|
|
|
|
if (member->bitfield_size == 0) {
|
|
member->byte_size = tag__size(tag, cu);
|
|
member->bit_size = member->byte_size * 8;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Try to figure out byte size, if it's not directly provided in DWARF
|
|
*/
|
|
if (member->byte_size == 0) {
|
|
struct tag *type = tag__strip_typedefs_and_modifiers(&member->tag, cu);
|
|
member->byte_size = tag__size(type, cu);
|
|
if (member->byte_size == 0) {
|
|
int bit_size;
|
|
if (tag__is_enumeration(type)) {
|
|
bit_size = tag__type(type)->size;
|
|
} else {
|
|
struct base_type *bt = tag__base_type(type);
|
|
bit_size = bt->bit_size ? bt->bit_size : base_type__name_to_size(bt, cu);
|
|
}
|
|
member->byte_size = (bit_size + 7) / 8 * 8;
|
|
}
|
|
}
|
|
member->bit_size = member->byte_size * 8;
|
|
|
|
/*
|
|
* XXX: after all the attemps to determine byte size, we might still
|
|
* be unsuccessful, because base_type__name_to_size doesn't know about
|
|
* the base_type name, so one has to add there when such base_type
|
|
* isn't found. pahole will put zero on the struct output so it should
|
|
* be easy to spot the name when such unlikely thing happens.
|
|
*/
|
|
if (member->byte_size == 0) {
|
|
member->bitfield_offset = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For little-endian architectures, DWARF data emitted by gcc/clang
|
|
* specifies bitfield offset as an offset from the highest-order bit
|
|
* of an underlying integral type (e.g., int) to a highest-order bit
|
|
* of a bitfield. E.g., for bitfield taking first 5 bits of int-backed
|
|
* bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit
|
|
* size), which is very counter-intuitive and isn't a natural
|
|
* extension of byte offset, which on little-endian points to
|
|
* lowest-order byte. So here we re-adjust bitfield offset to be an
|
|
* offset from lowest-order bit of underlying integral type to
|
|
* a lowest-order bit of a bitfield. This makes bitfield offset
|
|
* a natural extension of byte offset for bitfields and is uniform
|
|
* with how big-endian bit offsets work.
|
|
*/
|
|
if (cu->little_endian) {
|
|
member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size;
|
|
}
|
|
member->bit_offset = member->byte_offset * 8 + member->bitfield_offset;
|
|
|
|
/* make sure bitfield offset is non-negative */
|
|
if (member->bitfield_offset < 0) {
|
|
member->bitfield_offset += member->bit_size;
|
|
member->byte_offset -= member->byte_size;
|
|
member->bit_offset = member->byte_offset * 8 + member->bitfield_offset;
|
|
}
|
|
/* align on underlying base type natural alignment boundary */
|
|
member->bitfield_offset += (member->byte_offset % member->byte_size) * 8;
|
|
member->byte_offset = member->bit_offset / member->bit_size * member->bit_size / 8;
|
|
if (member->bitfield_offset >= member->bit_size) {
|
|
member->bitfield_offset -= member->bit_size;
|
|
member->byte_offset += member->byte_size;
|
|
}
|
|
|
|
if (conf_load && conf_load->fixup_silly_bitfields &&
|
|
member->byte_size == 8 * member->bitfield_size) {
|
|
member->bitfield_size = 0;
|
|
member->bitfield_offset = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int finalize_cu(struct cus *cus, struct cu *cu, struct dwarf_cu *dcu,
|
|
struct conf_load *conf)
|
|
{
|
|
base_type_name_to_size_table__init(strings);
|
|
cu__for_all_tags(cu, class_member__cache_byte_size, conf);
|
|
if (conf && conf->steal) {
|
|
return conf->steal(cu, conf);
|
|
}
|
|
return LSK__KEEPIT;
|
|
}
|
|
|
|
static int finalize_cu_immediately(struct cus *cus, struct cu *cu,
|
|
struct dwarf_cu *dcu,
|
|
struct conf_load *conf)
|
|
{
|
|
int lsk = finalize_cu(cus, cu, dcu, conf);
|
|
switch (lsk) {
|
|
case LSK__DELETE:
|
|
cu__delete(cu);
|
|
break;
|
|
case LSK__STOP_LOADING:
|
|
break;
|
|
case LSK__KEEPIT:
|
|
if (!cu->extra_dbg_info)
|
|
obstack_free(&dcu->obstack, NULL);
|
|
cus__add(cus, cu);
|
|
break;
|
|
}
|
|
return lsk;
|
|
}
|
|
|
|
static int cus__load_debug_types(struct cus *cus, struct conf_load *conf,
|
|
Dwfl_Module *mod, Dwarf *dw, Elf *elf,
|
|
const char *filename,
|
|
const unsigned char *build_id,
|
|
int build_id_len,
|
|
struct cu **cup, struct dwarf_cu *dcup)
|
|
{
|
|
Dwarf_Off off = 0, noff, type_off;
|
|
size_t cuhl;
|
|
uint8_t pointer_size, offset_size;
|
|
uint64_t signature;
|
|
|
|
*cup = NULL;
|
|
|
|
while (dwarf_next_unit(dw, off, &noff, &cuhl, NULL, NULL, &pointer_size,
|
|
&offset_size, &signature, &type_off)
|
|
== 0) {
|
|
|
|
if (*cup == NULL) {
|
|
struct cu *cu;
|
|
|
|
cu = cu__new("", pointer_size, build_id,
|
|
build_id_len, filename);
|
|
if (cu == NULL) {
|
|
return DWARF_CB_ABORT;
|
|
}
|
|
|
|
cu->uses_global_strings = true;
|
|
cu->elf = elf;
|
|
cu->dwfl = mod;
|
|
cu->extra_dbg_info = conf ? conf->extra_dbg_info : 0;
|
|
cu->has_addr_info = conf ? conf->get_addr_info : 0;
|
|
|
|
GElf_Ehdr ehdr;
|
|
if (gelf_getehdr(elf, &ehdr) == NULL) {
|
|
return DWARF_CB_ABORT;
|
|
}
|
|
cu->little_endian = ehdr.e_ident[EI_DATA] == ELFDATA2LSB;
|
|
|
|
dwarf_cu__init(dcup);
|
|
dcup->cu = cu;
|
|
/* Funny hack. */
|
|
dcup->type_unit = dcup;
|
|
cu->priv = dcup;
|
|
cu->dfops = &dwarf__ops;
|
|
|
|
*cup = cu;
|
|
}
|
|
|
|
Dwarf_Die die_mem;
|
|
Dwarf_Die *cu_die = dwarf_offdie_types(dw, off + cuhl,
|
|
&die_mem);
|
|
|
|
if (die__process(cu_die, *cup) != 0)
|
|
return DWARF_CB_ABORT;
|
|
|
|
off = noff;
|
|
}
|
|
|
|
if (*cup != NULL && cu__recode_dwarf_types(*cup) != 0)
|
|
return DWARF_CB_ABORT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cus__load_module(struct cus *cus, struct conf_load *conf,
|
|
Dwfl_Module *mod, Dwarf *dw, Elf *elf,
|
|
const char *filename)
|
|
{
|
|
Dwarf_Off off = 0, noff;
|
|
size_t cuhl;
|
|
GElf_Addr vaddr;
|
|
const unsigned char *build_id = NULL;
|
|
uint8_t pointer_size, offset_size;
|
|
|
|
#ifdef HAVE_DWFL_MODULE_BUILD_ID
|
|
int build_id_len = dwfl_module_build_id(mod, &build_id, &vaddr);
|
|
#else
|
|
int build_id_len = 0;
|
|
#endif
|
|
|
|
struct cu *type_cu;
|
|
struct dwarf_cu type_dcu;
|
|
int type_lsk = LSK__KEEPIT;
|
|
|
|
int res = cus__load_debug_types(cus, conf, mod, dw, elf, filename,
|
|
build_id, build_id_len,
|
|
&type_cu, &type_dcu);
|
|
if (res != 0) {
|
|
return res;
|
|
}
|
|
|
|
if (type_cu != NULL) {
|
|
type_lsk = finalize_cu(cus, type_cu, &type_dcu, conf);
|
|
if (type_lsk == LSK__KEEPIT) {
|
|
cus__add(cus, type_cu);
|
|
}
|
|
}
|
|
|
|
while (dwarf_nextcu(dw, off, &noff, &cuhl, NULL, &pointer_size,
|
|
&offset_size) == 0) {
|
|
Dwarf_Die die_mem;
|
|
Dwarf_Die *cu_die = dwarf_offdie(dw, off + cuhl, &die_mem);
|
|
|
|
/*
|
|
* DW_AT_name in DW_TAG_compile_unit can be NULL, first
|
|
* seen in:
|
|
* /usr/libexec/gcc/x86_64-redhat-linux/4.3.2/ecj1.debug
|
|
*/
|
|
const char *name = attr_string(cu_die, DW_AT_name);
|
|
struct cu *cu = cu__new(name ?: "", pointer_size,
|
|
build_id, build_id_len, filename);
|
|
if (cu == NULL)
|
|
return DWARF_CB_ABORT;
|
|
cu->uses_global_strings = true;
|
|
cu->elf = elf;
|
|
cu->dwfl = mod;
|
|
cu->extra_dbg_info = conf ? conf->extra_dbg_info : 0;
|
|
cu->has_addr_info = conf ? conf->get_addr_info : 0;
|
|
|
|
GElf_Ehdr ehdr;
|
|
if (gelf_getehdr(elf, &ehdr) == NULL) {
|
|
return DWARF_CB_ABORT;
|
|
}
|
|
cu->little_endian = ehdr.e_ident[EI_DATA] == ELFDATA2LSB;
|
|
|
|
struct dwarf_cu dcu;
|
|
|
|
dwarf_cu__init(&dcu);
|
|
dcu.cu = cu;
|
|
dcu.type_unit = type_cu ? &type_dcu : NULL;
|
|
cu->priv = &dcu;
|
|
cu->dfops = &dwarf__ops;
|
|
|
|
if (die__process_and_recode(cu_die, cu) != 0)
|
|
return DWARF_CB_ABORT;
|
|
|
|
if (finalize_cu_immediately(cus, cu, &dcu, conf)
|
|
== LSK__STOP_LOADING)
|
|
return DWARF_CB_ABORT;
|
|
|
|
off = noff;
|
|
}
|
|
|
|
if (type_lsk == LSK__DELETE)
|
|
cu__delete(type_cu);
|
|
|
|
return DWARF_CB_OK;
|
|
}
|
|
|
|
struct process_dwflmod_parms {
|
|
struct cus *cus;
|
|
struct conf_load *conf;
|
|
const char *filename;
|
|
uint32_t nr_dwarf_sections_found;
|
|
};
|
|
|
|
static int cus__process_dwflmod(Dwfl_Module *dwflmod,
|
|
void **userdata __unused,
|
|
const char *name __unused,
|
|
Dwarf_Addr base __unused,
|
|
void *arg)
|
|
{
|
|
struct process_dwflmod_parms *parms = arg;
|
|
struct cus *cus = parms->cus;
|
|
|
|
GElf_Addr dwflbias;
|
|
/*
|
|
* Does the relocation and saves the elf for later processing
|
|
* by the stealer, such as pahole_stealer, so that it don't
|
|
* have to create another Elf instance just to do things like
|
|
* reading this ELF file symtab to do CTF encoding of the
|
|
* DW_TAG_suprogram tags (functions).
|
|
*/
|
|
Elf *elf = dwfl_module_getelf(dwflmod, &dwflbias);
|
|
|
|
Dwarf_Addr dwbias;
|
|
Dwarf *dw = dwfl_module_getdwarf(dwflmod, &dwbias);
|
|
|
|
int err = DWARF_CB_OK;
|
|
if (dw != NULL) {
|
|
++parms->nr_dwarf_sections_found;
|
|
err = cus__load_module(cus, parms->conf, dwflmod, dw, elf,
|
|
parms->filename);
|
|
}
|
|
/*
|
|
* XXX We will fall back to try finding other debugging
|
|
* formats (CTF), so no point in telling this to the user
|
|
* Use for debugging.
|
|
* else
|
|
* fprintf(stderr,
|
|
* "%s: can't get debug context descriptor: %s\n",
|
|
* __func__, dwfl_errmsg(-1));
|
|
*/
|
|
|
|
return err;
|
|
}
|
|
|
|
static int cus__process_file(struct cus *cus, struct conf_load *conf, int fd,
|
|
const char *filename)
|
|
{
|
|
/* Duplicate an fd for dwfl_report_offline to swallow. */
|
|
int dwfl_fd = dup(fd);
|
|
|
|
if (dwfl_fd < 0)
|
|
return -1;
|
|
|
|
/*
|
|
* Use libdwfl in a trivial way to open the libdw handle for us.
|
|
* This takes care of applying relocations to DWARF data in ET_REL
|
|
* files.
|
|
*/
|
|
|
|
static const Dwfl_Callbacks callbacks = {
|
|
.section_address = dwfl_offline_section_address,
|
|
.find_debuginfo = dwfl_standard_find_debuginfo,
|
|
/* We use this table for core files too. */
|
|
.find_elf = dwfl_build_id_find_elf,
|
|
};
|
|
|
|
Dwfl *dwfl = dwfl_begin(&callbacks);
|
|
|
|
if (dwfl_report_offline(dwfl, filename, filename, dwfl_fd) == NULL)
|
|
return -1;
|
|
|
|
dwfl_report_end(dwfl, NULL, NULL);
|
|
|
|
struct process_dwflmod_parms parms = {
|
|
.cus = cus,
|
|
.conf = conf,
|
|
.filename = filename,
|
|
.nr_dwarf_sections_found = 0,
|
|
};
|
|
|
|
/* Process the one or more modules gleaned from this file. */
|
|
dwfl_getmodules(dwfl, cus__process_dwflmod, &parms, 0);
|
|
dwfl_end(dwfl);
|
|
return parms.nr_dwarf_sections_found ? 0 : -1;
|
|
}
|
|
|
|
static int dwarf__load_file(struct cus *cus, struct conf_load *conf,
|
|
const char *filename)
|
|
{
|
|
int fd, err;
|
|
|
|
elf_version(EV_CURRENT);
|
|
|
|
fd = open(filename, O_RDONLY);
|
|
|
|
if (fd == -1)
|
|
return -1;
|
|
|
|
err = cus__process_file(cus, conf, fd, filename);
|
|
close(fd);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int dwarf__init(void)
|
|
{
|
|
strings = strings__new();
|
|
return strings != NULL ? 0 : -ENOMEM;
|
|
}
|
|
|
|
static void dwarf__exit(void)
|
|
{
|
|
strings__delete(strings);
|
|
strings = NULL;
|
|
}
|
|
|
|
struct debug_fmt_ops dwarf__ops = {
|
|
.name = "dwarf",
|
|
.init = dwarf__init,
|
|
.exit = dwarf__exit,
|
|
.load_file = dwarf__load_file,
|
|
.strings__ptr = dwarf__strings_ptr,
|
|
.tag__decl_file = dwarf_tag__decl_file,
|
|
.tag__decl_line = dwarf_tag__decl_line,
|
|
.tag__orig_id = dwarf_tag__orig_id,
|
|
.has_alignment_info = true,
|
|
};
|