#include "config.h"
#include "dwarf_incl.h"
#include "dwarf_loc.h"
static Dwarf_Locdesc *
_dwarf_get_locdesc(Dwarf_Debug dbg,
Dwarf_Block * loc_block,
Dwarf_Addr lowpc,
Dwarf_Addr highpc, Dwarf_Error * error)
{
Dwarf_Unsigned loc_len;
Dwarf_Small *loc_ptr;
Dwarf_Small atom;
Dwarf_Unsigned offset;
Dwarf_Unsigned operand1, operand2;
Dwarf_Loc_Chain curr_loc, prev_loc, head_loc = NULL;
Dwarf_Unsigned op_count;
Dwarf_Loc *block_loc;
Dwarf_Locdesc *locdesc;
Dwarf_Word leb128_length;
Dwarf_Unsigned i;
loc_len = loc_block->bl_len;
loc_ptr = loc_block->bl_data;
offset = 0;
op_count = 0;
while (offset < loc_len) {
operand1 = 0;
operand2 = 0;
op_count++;
atom = *(Dwarf_Small *) loc_ptr;
loc_ptr++;
offset++;
curr_loc =
(Dwarf_Loc_Chain) _dwarf_get_alloc(dbg, DW_DLA_LOC_CHAIN,
1);
if (curr_loc == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (NULL);
}
curr_loc->lc_offset = offset;
curr_loc->lc_atom = atom;
switch (atom) {
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31:
break;
case DW_OP_regx:
operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_lit0:
case DW_OP_lit1:
case DW_OP_lit2:
case DW_OP_lit3:
case DW_OP_lit4:
case DW_OP_lit5:
case DW_OP_lit6:
case DW_OP_lit7:
case DW_OP_lit8:
case DW_OP_lit9:
case DW_OP_lit10:
case DW_OP_lit11:
case DW_OP_lit12:
case DW_OP_lit13:
case DW_OP_lit14:
case DW_OP_lit15:
case DW_OP_lit16:
case DW_OP_lit17:
case DW_OP_lit18:
case DW_OP_lit19:
case DW_OP_lit20:
case DW_OP_lit21:
case DW_OP_lit22:
case DW_OP_lit23:
case DW_OP_lit24:
case DW_OP_lit25:
case DW_OP_lit26:
case DW_OP_lit27:
case DW_OP_lit28:
case DW_OP_lit29:
case DW_OP_lit30:
case DW_OP_lit31:
operand1 = atom - DW_OP_lit0;
break;
case DW_OP_addr:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned,
loc_ptr, dbg->de_pointer_size);
loc_ptr += dbg->de_pointer_size;
offset += dbg->de_pointer_size;
break;
case DW_OP_const1u:
operand1 = *(Dwarf_Small *) loc_ptr;
loc_ptr = loc_ptr + 1;
offset = offset + 1;
break;
case DW_OP_const1s:
operand1 = *(Dwarf_Sbyte *) loc_ptr;
loc_ptr = loc_ptr + 1;
offset = offset + 1;
break;
case DW_OP_const2u:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
loc_ptr = loc_ptr + 2;
offset = offset + 2;
break;
case DW_OP_const2s:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
loc_ptr = loc_ptr + 2;
offset = offset + 2;
break;
case DW_OP_const4u:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 4);
loc_ptr = loc_ptr + 4;
offset = offset + 4;
break;
case DW_OP_const4s:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 4);
loc_ptr = loc_ptr + 4;
offset = offset + 4;
break;
case DW_OP_const8u:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 8);
loc_ptr = loc_ptr + 8;
offset = offset + 8;
break;
case DW_OP_const8s:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 8);
loc_ptr = loc_ptr + 8;
offset = offset + 8;
break;
case DW_OP_constu:
operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_consts:
operand1 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_fbreg:
operand1 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31:
operand1 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_bregx:
operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
operand2 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_dup:
case DW_OP_drop:
break;
case DW_OP_pick:
operand1 = *(Dwarf_Small *) loc_ptr;
loc_ptr = loc_ptr + 1;
offset = offset + 1;
break;
case DW_OP_over:
case DW_OP_swap:
case DW_OP_rot:
case DW_OP_deref:
break;
case DW_OP_deref_size:
operand1 = *(Dwarf_Small *) loc_ptr;
loc_ptr = loc_ptr + 1;
offset = offset + 1;
break;
case DW_OP_xderef:
break;
case DW_OP_xderef_size:
operand1 = *(Dwarf_Small *) loc_ptr;
loc_ptr = loc_ptr + 1;
offset = offset + 1;
break;
case DW_OP_abs:
case DW_OP_and:
case DW_OP_div:
case DW_OP_minus:
case DW_OP_mod:
case DW_OP_mul:
case DW_OP_neg:
case DW_OP_not:
case DW_OP_or:
case DW_OP_plus:
break;
case DW_OP_plus_uconst:
operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_shl:
case DW_OP_shr:
case DW_OP_shra:
case DW_OP_xor:
break;
case DW_OP_le:
case DW_OP_ge:
case DW_OP_eq:
case DW_OP_lt:
case DW_OP_gt:
case DW_OP_ne:
break;
case DW_OP_skip:
case DW_OP_bra:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
loc_ptr = loc_ptr + 2;
offset = offset + 2;
break;
case DW_OP_piece:
operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
case DW_OP_nop:
break;
case DW_OP_push_object_address:
break;
case DW_OP_call2:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
loc_ptr = loc_ptr + 2;
offset = offset + 2;
break;
case DW_OP_call4:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 4);
loc_ptr = loc_ptr + 4;
offset = offset + 4;
break;
case DW_OP_call_ref:
READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr,
dbg->de_length_size);
loc_ptr = loc_ptr + dbg->de_length_size;
offset = offset + dbg->de_length_size;
break;
case DW_OP_form_tls_address:
break;
case DW_OP_call_frame_cfa:
break;
case DW_OP_bit_piece:
operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
operand2 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
loc_ptr = loc_ptr + leb128_length;
offset = offset + leb128_length;
break;
default:
_dwarf_error(dbg, error, DW_DLE_LOC_EXPR_BAD);
return (NULL);
}
curr_loc->lc_number = operand1;
curr_loc->lc_number2 = operand2;
if (head_loc == NULL)
head_loc = prev_loc = curr_loc;
else {
prev_loc->lc_next = curr_loc;
prev_loc = curr_loc;
}
}
block_loc =
(Dwarf_Loc *) _dwarf_get_alloc(dbg, DW_DLA_LOC_BLOCK, op_count);
if (block_loc == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (NULL);
}
curr_loc = head_loc;
for (i = 0; i < op_count; i++) {
(block_loc + i)->lr_atom = curr_loc->lc_atom;
(block_loc + i)->lr_number = curr_loc->lc_number;
(block_loc + i)->lr_number2 = curr_loc->lc_number2;
(block_loc + i)->lr_offset = curr_loc->lc_offset;
prev_loc = curr_loc;
curr_loc = curr_loc->lc_next;
dwarf_dealloc(dbg, prev_loc, DW_DLA_LOC_CHAIN);
}
locdesc =
(Dwarf_Locdesc *) _dwarf_get_alloc(dbg, DW_DLA_LOCDESC, 1);
if (locdesc == NULL) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (NULL);
}
locdesc->ld_cents = op_count;
locdesc->ld_s = block_loc;
locdesc->ld_from_loclist = loc_block->bl_from_loclist;
locdesc->ld_section_offset = loc_block->bl_section_offset;
locdesc->ld_lopc = lowpc;
locdesc->ld_hipc = highpc;
return (locdesc);
}
#define MAX_ADDR ((address_size == 8)?0xffffffffffffffffULL:0xffffffff)
static int
_dwarf_read_loc_section(Dwarf_Debug dbg,
Dwarf_Block * return_block,
Dwarf_Addr * lowpc, Dwarf_Addr * hipc,
Dwarf_Off sec_offset, Dwarf_Error * error)
{
Dwarf_Small *beg = dbg->de_debug_loc + sec_offset;
int address_size = dbg->de_pointer_size;
Dwarf_Addr start_addr = 0;
Dwarf_Addr end_addr = 0;
Dwarf_Half exprblock_size = 0;
Dwarf_Unsigned exprblock_off =
2 * address_size + sizeof(Dwarf_Half);
if (sec_offset >= dbg->de_debug_loc_size) {
return DW_DLV_NO_ENTRY;
}
if (exprblock_off > dbg->de_debug_loc_size) {
_dwarf_error(NULL, error, DW_DLE_DEBUG_LOC_SECTION_SHORT);
return DW_DLV_ERROR;
}
READ_UNALIGNED(dbg, start_addr, Dwarf_Addr, beg, address_size);
READ_UNALIGNED(dbg, end_addr, Dwarf_Addr,
beg + address_size, address_size);
if (start_addr == 0 && end_addr == 0) {
exprblock_size = 0;
exprblock_off -= sizeof(Dwarf_Half);
} else if (start_addr == MAX_ADDR) {
exprblock_size = 0;
exprblock_off -= sizeof(Dwarf_Half);
} else {
READ_UNALIGNED(dbg, exprblock_size, Dwarf_Half,
beg + 2 * address_size, sizeof(Dwarf_Half));
if ((exprblock_off + exprblock_size) > dbg->de_debug_loc_size) {
_dwarf_error(NULL, error, DW_DLE_DEBUG_LOC_SECTION_SHORT);
return DW_DLV_ERROR;
}
}
#undef MAX_ADDR
*lowpc = start_addr;
*hipc = end_addr;
return_block->bl_len = exprblock_size;
return_block->bl_from_loclist = 1;
return_block->bl_data = beg + exprblock_off;
return_block->bl_section_offset =
((Dwarf_Small *) return_block->bl_data) - dbg->de_debug_loc;
return DW_DLV_OK;
}
static int
_dwarf_get_loclist_count(Dwarf_Debug dbg,
Dwarf_Off loclist_offset,
int *loclist_count, Dwarf_Error * error)
{
int count = 0;
Dwarf_Off offset = loclist_offset;
for (;;) {
Dwarf_Block b;
Dwarf_Addr lowpc;
Dwarf_Addr highpc;
int res = _dwarf_read_loc_section(dbg, &b,
&lowpc, &highpc,
offset, error);
if (res != DW_DLV_OK) {
return res;
}
offset = b.bl_len + b.bl_section_offset;
if (lowpc == 0 && highpc == 0) {
break;
}
count++;
}
*loclist_count = count;
return DW_DLV_OK;
}
static int
_dwarf_setup_loc(Dwarf_Attribute attr,
Dwarf_Debug * dbg_ret,
Dwarf_CU_Context *cucontext_ret,
Dwarf_Half * form_ret, Dwarf_Error * error)
{
Dwarf_Debug dbg = 0;
Dwarf_Half form = 0;
int blkres;
if (attr == NULL) {
_dwarf_error(NULL, error, DW_DLE_ATTR_NULL);
return (DW_DLV_ERROR);
}
if (attr->ar_cu_context == NULL) {
_dwarf_error(NULL, error, DW_DLE_ATTR_NO_CU_CONTEXT);
return (DW_DLV_ERROR);
}
*cucontext_ret = attr->ar_cu_context;
dbg = attr->ar_cu_context->cc_dbg;
if (dbg == NULL) {
_dwarf_error(NULL, error, DW_DLE_ATTR_DBG_NULL);
return (DW_DLV_ERROR);
}
*dbg_ret = dbg;
blkres = dwarf_whatform(attr, &form, error);
if (blkres != DW_DLV_OK) {
_dwarf_error(dbg, error, DW_DLE_LOC_EXPR_BAD);
return blkres;
}
*form_ret = form;
return DW_DLV_OK;
}
static int
_dwarf_get_loclist_header_start(Dwarf_Debug dbg,
Dwarf_Attribute attr,
Dwarf_Unsigned * loclist_offset,
Dwarf_Error * error)
{
int secload = 0;
int blkres = dwarf_formudata(attr, loclist_offset, error);
if (blkres != DW_DLV_OK) {
return (blkres);
}
if (!dbg->de_debug_loc) {
secload = _dwarf_load_section(dbg,
dbg->de_debug_loc_index,
&dbg->de_debug_loc, error);
if (secload != DW_DLV_OK) {
return secload;
}
}
return DW_DLV_OK;
}
static void
_dwarf_cleanup_llbuf(Dwarf_Debug dbg, Dwarf_Locdesc ** llbuf, int count)
{
int i;
for (i = 0; i < count; ++i) {
dwarf_dealloc(dbg, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
dwarf_dealloc(dbg, llbuf[i], DW_DLA_LOCDESC);
}
dwarf_dealloc(dbg, llbuf, DW_DLA_LIST);
}
int
dwarf_loclist_n(Dwarf_Attribute attr,
Dwarf_Locdesc *** llbuf_out,
Dwarf_Signed * listlen_out, Dwarf_Error * error)
{
Dwarf_Debug dbg;
Dwarf_Attribute loc_attr = attr;
Dwarf_Block loc_block;
Dwarf_Locdesc *locdesc = 0;
Dwarf_Half form = 0;
Dwarf_Addr lowpc = 0;
Dwarf_Addr highpc = 0;
Dwarf_Signed listlen = 0;
Dwarf_Locdesc **llbuf = 0;
Dwarf_CU_Context cucontext = 0;
unsigned address_size = 0;
int blkres;
int setup_res;
setup_res = _dwarf_setup_loc(attr, &dbg, &cucontext, &form, error);
if (setup_res != DW_DLV_OK) {
return setup_res;
}
address_size = cucontext->cc_address_size;
if (((cucontext->cc_version_stamp == CURRENT_VERSION_STAMP ||
cucontext->cc_version_stamp == CURRENT_VERSION_STAMP3) &&
(form == DW_FORM_data4 || form == DW_FORM_data8)) ||
(cucontext->cc_version_stamp == CURRENT_VERSION_STAMP4 &&
form == DW_FORM_sec_offset))
{
Dwarf_Unsigned loclist_offset = 0;
int off_res;
int count_res;
int loclist_count;
int lli;
off_res = _dwarf_get_loclist_header_start(dbg,
attr, &loclist_offset,
error);
if (off_res != DW_DLV_OK) {
return off_res;
}
count_res = _dwarf_get_loclist_count(dbg, loclist_offset,
&loclist_count, error);
listlen = loclist_count;
if (count_res != DW_DLV_OK) {
return count_res;
}
if (loclist_count == 0) {
return DW_DLV_NO_ENTRY;
}
llbuf = (Dwarf_Locdesc **)
_dwarf_get_alloc(dbg, DW_DLA_LIST, loclist_count);
if (!llbuf) {
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (DW_DLV_ERROR);
}
for (lli = 0; lli < loclist_count; ++lli) {
blkres = _dwarf_read_loc_section(dbg, &loc_block,
&lowpc,
&highpc,
loclist_offset, error);
if (blkres != DW_DLV_OK) {
_dwarf_cleanup_llbuf(dbg, llbuf, lli);
return (blkres);
}
locdesc = _dwarf_get_locdesc(dbg, &loc_block,
lowpc, highpc, error);
if (locdesc == NULL) {
_dwarf_cleanup_llbuf(dbg, llbuf, lli);
return (DW_DLV_ERROR);
}
llbuf[lli] = locdesc;
loclist_offset = loc_block.bl_section_offset +
loc_block.bl_len;
}
} else {
Dwarf_Block *tblock = 0;
blkres = dwarf_formblock(loc_attr, &tblock, error);
if (blkres != DW_DLV_OK) {
return (blkres);
}
loc_block = *tblock;
dwarf_dealloc(dbg, tblock, DW_DLA_BLOCK);
listlen = 1;
lowpc = 0;
highpc = (Dwarf_Unsigned) (-1LL);
locdesc = _dwarf_get_locdesc(dbg, &loc_block,
lowpc, highpc, error);
if (locdesc == NULL) {
return (DW_DLV_ERROR);
}
llbuf = (Dwarf_Locdesc **)
_dwarf_get_alloc(dbg, DW_DLA_LIST, listlen);
if (!llbuf) {
dwarf_dealloc(dbg, locdesc, DW_DLA_LOCDESC);
_dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
return (DW_DLV_ERROR);
}
llbuf[0] = locdesc;
}
*llbuf_out = llbuf;
*listlen_out = listlen;
return (DW_DLV_OK);
}
int
dwarf_loclist(Dwarf_Attribute attr,
Dwarf_Locdesc ** llbuf,
Dwarf_Signed * listlen, Dwarf_Error * error)
{
Dwarf_Debug dbg;
Dwarf_Attribute loc_attr = attr;
Dwarf_Block loc_block;
Dwarf_Locdesc *locdesc = 0;
Dwarf_Half form = 0;
Dwarf_Addr lowpc = 0;
Dwarf_Addr highpc = 0;
Dwarf_CU_Context cucontext = 0;
unsigned address_size = 0;
int blkres;
int setup_res;
setup_res = _dwarf_setup_loc(attr, &dbg, &cucontext, &form, error);
if (setup_res != DW_DLV_OK) {
return setup_res;
}
address_size = cucontext->cc_address_size;
if (((cucontext->cc_version_stamp == CURRENT_VERSION_STAMP ||
cucontext->cc_version_stamp == CURRENT_VERSION_STAMP3) &&
(form == DW_FORM_data4 || form == DW_FORM_data8)) ||
(cucontext->cc_version_stamp == CURRENT_VERSION_STAMP4 &&
form == DW_FORM_sec_offset))
{
Dwarf_Unsigned loclist_offset = 0;
int off_res;
off_res = _dwarf_get_loclist_header_start(dbg,
attr, &loclist_offset,
error);
if (off_res != DW_DLV_OK) {
return off_res;
}
blkres = _dwarf_read_loc_section(dbg, &loc_block,
&lowpc,
&highpc,
loclist_offset, error);
if (blkres != DW_DLV_OK) {
return (blkres);
}
} else {
Dwarf_Block *tblock = 0;
blkres = dwarf_formblock(loc_attr, &tblock, error);
if (blkres != DW_DLV_OK) {
return (blkres);
}
loc_block = *tblock;
dwarf_dealloc(dbg, tblock, DW_DLA_BLOCK);
lowpc = 0;
highpc = (Dwarf_Unsigned) (-1LL);
}
locdesc = _dwarf_get_locdesc(dbg, &loc_block, lowpc, highpc, error);
if (locdesc == NULL) {
return (DW_DLV_ERROR);
}
*llbuf = locdesc;
*listlen = 1;
return (DW_DLV_OK);
}
int
dwarf_get_loclist_entry(Dwarf_Debug dbg,
Dwarf_Unsigned offset,
Dwarf_Addr * hipc_offset,
Dwarf_Addr * lopc_offset,
Dwarf_Ptr * data,
Dwarf_Unsigned * entry_len,
Dwarf_Unsigned * next_entry,
Dwarf_Error * error)
{
Dwarf_Block b;
Dwarf_Addr lowpc;
Dwarf_Addr highpc;
int res;
if (!dbg->de_debug_loc) {
int secload = _dwarf_load_section(dbg,
dbg->de_debug_loc_index,
&dbg->de_debug_loc,
error);
if (secload != DW_DLV_OK) {
return secload;
}
}
res = _dwarf_read_loc_section(dbg,
&b, &lowpc, &highpc, offset, error);
if (res != DW_DLV_OK) {
return res;
}
*hipc_offset = highpc;
*lopc_offset = lowpc;
*entry_len = b.bl_len;
*data = b.bl_data;
*next_entry = b.bl_len + b.bl_section_offset;
return DW_DLV_OK;
}