/*-
* Copyright (c) 2009-2011,2014 Kai Wang
* 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.
*/
if (encode == DW_EH_PE_omit)
return (DW_DLE_NONE);
application = encode & 0xf0;
encode &= 0x0f;
switch (encode) {
case DW_EH_PE_absptr:
*val = dbg->read(data, offsetp, cie->cie_addrsize);
break;
case DW_EH_PE_uleb128:
*val = _dwarf_read_uleb128(data, offsetp);
break;
case DW_EH_PE_udata2:
*val = dbg->read(data, offsetp, 2);
break;
case DW_EH_PE_udata4:
*val = dbg->read(data, offsetp, 4);
break;
case DW_EH_PE_udata8:
*val = dbg->read(data, offsetp, 8);
break;
case DW_EH_PE_sleb128:
*val = _dwarf_read_sleb128(data, offsetp);
break;
case DW_EH_PE_sdata2:
*val = (int16_t) dbg->read(data, offsetp, 2);
break;
case DW_EH_PE_sdata4:
*val = (int32_t) dbg->read(data, offsetp, 4);
break;
case DW_EH_PE_sdata8:
*val = dbg->read(data, offsetp, 8);
break;
default:
DWARF_SET_ERROR(dbg, error, DW_DLE_FRAME_AUGMENTATION_UNKNOWN);
return (DW_DLE_FRAME_AUGMENTATION_UNKNOWN);
}
if (application == DW_EH_PE_pcrel) {
/*
* Value is relative to .eh_frame section virtual addr.
*/
switch (encode) {
case DW_EH_PE_uleb128:
case DW_EH_PE_udata2:
case DW_EH_PE_udata4:
case DW_EH_PE_udata8:
*val += pc;
break;
case DW_EH_PE_sleb128:
case DW_EH_PE_sdata2:
case DW_EH_PE_sdata4:
case DW_EH_PE_sdata8:
*val = pc + (int64_t) *val;
break;
default:
/* DW_EH_PE_absptr is absolute value. */
break;
}
}
/* XXX Applications other than DW_EH_PE_pcrel are not handled. */
return (DW_DLE_NONE);
}
static int
_dwarf_frame_parse_lsb_cie_augment(Dwarf_Debug dbg, Dwarf_Cie cie,
Dwarf_Error *error)
{
uint8_t *aug_p, *augdata_p;
uint64_t val, offset;
uint8_t encode;
int ret;
/*
* Here we're only interested in the presence of augment 'R'
* and associated CIE augment data, which describes the
* encoding scheme of FDE PC begin and range.
*/
aug_p = &cie->cie_augment[1];
augdata_p = cie->cie_augdata;
while (*aug_p != '\0') {
switch (*aug_p) {
case 'S':
break;
case 'L':
/* Skip one augment in augment data. */
augdata_p++;
break;
case 'P':
/* Skip two augments in augment data. */
encode = *augdata_p++;
offset = 0;
ret = _dwarf_frame_read_lsb_encoded(dbg, cie, &val,
augdata_p, &offset, encode, 0, error);
if (ret != DW_DLE_NONE)
return (ret);
augdata_p += offset;
break;
case 'R':
cie->cie_fde_encode = *augdata_p++;
break;
default:
DWARF_SET_ERROR(dbg, error,
DW_DLE_FRAME_AUGMENTATION_UNKNOWN);
return (DW_DLE_FRAME_AUGMENTATION_UNKNOWN);
}
aug_p++;
}
cie->cie_augment = ds->ds_data + *off;
p = (char *) ds->ds_data;
while (p[(*off)++] != '\0')
;
/* We only recognize normal .dwarf_frame and GNU .eh_frame sections. */
if (*cie->cie_augment != 0 && *cie->cie_augment != 'z') {
*off = cie->cie_offset + ((dwarf_size == 4) ? 4 : 12) +
cie->cie_length;
return (DW_DLE_NONE);
}
/* Optional EH Data field for .eh_frame section. */
if (strstr((char *)cie->cie_augment, "eh") != NULL)
cie->cie_ehdata = dbg->read(ds->ds_data, off,
dbg->dbg_pointer_size);
/* DWARF4 added "address_size" and "segment_size". */
if (cie->cie_version == 4) {
cie->cie_addrsize = dbg->read(ds->ds_data, off, 1);
cie->cie_segmentsize = dbg->read(ds->ds_data, off, 1);
} else {
/*
* Otherwise (DWARF[23]) we just set CIE addrsize to the
* debug context pointer size.
*/
cie->cie_addrsize = dbg->dbg_pointer_size;
}
/* Optional CIE augmentation data for .eh_frame section. */
if (*cie->cie_augment == 'z') {
cie->cie_auglen = _dwarf_read_uleb128(ds->ds_data, off);
cie->cie_augdata = ds->ds_data + *off;
*off += cie->cie_auglen;
/*
* XXX Use DW_EH_PE_absptr for default FDE PC start/range,
* in case _dwarf_frame_parse_lsb_cie_augment fails to
* find out the real encode.
*/
cie->cie_fde_encode = DW_EH_PE_absptr;
ret = _dwarf_frame_parse_lsb_cie_augment(dbg, cie, error);
if (ret != DW_DLE_NONE)
return (ret);
}
if (eh_frame) {
fde->fde_cieoff = dbg->read(ds->ds_data, off, 4);
cieoff = *off - (4 + fde->fde_cieoff);
/* This delta should never be 0. */
if (cieoff == fde->fde_offset) {
DWARF_SET_ERROR(dbg, error, DW_DLE_NO_CIE_FOR_FDE);
return (DW_DLE_NO_CIE_FOR_FDE);
}
} else {
fde->fde_cieoff = dbg->read(ds->ds_data, off, dwarf_size);
cieoff = fde->fde_cieoff;
}
if (_dwarf_frame_find_cie(fs, cieoff, &cie) ==
DW_DLE_NO_ENTRY) {
ret = _dwarf_frame_add_cie(dbg, fs, ds, &cieoff, &cie,
error);
if (ret != DW_DLE_NONE)
return (ret);
}
fde->fde_cie = cie;
if (eh_frame) {
/*
* The FDE PC start/range for .eh_frame is encoded according
* to the LSB spec's extension to DWARF2.
*/
ret = _dwarf_frame_read_lsb_encoded(dbg, cie, &val,
ds->ds_data, off, cie->cie_fde_encode, ds->ds_addr + *off,
error);
if (ret != DW_DLE_NONE)
return (ret);
fde->fde_initloc = val;
/*
* FDE PC range should not be relative value to anything.
* So pass 0 for pc value.
*/
ret = _dwarf_frame_read_lsb_encoded(dbg, cie, &val,
ds->ds_data, off, cie->cie_fde_encode, 0, error);
if (ret != DW_DLE_NONE)
return (ret);
fde->fde_adrange = val;
} else {
fde->fde_initloc = dbg->read(ds->ds_data, off,
cie->cie_addrsize);
fde->fde_adrange = dbg->read(ds->ds_data, off,
cie->cie_addrsize);
}
/* Optional FDE augmentation data for .eh_frame section. (ignored) */
if (eh_frame && *cie->cie_augment == 'z') {
fde->fde_auglen = _dwarf_read_uleb128(ds->ds_data, off);
fde->fde_augdata = ds->ds_data + *off;
*off += fde->fde_auglen;
}
for (i = 0; i < (*dest)->rt3_reg_table_size &&
i < src->rt3_reg_table_size; i++)
memcpy(&(*dest)->rt3_rules[i], &src->rt3_rules[i],
sizeof(Dwarf_Regtable_Entry3));
for (; i < (*dest)->rt3_reg_table_size; i++)
(*dest)->rt3_rules[i].dw_regnum =
dbg->dbg_frame_undefined_value;
return (DW_DLE_NONE);
}
int
_dwarf_frame_get_internal_table(Dwarf_Fde fde, Dwarf_Addr pc_req,
Dwarf_Regtable3 **ret_rt, Dwarf_Addr *ret_row_pc, Dwarf_Error *error)
{
Dwarf_Debug dbg;
Dwarf_Cie cie;
Dwarf_Regtable3 *rt;
Dwarf_Addr row_pc;
int i, ret;
assert(ret_rt != NULL);
dbg = fde->fde_dbg;
assert(dbg != NULL);
rt = dbg->dbg_internal_reg_table;
/* Clear the content of regtable from previous run. */
memset(&rt->rt3_cfa_rule, 0, sizeof(Dwarf_Regtable_Entry3));
memset(rt->rt3_rules, 0, rt->rt3_reg_table_size *
sizeof(Dwarf_Regtable_Entry3));
/* Set rules to initial values. */
for (i = 0; i < rt->rt3_reg_table_size; i++)
rt->rt3_rules[i].dw_regnum = dbg->dbg_frame_rule_initial_value;
/* Run initial instructions in CIE. */
cie = fde->fde_cie;
assert(cie != NULL);
ret = _dwarf_frame_run_inst(dbg, rt, cie->cie_addrsize,
cie->cie_initinst, cie->cie_instlen, cie->cie_caf, cie->cie_daf, 0,
~0ULL, &row_pc, error);
if (ret != DW_DLE_NONE)
return (ret);
/* Run instructions in FDE. */
if (pc_req >= fde->fde_initloc) {
ret = _dwarf_frame_run_inst(dbg, rt, cie->cie_addrsize,
fde->fde_inst, fde->fde_instlen, cie->cie_caf,
cie->cie_daf, fde->fde_initloc, pc_req, &row_pc, error);
if (ret != DW_DLE_NONE)
return (ret);
}
RCHECK(WRITE_VALUE(op, 1));
if (op == DW_CFA_nop)
return (DW_DLE_NONE);
high2 = op & 0xc0;
low6 = op & 0x3f;
if (high2 > 0) {
switch (high2) {
case DW_CFA_advance_loc:
case DW_CFA_restore:
break;
case DW_CFA_offset:
RCHECK(WRITE_ULEB128(val1));
break;
default:
DWARF_SET_ERROR(dbg, error,
DW_DLE_FRAME_INSTR_EXEC_ERROR);
return (DW_DLE_FRAME_INSTR_EXEC_ERROR);
}
return (DW_DLE_NONE);
}
switch (low6) {
case DW_CFA_set_loc:
RCHECK(WRITE_VALUE(val1, dbg->dbg_pointer_size));
break;
case DW_CFA_advance_loc1:
RCHECK(WRITE_VALUE(val1, 1));
break;
case DW_CFA_advance_loc2:
RCHECK(WRITE_VALUE(val1, 2));
break;
case DW_CFA_advance_loc4:
RCHECK(WRITE_VALUE(val1, 4));
break;
case DW_CFA_offset_extended:
case DW_CFA_def_cfa:
case DW_CFA_register:
RCHECK(WRITE_ULEB128(val1));
RCHECK(WRITE_ULEB128(val2));
break;
case DW_CFA_restore_extended:
case DW_CFA_undefined:
case DW_CFA_same_value:
case DW_CFA_def_cfa_register:
case DW_CFA_def_cfa_offset:
RCHECK(WRITE_ULEB128(val1));
break;
case DW_CFA_remember_state:
case DW_CFA_restore_state:
break;
default:
DWARF_SET_ERROR(dbg, error, DW_DLE_FRAME_INSTR_EXEC_ERROR);
return (DW_DLE_FRAME_INSTR_EXEC_ERROR);
}
/*
* Write FDE address range. Use a pair of relocation entries if
* application provided end symbol index. Otherwise write the
* length without assoicating any relocation info.
*/
if (fde->fde_esymndx > 0)
RCHECK(_dwarf_reloc_entry_add_pair(dbg, drs, ds,
dbg->dbg_pointer_size, ds->ds_size, fde->fde_symndx,
fde->fde_esymndx, fde->fde_initloc, fde->fde_eoff, error));
else
RCHECK(WRITE_VALUE(fde->fde_adrange, dbg->dbg_pointer_size));