642 lines
13 KiB
C
642 lines
13 KiB
C
/*-
|
|
* Copyright (c) 2007 John Birrell (jb@freebsd.org)
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "_libdwarf.h"
|
|
|
|
ELFTC_VCSID("$Id: libdwarf_loc.c 2070 2011-10-27 03:05:32Z jkoshy $");
|
|
|
|
/*
|
|
* Given an array of bytes of length 'len' representing a
|
|
* DWARF expression, compute the number of operations based
|
|
* on there being one byte describing the operation and
|
|
* zero or more bytes of operands as defined in the standard
|
|
* for each operation type. Also, if lbuf is non-null, store
|
|
* the opcode and oprand in it.
|
|
*/
|
|
static int
|
|
_dwarf_loc_fill_loc(Dwarf_Debug dbg, Dwarf_Locdesc *lbuf, uint8_t pointer_size,
|
|
uint8_t *p, int len)
|
|
{
|
|
int count;
|
|
uint64_t operand1;
|
|
uint64_t operand2;
|
|
uint8_t *ps, *pe;
|
|
|
|
count = 0;
|
|
ps = p;
|
|
pe = p + len;
|
|
|
|
/*
|
|
* Process each byte. If an error occurs, then the
|
|
* count will be set to -1.
|
|
*/
|
|
while (p < pe) {
|
|
|
|
operand1 = 0;
|
|
operand2 = 0;
|
|
|
|
if (lbuf != NULL) {
|
|
lbuf->ld_s[count].lr_atom = *p;
|
|
lbuf->ld_s[count].lr_offset = p - ps;
|
|
}
|
|
|
|
switch (*p++) {
|
|
/* Operations with no operands. */
|
|
case DW_OP_deref:
|
|
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:
|
|
|
|
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:
|
|
|
|
case DW_OP_dup:
|
|
case DW_OP_drop:
|
|
|
|
case DW_OP_over:
|
|
|
|
case DW_OP_swap:
|
|
case DW_OP_rot:
|
|
case DW_OP_xderef:
|
|
|
|
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:
|
|
|
|
case DW_OP_shl:
|
|
case DW_OP_shr:
|
|
case DW_OP_shra:
|
|
case DW_OP_xor:
|
|
|
|
case DW_OP_eq:
|
|
case DW_OP_ge:
|
|
case DW_OP_gt:
|
|
case DW_OP_le:
|
|
case DW_OP_lt:
|
|
case DW_OP_ne:
|
|
|
|
case DW_OP_nop:
|
|
case DW_OP_form_tls_address:
|
|
case DW_OP_call_frame_cfa:
|
|
case DW_OP_stack_value:
|
|
case DW_OP_GNU_push_tls_address:
|
|
break;
|
|
|
|
/* Operations with 1-byte operands. */
|
|
case DW_OP_const1u:
|
|
case DW_OP_const1s:
|
|
case DW_OP_pick:
|
|
case DW_OP_deref_size:
|
|
case DW_OP_xderef_size:
|
|
operand1 = *p++;
|
|
break;
|
|
|
|
/* Operations with 2-byte operands. */
|
|
case DW_OP_call2:
|
|
case DW_OP_const2u:
|
|
case DW_OP_const2s:
|
|
case DW_OP_bra:
|
|
case DW_OP_skip:
|
|
operand1 = dbg->decode(&p, 2);
|
|
break;
|
|
|
|
/* Operations with 4-byte operands. */
|
|
case DW_OP_call4:
|
|
case DW_OP_const4u:
|
|
case DW_OP_const4s:
|
|
operand1 = dbg->decode(&p, 4);
|
|
break;
|
|
|
|
/* Operations with 8-byte operands. */
|
|
case DW_OP_const8u:
|
|
case DW_OP_const8s:
|
|
operand1 = dbg->decode(&p, 8);
|
|
break;
|
|
|
|
/* Operations with an unsigned LEB128 operand. */
|
|
case DW_OP_constu:
|
|
case DW_OP_plus_uconst:
|
|
case DW_OP_regx:
|
|
case DW_OP_piece:
|
|
operand1 = _dwarf_decode_uleb128(&p);
|
|
break;
|
|
|
|
/* Operations with a signed LEB128 operand. */
|
|
case DW_OP_consts:
|
|
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:
|
|
case DW_OP_fbreg:
|
|
operand1 = _dwarf_decode_sleb128(&p);
|
|
break;
|
|
|
|
/*
|
|
* Oeration with two unsigned LEB128 operands.
|
|
*/
|
|
case DW_OP_bit_piece:
|
|
operand1 = _dwarf_decode_uleb128(&p);
|
|
operand2 = _dwarf_decode_uleb128(&p);
|
|
break;
|
|
|
|
/*
|
|
* Operations with an unsigned LEB128 operand
|
|
* followed by a signed LEB128 operand.
|
|
*/
|
|
case DW_OP_bregx:
|
|
operand1 = _dwarf_decode_uleb128(&p);
|
|
operand2 = _dwarf_decode_sleb128(&p);
|
|
break;
|
|
|
|
/*
|
|
* Operation with an unsigned LEB128 operand
|
|
* followed by a block. Store a pointer to the
|
|
* block in the operand2.
|
|
*/
|
|
case DW_OP_implicit_value:
|
|
operand1 = _dwarf_decode_uleb128(&p);
|
|
operand2 = (Dwarf_Unsigned) (uintptr_t) p;
|
|
p += operand1;
|
|
break;
|
|
|
|
/* Target address size operand. */
|
|
case DW_OP_addr:
|
|
operand1 = dbg->decode(&p, pointer_size);
|
|
break;
|
|
|
|
/*
|
|
* XXX Opcode DW_OP_call_ref has an operand with size
|
|
* "dwarf_size". Here we use dbg->dbg_offset_size
|
|
* as "dwarf_size" to be compatible with SGI libdwarf.
|
|
* However note that dbg->dbg_offset_size is just
|
|
* a "guess" value so the parsing result of
|
|
* DW_OP_call_ref might not be correct at all. XXX
|
|
*/
|
|
case DW_OP_call_ref:
|
|
operand1 = dbg->decode(&p, dbg->dbg_offset_size);
|
|
break;
|
|
|
|
/* All other operations cause an error. */
|
|
default:
|
|
count = -1;
|
|
break;
|
|
}
|
|
|
|
if (lbuf != NULL) {
|
|
lbuf->ld_s[count].lr_number = operand1;
|
|
lbuf->ld_s[count].lr_number2 = operand2;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
return (count);
|
|
}
|
|
|
|
int
|
|
_dwarf_loc_expr_add_atom(Dwarf_Debug dbg, uint8_t *out, uint8_t *end,
|
|
Dwarf_Small atom, Dwarf_Unsigned operand1, Dwarf_Unsigned operand2,
|
|
int *length, Dwarf_Error *error)
|
|
{
|
|
uint8_t buf[64];
|
|
uint8_t *p, *pe;
|
|
uint64_t offset;
|
|
int len;
|
|
|
|
if (out != NULL && end != NULL) {
|
|
p = out;
|
|
pe = end;
|
|
} else {
|
|
p = out = buf;
|
|
pe = &buf[sizeof(buf)];
|
|
}
|
|
|
|
switch (atom) {
|
|
/* Operations with no operands. */
|
|
case DW_OP_deref:
|
|
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:
|
|
|
|
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:
|
|
|
|
case DW_OP_dup:
|
|
case DW_OP_drop:
|
|
|
|
case DW_OP_over:
|
|
|
|
case DW_OP_swap:
|
|
case DW_OP_rot:
|
|
case DW_OP_xderef:
|
|
|
|
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:
|
|
|
|
case DW_OP_shl:
|
|
case DW_OP_shr:
|
|
case DW_OP_shra:
|
|
case DW_OP_xor:
|
|
|
|
case DW_OP_eq:
|
|
case DW_OP_ge:
|
|
case DW_OP_gt:
|
|
case DW_OP_le:
|
|
case DW_OP_lt:
|
|
case DW_OP_ne:
|
|
|
|
case DW_OP_nop:
|
|
case DW_OP_GNU_push_tls_address:
|
|
*p++ = atom;
|
|
break;
|
|
|
|
/* Operations with 1-byte operands. */
|
|
case DW_OP_const1u:
|
|
case DW_OP_const1s:
|
|
case DW_OP_pick:
|
|
case DW_OP_deref_size:
|
|
case DW_OP_xderef_size:
|
|
*p++ = atom;
|
|
*p++ = (uint8_t) operand1;
|
|
break;
|
|
|
|
/* Operations with 2-byte operands. */
|
|
case DW_OP_const2u:
|
|
case DW_OP_const2s:
|
|
case DW_OP_bra:
|
|
case DW_OP_skip:
|
|
*p++ = atom;
|
|
offset = 0;
|
|
dbg->write(p, &offset, operand1, 2);
|
|
p += 2;
|
|
break;
|
|
|
|
/* Operations with 4-byte operands. */
|
|
case DW_OP_const4u:
|
|
case DW_OP_const4s:
|
|
*p++ = atom;
|
|
offset = 0;
|
|
dbg->write(p, &offset, operand1, 4);
|
|
p += 4;
|
|
break;
|
|
|
|
/* Operations with 8-byte operands. */
|
|
case DW_OP_const8u:
|
|
case DW_OP_const8s:
|
|
*p++ = atom;
|
|
offset = 0;
|
|
dbg->write(p, &offset, operand1, 8);
|
|
p += 8;
|
|
break;
|
|
|
|
/* Operations with an unsigned LEB128 operand. */
|
|
case DW_OP_constu:
|
|
case DW_OP_plus_uconst:
|
|
case DW_OP_regx:
|
|
case DW_OP_piece:
|
|
*p++ = atom;
|
|
len = _dwarf_write_uleb128(p, pe, operand1);
|
|
assert(len > 0);
|
|
p += len;
|
|
break;
|
|
|
|
/* Operations with a signed LEB128 operand. */
|
|
case DW_OP_consts:
|
|
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:
|
|
case DW_OP_fbreg:
|
|
*p++ = atom;
|
|
len = _dwarf_write_sleb128(p, pe, operand1);
|
|
assert(len > 0);
|
|
p += len;
|
|
break;
|
|
|
|
/*
|
|
* Operations with an unsigned LEB128 operand
|
|
* followed by a signed LEB128 operand.
|
|
*/
|
|
case DW_OP_bregx:
|
|
*p++ = atom;
|
|
len = _dwarf_write_uleb128(p, pe, operand1);
|
|
assert(len > 0);
|
|
p += len;
|
|
len = _dwarf_write_sleb128(p, pe, operand2);
|
|
assert(len > 0);
|
|
p += len;
|
|
break;
|
|
|
|
/* Target address size operand. */
|
|
case DW_OP_addr:
|
|
*p++ = atom;
|
|
offset = 0;
|
|
dbg->write(p, &offset, operand1, dbg->dbg_pointer_size);
|
|
p += dbg->dbg_pointer_size;
|
|
break;
|
|
|
|
/* All other operations cause an error. */
|
|
default:
|
|
DWARF_SET_ERROR(dbg, error, DW_DLE_LOC_EXPR_BAD);
|
|
return (DW_DLE_LOC_EXPR_BAD);
|
|
}
|
|
|
|
if (length)
|
|
*length = p - out;
|
|
|
|
return (DW_DLE_NONE);
|
|
}
|
|
|
|
int
|
|
_dwarf_loc_fill_locdesc(Dwarf_Debug dbg, Dwarf_Locdesc *llbuf, uint8_t *in,
|
|
uint64_t in_len, uint8_t pointer_size, Dwarf_Error *error)
|
|
{
|
|
int num;
|
|
|
|
assert(llbuf != NULL);
|
|
assert(in != NULL);
|
|
assert(in_len > 0);
|
|
|
|
/* Compute the number of locations. */
|
|
if ((num = _dwarf_loc_fill_loc(dbg, NULL, pointer_size, in, in_len)) <
|
|
0) {
|
|
DWARF_SET_ERROR(dbg, error, DW_DLE_LOC_EXPR_BAD);
|
|
return (DW_DLE_LOC_EXPR_BAD);
|
|
}
|
|
|
|
llbuf->ld_cents = num;
|
|
if (num <= 0)
|
|
return (DW_DLE_NONE);
|
|
|
|
if ((llbuf->ld_s = calloc(num, sizeof(Dwarf_Loc))) == NULL) {
|
|
DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
|
|
return (DW_DLE_MEMORY);
|
|
}
|
|
|
|
(void) _dwarf_loc_fill_loc(dbg, llbuf, pointer_size, in, in_len);
|
|
|
|
return (DW_DLE_NONE);
|
|
}
|
|
|
|
int
|
|
_dwarf_loc_fill_locexpr(Dwarf_Debug dbg, Dwarf_Locdesc **ret_llbuf, uint8_t *in,
|
|
uint64_t in_len, uint8_t pointer_size, Dwarf_Error *error)
|
|
{
|
|
Dwarf_Locdesc *llbuf;
|
|
int ret;
|
|
|
|
if ((llbuf = malloc(sizeof(Dwarf_Locdesc))) == NULL) {
|
|
DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
|
|
return (DW_DLE_MEMORY);
|
|
}
|
|
llbuf->ld_lopc = 0;
|
|
llbuf->ld_hipc = ~0ULL;
|
|
llbuf->ld_s = NULL;
|
|
|
|
ret = _dwarf_loc_fill_locdesc(dbg, llbuf, in, in_len, pointer_size,
|
|
error);
|
|
if (ret != DW_DLE_NONE) {
|
|
free(llbuf);
|
|
return (ret);
|
|
}
|
|
|
|
*ret_llbuf = llbuf;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
_dwarf_loc_add(Dwarf_Die die, Dwarf_Attribute at, Dwarf_Error *error)
|
|
{
|
|
Dwarf_Debug dbg;
|
|
Dwarf_CU cu;
|
|
int ret;
|
|
|
|
assert(at->at_ld == NULL);
|
|
assert(at->u[1].u8p != NULL);
|
|
assert(at->u[0].u64 > 0);
|
|
|
|
cu = die->die_cu;
|
|
assert(cu != NULL);
|
|
|
|
dbg = cu->cu_dbg;
|
|
assert(dbg != NULL);
|
|
|
|
ret = _dwarf_loc_fill_locexpr(dbg, &at->at_ld, at->u[1].u8p,
|
|
at->u[0].u64, cu->cu_pointer_size, error);
|
|
|
|
return (ret);
|
|
}
|