#include "config.h"
#include "dwarf_incl.h"
#ifdef HAVE_ELF_H
#include <elf.h>
#endif
#include <stdio.h>
#include "dwarf_die_deliv.h"
static Dwarf_CU_Context
_dwarf_find_CU_Context(Dwarf_Debug dbg, Dwarf_Off offset)
{
Dwarf_CU_Context cu_context;
if (offset >= dbg->de_info_last_offset)
return (NULL);
if (dbg->de_cu_context != NULL &&
dbg->de_cu_context->cc_next != NULL &&
dbg->de_cu_context->cc_next->cc_debug_info_offset == offset) {
return (dbg->de_cu_context->cc_next);
}
if (dbg->de_cu_context != NULL &&
dbg->de_cu_context->cc_debug_info_offset <= offset) {
for (cu_context = dbg->de_cu_context;
cu_context != NULL; cu_context = cu_context->cc_next) {
if (offset >= cu_context->cc_debug_info_offset &&
offset < cu_context->cc_debug_info_offset +
cu_context->cc_length + cu_context->cc_length_size
+ cu_context->cc_extension_size) {
return (cu_context);
}
}
}
for (cu_context = dbg->de_cu_context_list;
cu_context != NULL; cu_context = cu_context->cc_next) {
if (offset >= cu_context->cc_debug_info_offset &&
offset < cu_context->cc_debug_info_offset +
cu_context->cc_length + cu_context->cc_length_size
+ cu_context->cc_extension_size) {
return (cu_context);
}
}
return (NULL);
}
static Dwarf_CU_Context
_dwarf_find_offdie_CU_Context(Dwarf_Debug dbg, Dwarf_Off offset)
{
Dwarf_CU_Context cu_context;
for (cu_context = dbg->de_offdie_cu_context;
cu_context != NULL; cu_context = cu_context->cc_next)
if (offset >= cu_context->cc_debug_info_offset &&
offset < cu_context->cc_debug_info_offset +
cu_context->cc_length + cu_context->cc_length_size
+ cu_context->cc_extension_size)
return (cu_context);
return (NULL);
}
static Dwarf_CU_Context
_dwarf_make_CU_Context(Dwarf_Debug dbg,
Dwarf_Off offset, Dwarf_Error * error)
{
Dwarf_CU_Context cu_context;
Dwarf_Unsigned length;
Dwarf_Signed abbrev_offset;
Dwarf_Byte_Ptr cu_ptr;
int local_extension_size = 0;
int local_length_size;
cu_context =
(Dwarf_CU_Context) _dwarf_get_alloc(dbg, DW_DLA_CU_CONTEXT, 1);
if (cu_context == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (NULL);
}
cu_context->cc_dbg = dbg;
cu_ptr = (Dwarf_Byte_Ptr) (dbg->de_debug_info + offset);
READ_AREA_LENGTH(dbg, length, Dwarf_Unsigned,
cu_ptr, local_length_size, local_extension_size);
cu_context->cc_length_size = local_length_size;
cu_context->cc_extension_size = local_extension_size;
cu_context->cc_length = (Dwarf_Word) length;
READ_UNALIGNED(dbg, cu_context->cc_version_stamp, Dwarf_Half,
cu_ptr, sizeof(Dwarf_Half));
cu_ptr += sizeof(Dwarf_Half);
READ_UNALIGNED(dbg, abbrev_offset, Dwarf_Signed,
cu_ptr, local_length_size);
cu_ptr += local_length_size;
cu_context->cc_abbrev_offset = (Dwarf_Sword) abbrev_offset;
cu_context->cc_address_size = *(Dwarf_Small *) cu_ptr;
if ((length < CU_VERSION_STAMP_SIZE + local_length_size +
CU_ADDRESS_SIZE_SIZE) ||
(offset + length + local_length_size +
local_extension_size > dbg->de_debug_info_size)) {
dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
_dwarf_error(dbg, error, DW_DLE_CU_LENGTH_ERROR);
return (NULL);
}
if (cu_context->cc_address_size != dbg->de_pointer_size) {
dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
_dwarf_error(dbg, error, DW_DLE_CU_ADDRESS_SIZE_BAD);
return (NULL);
}
if (cu_context->cc_version_stamp != CURRENT_VERSION_STAMP
&& cu_context->cc_version_stamp != CURRENT_VERSION_STAMP3) {
dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
_dwarf_error(dbg, error, DW_DLE_VERSION_STAMP_ERROR);
return (NULL);
}
if (abbrev_offset >= dbg->de_debug_abbrev_size) {
dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
_dwarf_error(dbg, error, DW_DLE_ABBREV_OFFSET_ERROR);
return (NULL);
}
cu_context->cc_abbrev_hash_table =
(Dwarf_Hash_Table) _dwarf_get_alloc(dbg, DW_DLA_HASH_TABLE, 1);
if (cu_context->cc_abbrev_hash_table == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (NULL);
}
cu_context->cc_debug_info_offset = (Dwarf_Word) offset;
dbg->de_info_last_offset =
(Dwarf_Word) (offset + length +
local_extension_size + local_length_size);
if (dbg->de_cu_context_list == NULL) {
dbg->de_cu_context_list = cu_context;
dbg->de_cu_context_list_end = cu_context;
} else {
dbg->de_cu_context_list_end->cc_next = cu_context;
dbg->de_cu_context_list_end = cu_context;
}
return (cu_context);
}
int
dwarf_next_cu_header(Dwarf_Debug dbg,
Dwarf_Unsigned * cu_header_length,
Dwarf_Half * version_stamp,
Dwarf_Unsigned * abbrev_offset,
Dwarf_Half * address_size,
Dwarf_Unsigned * next_cu_offset,
Dwarf_Error * error)
{
Dwarf_Unsigned new_offset;
Dwarf_CU_Context cu_context;
if (dbg == NULL) {
_dwarf_error(NULL, error, DW_DLE_DBG_NULL);
return (DW_DLV_ERROR);
}
if (dbg->de_cu_context == NULL) {
new_offset = 0;
if (!dbg->de_debug_info) {
int res = _dwarf_load_debug_info(dbg, error);
if (res != DW_DLV_OK) {
return res;
}
}
} else {
new_offset = dbg->de_cu_context->cc_debug_info_offset +
dbg->de_cu_context->cc_length +
dbg->de_cu_context->cc_length_size +
dbg->de_cu_context->cc_extension_size;
}
if ((new_offset + _dwarf_length_of_cu_header_simple(dbg)) >=
dbg->de_debug_info_size) {
dbg->de_cu_context = NULL;
return (DW_DLV_NO_ENTRY);
}
cu_context = _dwarf_find_CU_Context(dbg, new_offset);
if (cu_context == NULL) {
cu_context = _dwarf_make_CU_Context(dbg, new_offset, error);
if (cu_context == NULL) {
return (DW_DLV_ERROR);
}
}
dbg->de_cu_context = cu_context;
if (cu_header_length != NULL)
*cu_header_length = cu_context->cc_length;
if (version_stamp != NULL)
*version_stamp = cu_context->cc_version_stamp;
if (abbrev_offset != NULL)
*abbrev_offset = cu_context->cc_abbrev_offset;
if (address_size != NULL)
*address_size = cu_context->cc_address_size;
new_offset = new_offset + cu_context->cc_length +
cu_context->cc_length_size + cu_context->cc_extension_size;
*next_cu_offset = new_offset;
return (DW_DLV_OK);
}
static Dwarf_Byte_Ptr
_dwarf_next_die_info_ptr(Dwarf_Byte_Ptr die_info_ptr,
Dwarf_CU_Context cu_context,
Dwarf_Byte_Ptr die_info_end,
Dwarf_Byte_Ptr cu_info_start,
Dwarf_Bool want_AT_sibling,
Dwarf_Bool * has_die_child)
{
Dwarf_Byte_Ptr info_ptr;
Dwarf_Byte_Ptr abbrev_ptr;
Dwarf_Word abbrev_code;
Dwarf_Abbrev_List abbrev_list;
Dwarf_Half attr;
Dwarf_Half attr_form;
Dwarf_Unsigned offset;
Dwarf_Word leb128_length;
Dwarf_Unsigned utmp;
Dwarf_Debug dbg;
info_ptr = die_info_ptr;
DECODE_LEB128_UWORD(info_ptr, utmp)
abbrev_code = (Dwarf_Word) utmp;
if (abbrev_code == 0) {
return NULL;
}
abbrev_list = _dwarf_get_abbrev_for_code(cu_context, abbrev_code);
if (abbrev_list == NULL) {
return (NULL);
}
dbg = cu_context->cc_dbg;
*has_die_child = abbrev_list->ab_has_child;
abbrev_ptr = abbrev_list->ab_abbrev_ptr;
do {
Dwarf_Unsigned utmp2;
DECODE_LEB128_UWORD(abbrev_ptr, utmp2)
attr = (Dwarf_Half) utmp2;
DECODE_LEB128_UWORD(abbrev_ptr, utmp2)
attr_form = (Dwarf_Half) utmp2;
if (attr_form == DW_FORM_indirect) {
Dwarf_Unsigned utmp6;
DECODE_LEB128_UWORD(info_ptr, utmp6)
attr_form = (Dwarf_Half) utmp6;
}
if (want_AT_sibling && attr == DW_AT_sibling) {
switch (attr_form) {
case DW_FORM_ref1:
offset = *(Dwarf_Small *) info_ptr;
break;
case DW_FORM_ref2:
READ_UNALIGNED(dbg, offset, Dwarf_Unsigned,
info_ptr, sizeof(Dwarf_Half));
break;
case DW_FORM_ref4:
READ_UNALIGNED(dbg, offset, Dwarf_Unsigned,
info_ptr, sizeof(Dwarf_ufixed));
break;
case DW_FORM_ref8:
READ_UNALIGNED(dbg, offset, Dwarf_Unsigned,
info_ptr, sizeof(Dwarf_Unsigned));
break;
case DW_FORM_ref_udata:
offset =
_dwarf_decode_u_leb128(info_ptr, &leb128_length);
break;
default:
return (NULL);
}
*has_die_child = false;
if (cu_info_start + offset > die_info_end) {
return (NULL);
}
return (cu_info_start + offset);
}
if (attr_form != 0) {
info_ptr += _dwarf_get_size_of_val(cu_context->cc_dbg,
attr_form, info_ptr,
cu_context->
cc_length_size);
if (info_ptr > die_info_end) {
return (NULL);
}
}
} while (attr != 0 || attr_form != 0);
return (info_ptr);
}
int
dwarf_siblingof(Dwarf_Debug dbg,
Dwarf_Die die,
Dwarf_Die * caller_ret_die, Dwarf_Error * error)
{
Dwarf_Die ret_die;
Dwarf_Byte_Ptr die_info_ptr;
Dwarf_Byte_Ptr cu_info_start = 0;
Dwarf_Byte_Ptr die_info_end = 0;
Dwarf_Half abbrev_code;
Dwarf_Unsigned utmp;
if (dbg == NULL) {
_dwarf_error(NULL, error, DW_DLE_DBG_NULL);
return (DW_DLV_ERROR);
}
if (die == NULL) {
Dwarf_Off off2;
if (dbg->de_cu_context == NULL) {
_dwarf_error(dbg, error, DW_DLE_DBG_NO_CU_CONTEXT);
return (DW_DLV_ERROR);
}
off2 = dbg->de_cu_context->cc_debug_info_offset;
die_info_ptr = dbg->de_debug_info +
off2 + _dwarf_length_of_cu_header(dbg, off2);
} else {
Dwarf_Bool has_child = false;
Dwarf_Sword child_depth;
CHECK_DIE(die, DW_DLV_ERROR)
die_info_ptr = die->di_debug_info_ptr;
if (*die_info_ptr == 0) {
return (DW_DLV_NO_ENTRY);
}
cu_info_start = dbg->de_debug_info +
die->di_cu_context->cc_debug_info_offset;
die_info_end = cu_info_start + die->di_cu_context->cc_length +
die->di_cu_context->cc_length_size +
die->di_cu_context->cc_extension_size;
if ((*die_info_ptr) == 0) {
return (DW_DLV_NO_ENTRY);
}
child_depth = 0;
do {
die_info_ptr = _dwarf_next_die_info_ptr(die_info_ptr,
die->di_cu_context,
die_info_end,
cu_info_start, true,
&has_child);
if (die_info_ptr == NULL) {
_dwarf_error(dbg, error, DW_DLE_NEXT_DIE_PTR_NULL);
return (DW_DLV_ERROR);
}
if ((*die_info_ptr) == 0 && has_child) {
die_info_ptr++;
has_child = false;
}
if ((die_info_ptr == die_info_end) ||
((*die_info_ptr) == 0)) {
for (; child_depth > 0 && *die_info_ptr == 0;
child_depth--, die_info_ptr++);
} else {
child_depth = has_child ? child_depth + 1 : child_depth;
}
} while (child_depth != 0);
}
if (die != NULL && die_info_ptr >= die_info_end) {
return (DW_DLV_NO_ENTRY);
}
if ((*die_info_ptr) == 0) {
return (DW_DLV_NO_ENTRY);
}
ret_die = (Dwarf_Die) _dwarf_get_alloc(dbg, DW_DLA_DIE, 1);
if (ret_die == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (DW_DLV_ERROR);
}
ret_die->di_debug_info_ptr = die_info_ptr;
ret_die->di_cu_context =
die == NULL ? dbg->de_cu_context : die->di_cu_context;
DECODE_LEB128_UWORD(die_info_ptr, utmp)
abbrev_code = (Dwarf_Half) utmp;
if (abbrev_code == 0) {
dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
return (DW_DLV_NO_ENTRY);
}
ret_die->di_abbrev_list =
_dwarf_get_abbrev_for_code(ret_die->di_cu_context, abbrev_code);
if (ret_die->di_abbrev_list == NULL || (die == NULL &&
ret_die->di_abbrev_list->
ab_tag !=
DW_TAG_compile_unit)) {
dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
_dwarf_error(dbg, error, DW_DLE_FIRST_DIE_NOT_CU);
return (DW_DLV_ERROR);
}
*caller_ret_die = ret_die;
return (DW_DLV_OK);
}
int
dwarf_child(Dwarf_Die die,
Dwarf_Die * caller_ret_die, Dwarf_Error * error)
{
Dwarf_Byte_Ptr die_info_ptr = 0;
Dwarf_Byte_Ptr die_info_end = 0;
Dwarf_Die ret_die = 0;
Dwarf_Bool has_die_child = 0;
Dwarf_Debug dbg;
Dwarf_Half abbrev_code = 0;
Dwarf_Unsigned utmp = 0;
CHECK_DIE(die, DW_DLV_ERROR)
dbg = die->di_cu_context->cc_dbg;
die_info_ptr = die->di_debug_info_ptr;
if ((*die_info_ptr) == 0)
return (DW_DLV_NO_ENTRY);
die_info_end = dbg->de_debug_info +
die->di_cu_context->cc_debug_info_offset +
die->di_cu_context->cc_length +
die->di_cu_context->cc_length_size +
die->di_cu_context->cc_extension_size;
die_info_ptr =
_dwarf_next_die_info_ptr(die_info_ptr, die->di_cu_context,
die_info_end, NULL, false,
&has_die_child);
if (die_info_ptr == NULL) {
_dwarf_error(dbg, error, DW_DLE_NEXT_DIE_PTR_NULL);
return (DW_DLV_ERROR);
}
if (!has_die_child)
return (DW_DLV_NO_ENTRY);
ret_die = (Dwarf_Die) _dwarf_get_alloc(dbg, DW_DLA_DIE, 1);
if (ret_die == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (DW_DLV_ERROR);
}
ret_die->di_debug_info_ptr = die_info_ptr;
ret_die->di_cu_context = die->di_cu_context;
DECODE_LEB128_UWORD(die_info_ptr, utmp)
abbrev_code = (Dwarf_Half) utmp;
if (abbrev_code == 0) {
*caller_ret_die = 0;
dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
return DW_DLV_NO_ENTRY;
}
ret_die->di_abbrev_list =
_dwarf_get_abbrev_for_code(die->di_cu_context, abbrev_code);
if (ret_die->di_abbrev_list == NULL) {
dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
_dwarf_error(dbg, error, DW_DLE_DIE_BAD);
return (DW_DLV_ERROR);
}
*caller_ret_die = ret_die;
return (DW_DLV_OK);
}
int
dwarf_offdie(Dwarf_Debug dbg,
Dwarf_Off offset, Dwarf_Die * new_die, Dwarf_Error * error)
{
Dwarf_CU_Context cu_context;
Dwarf_Off new_cu_offset = 0;
Dwarf_Die die = 0;
Dwarf_Byte_Ptr info_ptr = 0;
Dwarf_Half abbrev_code = 0;
Dwarf_Unsigned utmp = 0;
if (dbg == NULL) {
_dwarf_error(NULL, error, DW_DLE_DBG_NULL);
return (DW_DLV_ERROR);
}
cu_context = _dwarf_find_CU_Context(dbg, offset);
if (cu_context == NULL)
cu_context = _dwarf_find_offdie_CU_Context(dbg, offset);
if (cu_context == NULL) {
int res = _dwarf_load_debug_info(dbg, error);
if (res != DW_DLV_OK) {
return res;
}
if (dbg->de_cu_context_list_end != NULL)
new_cu_offset =
dbg->de_cu_context_list_end->cc_debug_info_offset +
dbg->de_cu_context_list_end->cc_length +
dbg->de_cu_context_list_end->cc_length_size +
dbg->de_cu_context_list_end->cc_extension_size;
do {
if ((new_cu_offset +
_dwarf_length_of_cu_header_simple(dbg)) >=
dbg->de_debug_info_size) {
_dwarf_error(dbg, error, DW_DLE_OFFSET_BAD);
return (DW_DLV_ERROR);
}
cu_context =
_dwarf_make_CU_Context(dbg, new_cu_offset, error);
if (cu_context == NULL) {
return (DW_DLV_ERROR);
}
if (dbg->de_offdie_cu_context == NULL) {
dbg->de_offdie_cu_context = cu_context;
dbg->de_offdie_cu_context_end = cu_context;
} else {
dbg->de_offdie_cu_context_end->cc_next = cu_context;
dbg->de_offdie_cu_context_end = cu_context;
}
new_cu_offset = new_cu_offset + cu_context->cc_length +
cu_context->cc_length_size;
} while (offset >= new_cu_offset);
}
die = (Dwarf_Die) _dwarf_get_alloc(dbg, DW_DLA_DIE, 1);
if (die == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (DW_DLV_ERROR);
}
die->di_cu_context = cu_context;
info_ptr = dbg->de_debug_info + offset;
die->di_debug_info_ptr = info_ptr;
DECODE_LEB128_UWORD(info_ptr, utmp)
abbrev_code = (Dwarf_Half) utmp;
if (abbrev_code == 0) {
*new_die = 0;
dwarf_dealloc(dbg, die, DW_DLA_DIE);
return DW_DLV_NO_ENTRY;
}
die->di_abbrev_list =
_dwarf_get_abbrev_for_code(cu_context, abbrev_code);
if (die->di_abbrev_list == NULL) {
dwarf_dealloc(dbg, die, DW_DLA_DIE);
_dwarf_error(dbg, error, DW_DLE_DIE_ABBREV_LIST_NULL);
return (DW_DLV_ERROR);
}
*new_die = die;
return (DW_DLV_OK);
}