#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include <search.h>
#include "libdwP.h"
static int
findcu_cb (const void *arg1, const void *arg2)
{
struct Dwarf_CU *cu1 = (struct Dwarf_CU *) arg1;
struct Dwarf_CU *cu2 = (struct Dwarf_CU *) arg2;
if (cu1->end == 0)
{
if (cu1->start < cu2->start)
return -1;
if (cu1->start >= cu2->end)
return 1;
}
else
{
if (cu2->start < cu1->start)
return 1;
if (cu2->start >= cu1->end)
return -1;
}
return 0;
}
int
__libdw_finddbg_cb (const void *arg1, const void *arg2)
{
Dwarf *dbg1 = (Dwarf *) arg1;
Dwarf *dbg2 = (Dwarf *) arg2;
Elf_Data *dbg1_data = dbg1->sectiondata[IDX_debug_info];
unsigned char *dbg1_start = dbg1_data->d_buf;
size_t dbg1_size = dbg1_data->d_size;
Elf_Data *dbg2_data = dbg2->sectiondata[IDX_debug_info];
unsigned char *dbg2_start = dbg2_data->d_buf;
size_t dbg2_size = dbg2_data->d_size;
if (dbg1_size == 0)
{
if (dbg1_start < dbg2_start)
return -1;
if (dbg1_start >= dbg2_start + dbg2_size)
return 1;
}
else
{
if (dbg2_start < dbg1_start)
return 1;
if (dbg2_start >= dbg1_start + dbg1_size)
return -1;
}
return 0;
}
struct Dwarf_CU *
internal_function
__libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
{
Dwarf_Off *const offsetp
= debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
void **tree = debug_types ? &dbg->tu_tree : &dbg->cu_tree;
Dwarf_Off oldoff = *offsetp;
uint16_t version;
uint8_t unit_type;
uint8_t address_size;
uint8_t offset_size;
Dwarf_Off abbrev_offset;
uint64_t unit_id8;
Dwarf_Off subdie_offset;
if (__libdw_next_unit (dbg, debug_types, oldoff, offsetp, NULL,
&version, &unit_type, &abbrev_offset,
&address_size, &offset_size,
&unit_id8, &subdie_offset) != 0)
return NULL;
if (unlikely (version < 2) || unlikely (version > 5)
|| (debug_types && unlikely (version != 4)))
{
__libdw_seterrno (DWARF_E_VERSION);
return NULL;
}
if (unlikely (address_size != 4 && address_size != 8))
address_size = 8;
if (unlikely (offset_size != 4 && offset_size != 8))
offset_size = 8;
size_t sec_idx = debug_types ? IDX_debug_types : IDX_debug_info;
Elf_Data *data = dbg->sectiondata[sec_idx];
if (unlikely (*offsetp > data->d_size))
*offsetp = data->d_size;
uint32_t dwp_row;
Dwarf_Off dwp_abbrev_offset;
if (__libdw_dwp_find_unit (dbg, debug_types, oldoff, version, unit_type,
unit_id8, &dwp_row, &dwp_abbrev_offset) != 0)
return NULL;
abbrev_offset += dwp_abbrev_offset;
struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU);
newp->dbg = dbg;
newp->sec_idx = sec_idx;
newp->start = oldoff;
newp->end = *offsetp;
newp->dwp_row = dwp_row;
newp->address_size = address_size;
newp->offset_size = offset_size;
newp->version = version;
newp->unit_id8 = unit_id8;
newp->subdie_offset = subdie_offset;
Dwarf_Abbrev_Hash_init (&newp->abbrev_hash, 41);
newp->orig_abbrev_offset = newp->last_abbrev_offset = abbrev_offset;
newp->files = NULL;
newp->lines = NULL;
newp->locs = NULL;
newp->split = (Dwarf_CU *) -1;
newp->base_address = (Dwarf_Addr) -1;
newp->addr_base = (Dwarf_Off) -1;
newp->str_off_base = (Dwarf_Off) -1;
newp->ranges_base = (Dwarf_Off) -1;
newp->locs_base = (Dwarf_Off) -1;
newp->startp = data->d_buf + newp->start;
newp->endp = data->d_buf + newp->end;
if (debug_types)
newp->unit_type = DW_UT_type;
else if (version < 5)
{
newp->unit_type = DW_UT_compile;
Dwarf_Die cudie = CUDIE (newp);
int tag = INTUSE(dwarf_tag) (&cudie);
if (tag == DW_TAG_compile_unit)
{
Dwarf_Attribute dwo_id;
if (INTUSE(dwarf_attr) (&cudie, DW_AT_GNU_dwo_id, &dwo_id) != NULL)
{
Dwarf_Word id8;
if (INTUSE(dwarf_formudata) (&dwo_id, &id8) == 0)
{
if (INTUSE(dwarf_haschildren) (&cudie) == 0
&& INTUSE(dwarf_hasattr) (&cudie,
DW_AT_GNU_dwo_name) == 1)
newp->unit_type = DW_UT_skeleton;
else
newp->unit_type = DW_UT_split_compile;
newp->unit_id8 = id8;
}
}
}
else if (tag == DW_TAG_partial_unit)
newp->unit_type = DW_UT_partial;
else if (tag == DW_TAG_type_unit)
newp->unit_type = DW_UT_type;
}
else
newp->unit_type = unit_type;
if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, unit_id8, newp);
if (tsearch (newp, tree, findcu_cb) == NULL)
{
*offsetp = oldoff;
__libdw_seterrno (DWARF_E_NOMEM);
return NULL;
}
return newp;
}
struct Dwarf_CU *
internal_function
__libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool v4_debug_types)
{
void **tree = v4_debug_types ? &dbg->tu_tree : &dbg->cu_tree;
Dwarf_Off *next_offset
= v4_debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
struct Dwarf_CU fake = { .start = start, .end = 0 };
struct Dwarf_CU **found = tfind (&fake, tree, findcu_cb);
if (found != NULL)
return *found;
if (start < *next_offset)
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return NULL;
}
while (1)
{
struct Dwarf_CU *newp = __libdw_intern_next_unit (dbg, v4_debug_types);
if (newp == NULL)
return NULL;
if (start < *next_offset || start == newp->start)
return newp;
}
}
struct Dwarf_CU *
internal_function
__libdw_findcu_addr (Dwarf *dbg, void *addr)
{
void **tree;
Dwarf_Off start;
if (addr >= dbg->sectiondata[IDX_debug_info]->d_buf
&& addr < (dbg->sectiondata[IDX_debug_info]->d_buf
+ dbg->sectiondata[IDX_debug_info]->d_size))
{
tree = &dbg->cu_tree;
start = addr - dbg->sectiondata[IDX_debug_info]->d_buf;
}
else if (dbg->sectiondata[IDX_debug_types] != NULL
&& addr >= dbg->sectiondata[IDX_debug_types]->d_buf
&& addr < (dbg->sectiondata[IDX_debug_types]->d_buf
+ dbg->sectiondata[IDX_debug_types]->d_size))
{
tree = &dbg->tu_tree;
start = addr - dbg->sectiondata[IDX_debug_types]->d_buf;
}
else
return NULL;
struct Dwarf_CU fake = { .start = start, .end = 0 };
struct Dwarf_CU **found = tfind (&fake, tree, findcu_cb);
if (found != NULL)
return *found;
return NULL;
}
Dwarf *
internal_function
__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr)
{
Elf_Data fake_data = { .d_buf = addr, .d_size = 0 };
Dwarf fake = { .sectiondata[IDX_debug_info] = &fake_data };
Dwarf **found = tfind (&fake, &dbg->split_tree, __libdw_finddbg_cb);
if (found != NULL)
return *found;
return NULL;
}