diff options
Diffstat (limited to 'rtemstoolkit/elftoolchain/libdwarf/libdwarf_frame.c')
-rw-r--r-- | rtemstoolkit/elftoolchain/libdwarf/libdwarf_frame.c | 1608 |
1 files changed, 1608 insertions, 0 deletions
diff --git a/rtemstoolkit/elftoolchain/libdwarf/libdwarf_frame.c b/rtemstoolkit/elftoolchain/libdwarf/libdwarf_frame.c new file mode 100644 index 0000000..cfa6971 --- /dev/null +++ b/rtemstoolkit/elftoolchain/libdwarf/libdwarf_frame.c @@ -0,0 +1,1608 @@ +/*- + * 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. + */ + +#include "_libdwarf.h" + +ELFTC_VCSID("$Id: libdwarf_frame.c 3589 2018-03-13 20:34:33Z kaiwang27 $"); + +static int +_dwarf_frame_find_cie(Dwarf_FrameSec fs, Dwarf_Unsigned offset, + Dwarf_Cie *ret_cie) +{ + Dwarf_Cie cie; + + STAILQ_FOREACH(cie, &fs->fs_cielist, cie_next) { + if (cie->cie_offset == offset) + break; + } + + if (cie == NULL) + return (DW_DLE_NO_ENTRY); + + if (ret_cie != NULL) + *ret_cie = cie; + + return (DW_DLE_NONE); +} + +static int +_dwarf_frame_read_lsb_encoded(Dwarf_Debug dbg, Dwarf_Cie cie, uint64_t *val, + uint8_t *data, uint64_t *offsetp, uint8_t encode, Dwarf_Addr pc, + Dwarf_Error *error) +{ + uint8_t application; + + 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; + + assert(cie->cie_augment != NULL && *cie->cie_augment == 'z'); + + /* + * 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++; + } + + return (DW_DLE_NONE); +} + +static int +_dwarf_frame_add_cie(Dwarf_Debug dbg, Dwarf_FrameSec fs, Dwarf_Section *ds, + Dwarf_Unsigned *off, Dwarf_Cie *ret_cie, Dwarf_Error *error) +{ + Dwarf_Cie cie; + uint64_t length; + int dwarf_size, ret; + char *p; + + /* Check if we already added this CIE. */ + if (_dwarf_frame_find_cie(fs, *off, &cie) != DW_DLE_NO_ENTRY) { + *off += cie->cie_length + 4; + return (DW_DLE_NONE); + } + + if ((cie = calloc(1, sizeof(struct _Dwarf_Cie))) == NULL) { + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + STAILQ_INSERT_TAIL(&fs->fs_cielist, cie, cie_next); + + cie->cie_dbg = dbg; + cie->cie_index = fs->fs_cielen; + cie->cie_offset = *off; + + length = dbg->read(ds->ds_data, off, 4); + if (length == 0xffffffff) { + dwarf_size = 8; + length = dbg->read(ds->ds_data, off, 8); + } else + dwarf_size = 4; + + if (length > ds->ds_size - *off) { + DWARF_SET_ERROR(dbg, error, DW_DLE_DEBUG_FRAME_LENGTH_BAD); + return (DW_DLE_DEBUG_FRAME_LENGTH_BAD); + } + + (void) dbg->read(ds->ds_data, off, dwarf_size); /* Skip CIE id. */ + cie->cie_length = length; + + cie->cie_version = dbg->read(ds->ds_data, off, 1); + if (cie->cie_version != 1 && cie->cie_version != 3 && + cie->cie_version != 4) { + DWARF_SET_ERROR(dbg, error, DW_DLE_FRAME_VERSION_BAD); + return (DW_DLE_FRAME_VERSION_BAD); + } + + 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; + } + + cie->cie_caf = _dwarf_read_uleb128(ds->ds_data, off); + cie->cie_daf = _dwarf_read_sleb128(ds->ds_data, off); + + /* Return address register. */ + if (cie->cie_version == 1) + cie->cie_ra = dbg->read(ds->ds_data, off, 1); + else + cie->cie_ra = _dwarf_read_uleb128(ds->ds_data, off); + + /* 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); + } + + /* CIE Initial instructions. */ + cie->cie_initinst = ds->ds_data + *off; + if (dwarf_size == 4) + cie->cie_instlen = cie->cie_offset + 4 + length - *off; + else + cie->cie_instlen = cie->cie_offset + 12 + length - *off; + + *off += cie->cie_instlen; + +#ifdef FRAME_DEBUG + printf("cie:\n"); + printf("\tcie_version=%u cie_offset=%ju cie_length=%ju cie_augment=%s" + " cie_instlen=%ju cie->cie_caf=%ju cie->cie_daf=%jd off=%ju\n", + cie->cie_version, cie->cie_offset, cie->cie_length, + (char *)cie->cie_augment, cie->cie_instlen, cie->cie_caf, + cie->cie_daf, *off); +#endif + + if (ret_cie != NULL) + *ret_cie = cie; + + fs->fs_cielen++; + + return (DW_DLE_NONE); +} + +static int +_dwarf_frame_add_fde(Dwarf_Debug dbg, Dwarf_FrameSec fs, Dwarf_Section *ds, + Dwarf_Unsigned *off, int eh_frame, Dwarf_Error *error) +{ + Dwarf_Cie cie; + Dwarf_Fde fde; + Dwarf_Unsigned cieoff; + uint64_t length, val; + int dwarf_size, ret; + + if ((fde = calloc(1, sizeof(struct _Dwarf_Fde))) == NULL) { + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + STAILQ_INSERT_TAIL(&fs->fs_fdelist, fde, fde_next); + + fde->fde_dbg = dbg; + fde->fde_fs = fs; + fde->fde_addr = ds->ds_data + *off; + fde->fde_offset = *off; + + length = dbg->read(ds->ds_data, off, 4); + if (length == 0xffffffff) { + dwarf_size = 8; + length = dbg->read(ds->ds_data, off, 8); + } else + dwarf_size = 4; + + if (length > ds->ds_size - *off) { + DWARF_SET_ERROR(dbg, error, DW_DLE_DEBUG_FRAME_LENGTH_BAD); + return (DW_DLE_DEBUG_FRAME_LENGTH_BAD); + } + + fde->fde_length = length; + + 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; + } + + fde->fde_inst = ds->ds_data + *off; + if (dwarf_size == 4) + fde->fde_instlen = fde->fde_offset + 4 + length - *off; + else + fde->fde_instlen = fde->fde_offset + 12 + length - *off; + + *off += fde->fde_instlen; + +#ifdef FRAME_DEBUG + printf("fde:"); + if (eh_frame) + printf("(eh_frame)"); + putchar('\n'); + printf("\tfde_offset=%ju fde_length=%ju fde_cieoff=%ju" + " fde_instlen=%ju off=%ju\n", fde->fde_offset, fde->fde_length, + fde->fde_cieoff, fde->fde_instlen, *off); +#endif + + fs->fs_fdelen++; + + return (DW_DLE_NONE); +} + +static void +_dwarf_frame_section_cleanup(Dwarf_FrameSec fs) +{ + Dwarf_Cie cie, tcie; + Dwarf_Fde fde, tfde; + + STAILQ_FOREACH_SAFE(cie, &fs->fs_cielist, cie_next, tcie) { + STAILQ_REMOVE(&fs->fs_cielist, cie, _Dwarf_Cie, cie_next); + free(cie); + } + + STAILQ_FOREACH_SAFE(fde, &fs->fs_fdelist, fde_next, tfde) { + STAILQ_REMOVE(&fs->fs_fdelist, fde, _Dwarf_Fde, fde_next); + free(fde); + } + + if (fs->fs_ciearray != NULL) + free(fs->fs_ciearray); + if (fs->fs_fdearray != NULL) + free(fs->fs_fdearray); + + free(fs); +} + +static int +_dwarf_frame_section_init(Dwarf_Debug dbg, Dwarf_FrameSec *frame_sec, + Dwarf_Section *ds, int eh_frame, Dwarf_Error *error) +{ + Dwarf_FrameSec fs; + Dwarf_Cie cie; + Dwarf_Fde fde; + uint64_t length, offset, cie_id, entry_off; + int dwarf_size, i, ret; + + assert(frame_sec != NULL); + assert(*frame_sec == NULL); + + if ((fs = calloc(1, sizeof(struct _Dwarf_FrameSec))) == NULL) { + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + STAILQ_INIT(&fs->fs_cielist); + STAILQ_INIT(&fs->fs_fdelist); + + offset = 0; + while (offset < ds->ds_size) { + entry_off = offset; + length = dbg->read(ds->ds_data, &offset, 4); + if (length == 0xffffffff) { + dwarf_size = 8; + length = dbg->read(ds->ds_data, &offset, 8); + } else + dwarf_size = 4; + + if (length > ds->ds_size - offset || + (length == 0 && !eh_frame)) { + DWARF_SET_ERROR(dbg, error, + DW_DLE_DEBUG_FRAME_LENGTH_BAD); + return (DW_DLE_DEBUG_FRAME_LENGTH_BAD); + } + + /* Check terminator for .eh_frame */ + if (eh_frame && length == 0) + break; + + cie_id = dbg->read(ds->ds_data, &offset, dwarf_size); + + if (eh_frame) { + /* GNU .eh_frame use CIE id 0. */ + if (cie_id == 0) + ret = _dwarf_frame_add_cie(dbg, fs, ds, + &entry_off, NULL, error); + else + ret = _dwarf_frame_add_fde(dbg, fs, ds, + &entry_off, 1, error); + } else { + /* .dwarf_frame use CIE id ~0 */ + if ((dwarf_size == 4 && cie_id == ~0U) || + (dwarf_size == 8 && cie_id == ~0ULL)) + ret = _dwarf_frame_add_cie(dbg, fs, ds, + &entry_off, NULL, error); + else + ret = _dwarf_frame_add_fde(dbg, fs, ds, + &entry_off, 0, error); + } + + if (ret != DW_DLE_NONE) + goto fail_cleanup; + + offset = entry_off; + } + + /* Create CIE array. */ + if (fs->fs_cielen > 0) { + if ((fs->fs_ciearray = malloc(sizeof(Dwarf_Cie) * + fs->fs_cielen)) == NULL) { + ret = DW_DLE_MEMORY; + DWARF_SET_ERROR(dbg, error, ret); + goto fail_cleanup; + } + i = 0; + STAILQ_FOREACH(cie, &fs->fs_cielist, cie_next) { + fs->fs_ciearray[i++] = cie; + } + assert((Dwarf_Unsigned)i == fs->fs_cielen); + } + + /* Create FDE array. */ + if (fs->fs_fdelen > 0) { + if ((fs->fs_fdearray = malloc(sizeof(Dwarf_Fde) * + fs->fs_fdelen)) == NULL) { + ret = DW_DLE_MEMORY; + DWARF_SET_ERROR(dbg, error, ret); + goto fail_cleanup; + } + i = 0; + STAILQ_FOREACH(fde, &fs->fs_fdelist, fde_next) { + fs->fs_fdearray[i++] = fde; + } + assert((Dwarf_Unsigned)i == fs->fs_fdelen); + } + + *frame_sec = fs; + + return (DW_DLE_NONE); + +fail_cleanup: + + _dwarf_frame_section_cleanup(fs); + + return (ret); +} + +static int +_dwarf_frame_run_inst(Dwarf_Debug dbg, Dwarf_Regtable3 *rt, uint8_t addr_size, + uint8_t *insts, Dwarf_Unsigned len, Dwarf_Unsigned caf, Dwarf_Signed daf, + Dwarf_Addr pc, Dwarf_Addr pc_req, Dwarf_Addr *row_pc, Dwarf_Error *error) +{ + Dwarf_Regtable3 *init_rt, *saved_rt; + uint8_t *p, *pe; + uint8_t high2, low6; + uint64_t reg, reg2, uoff, soff; + int ret; + +#define CFA rt->rt3_cfa_rule +#define INITCFA init_rt->rt3_cfa_rule +#define RL rt->rt3_rules +#define INITRL init_rt->rt3_rules + +#define CHECK_TABLE_SIZE(x) \ + do { \ + if ((x) >= rt->rt3_reg_table_size) { \ + DWARF_SET_ERROR(dbg, error, \ + DW_DLE_DF_REG_NUM_TOO_HIGH); \ + ret = DW_DLE_DF_REG_NUM_TOO_HIGH; \ + goto program_done; \ + } \ + } while(0) + +#ifdef FRAME_DEBUG + printf("frame_run_inst: (caf=%ju, daf=%jd)\n", caf, daf); +#endif + + ret = DW_DLE_NONE; + init_rt = saved_rt = NULL; + *row_pc = pc; + + /* Save a copy of the table as initial state. */ + _dwarf_frame_regtable_copy(dbg, &init_rt, rt, error); + + p = insts; + pe = p + len; + + while (p < pe) { + +#ifdef FRAME_DEBUG + printf("p=%p pe=%p pc=%#jx pc_req=%#jx\n", p, pe, pc, pc_req); +#endif + + if (*p == DW_CFA_nop) { +#ifdef FRAME_DEBUG + printf("DW_CFA_nop\n"); +#endif + p++; + continue; + } + + high2 = *p & 0xc0; + low6 = *p & 0x3f; + p++; + + if (high2 > 0) { + switch (high2) { + case DW_CFA_advance_loc: + pc += low6 * caf; +#ifdef FRAME_DEBUG + printf("DW_CFA_advance_loc(%#jx(%u))\n", pc, + low6); +#endif + if (pc_req < pc) + goto program_done; + break; + case DW_CFA_offset: + *row_pc = pc; + CHECK_TABLE_SIZE(low6); + RL[low6].dw_offset_relevant = 1; + RL[low6].dw_value_type = DW_EXPR_OFFSET; + RL[low6].dw_regnum = dbg->dbg_frame_cfa_value; + RL[low6].dw_offset_or_block_len = + _dwarf_decode_uleb128(&p) * daf; +#ifdef FRAME_DEBUG + printf("DW_CFA_offset(%jd)\n", + RL[low6].dw_offset_or_block_len); +#endif + break; + case DW_CFA_restore: + *row_pc = pc; + CHECK_TABLE_SIZE(low6); + memcpy(&RL[low6], &INITRL[low6], + sizeof(Dwarf_Regtable_Entry3)); +#ifdef FRAME_DEBUG + printf("DW_CFA_restore(%u)\n", low6); +#endif + break; + default: + DWARF_SET_ERROR(dbg, error, + DW_DLE_FRAME_INSTR_EXEC_ERROR); + ret = DW_DLE_FRAME_INSTR_EXEC_ERROR; + goto program_done; + } + + continue; + } + + switch (low6) { + case DW_CFA_set_loc: + pc = dbg->decode(&p, addr_size); +#ifdef FRAME_DEBUG + printf("DW_CFA_set_loc(pc=%#jx)\n", pc); +#endif + if (pc_req < pc) + goto program_done; + break; + case DW_CFA_advance_loc1: + pc += dbg->decode(&p, 1) * caf; +#ifdef FRAME_DEBUG + printf("DW_CFA_set_loc1(pc=%#jx)\n", pc); +#endif + if (pc_req < pc) + goto program_done; + break; + case DW_CFA_advance_loc2: + pc += dbg->decode(&p, 2) * caf; +#ifdef FRAME_DEBUG + printf("DW_CFA_set_loc2(pc=%#jx)\n", pc); +#endif + if (pc_req < pc) + goto program_done; + break; + case DW_CFA_advance_loc4: + pc += dbg->decode(&p, 4) * caf; +#ifdef FRAME_DEBUG + printf("DW_CFA_set_loc4(pc=%#jx)\n", pc); +#endif + if (pc_req < pc) + goto program_done; + break; + case DW_CFA_offset_extended: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + uoff = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 1; + RL[reg].dw_value_type = DW_EXPR_OFFSET; + RL[reg].dw_regnum = dbg->dbg_frame_cfa_value; + RL[reg].dw_offset_or_block_len = uoff * daf; +#ifdef FRAME_DEBUG + printf("DW_CFA_offset_extended(reg=%ju,uoff=%ju)\n", + reg, uoff); +#endif + break; + case DW_CFA_restore_extended: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + memcpy(&RL[reg], &INITRL[reg], + sizeof(Dwarf_Regtable_Entry3)); +#ifdef FRAME_DEBUG + printf("DW_CFA_restore_extended(%ju)\n", reg); +#endif + break; + case DW_CFA_undefined: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 0; + RL[reg].dw_regnum = dbg->dbg_frame_undefined_value; +#ifdef FRAME_DEBUG + printf("DW_CFA_undefined(%ju)\n", reg); +#endif + break; + case DW_CFA_same_value: + reg = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 0; + RL[reg].dw_regnum = dbg->dbg_frame_same_value; +#ifdef FRAME_DEBUG + printf("DW_CFA_same_value(%ju)\n", reg); +#endif + break; + case DW_CFA_register: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + reg2 = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 0; + RL[reg].dw_regnum = reg2; +#ifdef FRAME_DEBUG + printf("DW_CFA_register(reg=%ju,reg2=%ju)\n", reg, + reg2); +#endif + break; + case DW_CFA_remember_state: + _dwarf_frame_regtable_copy(dbg, &saved_rt, rt, error); +#ifdef FRAME_DEBUG + printf("DW_CFA_remember_state\n"); +#endif + break; + case DW_CFA_restore_state: + *row_pc = pc; + _dwarf_frame_regtable_copy(dbg, &rt, saved_rt, error); +#ifdef FRAME_DEBUG + printf("DW_CFA_restore_state\n"); +#endif + break; + case DW_CFA_def_cfa: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + uoff = _dwarf_decode_uleb128(&p); + CFA.dw_offset_relevant = 1; + CFA.dw_value_type = DW_EXPR_OFFSET; + CFA.dw_regnum = reg; + CFA.dw_offset_or_block_len = uoff; +#ifdef FRAME_DEBUG + printf("DW_CFA_def_cfa(reg=%ju,uoff=%ju)\n", reg, uoff); +#endif + break; + case DW_CFA_def_cfa_register: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + CFA.dw_regnum = reg; + /* + * Note that DW_CFA_def_cfa_register change the CFA + * rule register while keep the old offset. So we + * should not touch the CFA.dw_offset_relevant flag + * here. + */ +#ifdef FRAME_DEBUG + printf("DW_CFA_def_cfa_register(%ju)\n", reg); +#endif + break; + case DW_CFA_def_cfa_offset: + *row_pc = pc; + uoff = _dwarf_decode_uleb128(&p); + CFA.dw_offset_relevant = 1; + CFA.dw_value_type = DW_EXPR_OFFSET; + CFA.dw_offset_or_block_len = uoff; +#ifdef FRAME_DEBUG + printf("DW_CFA_def_cfa_offset(%ju)\n", uoff); +#endif + break; + case DW_CFA_def_cfa_expression: + *row_pc = pc; + CFA.dw_offset_relevant = 0; + CFA.dw_value_type = DW_EXPR_EXPRESSION; + CFA.dw_offset_or_block_len = _dwarf_decode_uleb128(&p); + CFA.dw_block_ptr = p; + p += CFA.dw_offset_or_block_len; +#ifdef FRAME_DEBUG + printf("DW_CFA_def_cfa_expression\n"); +#endif + break; + case DW_CFA_expression: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 0; + RL[reg].dw_value_type = DW_EXPR_EXPRESSION; + RL[reg].dw_offset_or_block_len = + _dwarf_decode_uleb128(&p); + RL[reg].dw_block_ptr = p; + p += RL[reg].dw_offset_or_block_len; +#ifdef FRAME_DEBUG + printf("DW_CFA_expression\n"); +#endif + break; + case DW_CFA_offset_extended_sf: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + soff = _dwarf_decode_sleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 1; + RL[reg].dw_value_type = DW_EXPR_OFFSET; + RL[reg].dw_regnum = dbg->dbg_frame_cfa_value; + RL[reg].dw_offset_or_block_len = soff * daf; +#ifdef FRAME_DEBUG + printf("DW_CFA_offset_extended_sf(reg=%ju,soff=%jd)\n", + reg, soff); +#endif + break; + case DW_CFA_def_cfa_sf: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + soff = _dwarf_decode_sleb128(&p); + CFA.dw_offset_relevant = 1; + CFA.dw_value_type = DW_EXPR_OFFSET; + CFA.dw_regnum = reg; + CFA.dw_offset_or_block_len = soff * daf; +#ifdef FRAME_DEBUG + printf("DW_CFA_def_cfa_sf(reg=%ju,soff=%jd)\n", reg, + soff); +#endif + break; + case DW_CFA_def_cfa_offset_sf: + *row_pc = pc; + soff = _dwarf_decode_sleb128(&p); + CFA.dw_offset_relevant = 1; + CFA.dw_value_type = DW_EXPR_OFFSET; + CFA.dw_offset_or_block_len = soff * daf; +#ifdef FRAME_DEBUG + printf("DW_CFA_def_cfa_offset_sf(soff=%jd)\n", soff); +#endif + break; + case DW_CFA_val_offset: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + uoff = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 1; + RL[reg].dw_value_type = DW_EXPR_VAL_OFFSET; + RL[reg].dw_regnum = dbg->dbg_frame_cfa_value; + RL[reg].dw_offset_or_block_len = uoff * daf; +#ifdef FRAME_DEBUG + printf("DW_CFA_val_offset(reg=%ju,uoff=%ju)\n", reg, + uoff); +#endif + break; + case DW_CFA_val_offset_sf: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + soff = _dwarf_decode_sleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 1; + RL[reg].dw_value_type = DW_EXPR_VAL_OFFSET; + RL[reg].dw_regnum = dbg->dbg_frame_cfa_value; + RL[reg].dw_offset_or_block_len = soff * daf; +#ifdef FRAME_DEBUG + printf("DW_CFA_val_offset_sf(reg=%ju,soff=%jd)\n", reg, + soff); +#endif + break; + case DW_CFA_val_expression: + *row_pc = pc; + reg = _dwarf_decode_uleb128(&p); + CHECK_TABLE_SIZE(reg); + RL[reg].dw_offset_relevant = 0; + RL[reg].dw_value_type = DW_EXPR_VAL_EXPRESSION; + RL[reg].dw_offset_or_block_len = + _dwarf_decode_uleb128(&p); + RL[reg].dw_block_ptr = p; + p += RL[reg].dw_offset_or_block_len; +#ifdef FRAME_DEBUG + printf("DW_CFA_val_expression\n"); +#endif + break; + default: + DWARF_SET_ERROR(dbg, error, + DW_DLE_FRAME_INSTR_EXEC_ERROR); + ret = DW_DLE_FRAME_INSTR_EXEC_ERROR; + goto program_done; + } + } + +program_done: + + free(init_rt->rt3_rules); + free(init_rt); + if (saved_rt) { + free(saved_rt->rt3_rules); + free(saved_rt); + } + + return (ret); + +#undef CFA +#undef INITCFA +#undef RL +#undef INITRL +#undef CHECK_TABLE_SIZE +} + +static int +_dwarf_frame_convert_inst(Dwarf_Debug dbg, uint8_t addr_size, uint8_t *insts, + Dwarf_Unsigned len, Dwarf_Unsigned *count, Dwarf_Frame_Op *fop, + Dwarf_Frame_Op3 *fop3, Dwarf_Error *error) +{ + uint8_t *p, *pe; + uint8_t high2, low6; + uint64_t reg, reg2, uoff, soff, blen; + +#define SET_BASE_OP(x) \ + do { \ + if (fop != NULL) \ + fop[*count].fp_base_op = (x) >> 6; \ + if (fop3 != NULL) \ + fop3[*count].fp_base_op = (x) >> 6; \ + } while(0) + +#define SET_EXTENDED_OP(x) \ + do { \ + if (fop != NULL) \ + fop[*count].fp_extended_op = (x); \ + if (fop3 != NULL) \ + fop3[*count].fp_extended_op = (x); \ + } while(0) + +#define SET_REGISTER(x) \ + do { \ + if (fop != NULL) \ + fop[*count].fp_register = (x); \ + if (fop3 != NULL) \ + fop3[*count].fp_register = (x); \ + } while(0) + +#define SET_OFFSET(x) \ + do { \ + if (fop != NULL) \ + fop[*count].fp_offset = (x); \ + if (fop3 != NULL) \ + fop3[*count].fp_offset_or_block_len = \ + (x); \ + } while(0) + +#define SET_INSTR_OFFSET(x) \ + do { \ + if (fop != NULL) \ + fop[*count].fp_instr_offset = (x); \ + if (fop3 != NULL) \ + fop3[*count].fp_instr_offset = (x); \ + } while(0) + +#define SET_BLOCK_LEN(x) \ + do { \ + if (fop3 != NULL) \ + fop3[*count].fp_offset_or_block_len = \ + (x); \ + } while(0) + +#define SET_EXPR_BLOCK(addr, len) \ + do { \ + if (fop3 != NULL) { \ + fop3[*count].fp_expr_block = \ + malloc((size_t) (len)); \ + if (fop3[*count].fp_expr_block == NULL) { \ + DWARF_SET_ERROR(dbg, error, \ + DW_DLE_MEMORY); \ + return (DW_DLE_MEMORY); \ + } \ + memcpy(&fop3[*count].fp_expr_block, \ + (addr), (len)); \ + } \ + } while(0) + + *count = 0; + + p = insts; + pe = p + len; + + while (p < pe) { + + SET_INSTR_OFFSET(p - insts); + + if (*p == DW_CFA_nop) { + p++; + (*count)++; + continue; + } + + high2 = *p & 0xc0; + low6 = *p & 0x3f; + p++; + + if (high2 > 0) { + switch (high2) { + case DW_CFA_advance_loc: + SET_BASE_OP(high2); + SET_OFFSET(low6); + break; + case DW_CFA_offset: + SET_BASE_OP(high2); + SET_REGISTER(low6); + uoff = _dwarf_decode_uleb128(&p); + SET_OFFSET(uoff); + break; + case DW_CFA_restore: + SET_BASE_OP(high2); + SET_REGISTER(low6); + break; + default: + DWARF_SET_ERROR(dbg, error, + DW_DLE_FRAME_INSTR_EXEC_ERROR); + return (DW_DLE_FRAME_INSTR_EXEC_ERROR); + } + + (*count)++; + continue; + } + + SET_EXTENDED_OP(low6); + + switch (low6) { + case DW_CFA_set_loc: + uoff = dbg->decode(&p, addr_size); + SET_OFFSET(uoff); + break; + case DW_CFA_advance_loc1: + uoff = dbg->decode(&p, 1); + SET_OFFSET(uoff); + break; + case DW_CFA_advance_loc2: + uoff = dbg->decode(&p, 2); + SET_OFFSET(uoff); + break; + case DW_CFA_advance_loc4: + uoff = dbg->decode(&p, 4); + SET_OFFSET(uoff); + break; + case DW_CFA_offset_extended: + case DW_CFA_def_cfa: + case DW_CFA_val_offset: + reg = _dwarf_decode_uleb128(&p); + uoff = _dwarf_decode_uleb128(&p); + SET_REGISTER(reg); + SET_OFFSET(uoff); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + reg = _dwarf_decode_uleb128(&p); + SET_REGISTER(reg); + break; + case DW_CFA_register: + reg = _dwarf_decode_uleb128(&p); + reg2 = _dwarf_decode_uleb128(&p); + SET_REGISTER(reg); + SET_OFFSET(reg2); + break; + case DW_CFA_remember_state: + case DW_CFA_restore_state: + break; + case DW_CFA_def_cfa_offset: + uoff = _dwarf_decode_uleb128(&p); + SET_OFFSET(uoff); + break; + case DW_CFA_def_cfa_expression: + blen = _dwarf_decode_uleb128(&p); + SET_BLOCK_LEN(blen); + SET_EXPR_BLOCK(p, blen); + p += blen; + break; + case DW_CFA_expression: + case DW_CFA_val_expression: + reg = _dwarf_decode_uleb128(&p); + blen = _dwarf_decode_uleb128(&p); + SET_REGISTER(reg); + SET_BLOCK_LEN(blen); + SET_EXPR_BLOCK(p, blen); + p += blen; + break; + case DW_CFA_offset_extended_sf: + case DW_CFA_def_cfa_sf: + case DW_CFA_val_offset_sf: + reg = _dwarf_decode_uleb128(&p); + soff = _dwarf_decode_sleb128(&p); + SET_REGISTER(reg); + SET_OFFSET(soff); + break; + case DW_CFA_def_cfa_offset_sf: + soff = _dwarf_decode_sleb128(&p); + SET_OFFSET(soff); + break; + default: + DWARF_SET_ERROR(dbg, error, + DW_DLE_FRAME_INSTR_EXEC_ERROR); + return (DW_DLE_FRAME_INSTR_EXEC_ERROR); + } + + (*count)++; + } + + return (DW_DLE_NONE); +} + +int +_dwarf_frame_get_fop(Dwarf_Debug dbg, uint8_t addr_size, uint8_t *insts, + Dwarf_Unsigned len, Dwarf_Frame_Op **ret_oplist, Dwarf_Signed *ret_opcnt, + Dwarf_Error *error) +{ + Dwarf_Frame_Op *oplist; + Dwarf_Unsigned count; + int ret; + + ret = _dwarf_frame_convert_inst(dbg, addr_size, insts, len, &count, + NULL, NULL, error); + if (ret != DW_DLE_NONE) + return (ret); + + if ((oplist = calloc(count, sizeof(Dwarf_Frame_Op))) == NULL) { + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + + ret = _dwarf_frame_convert_inst(dbg, addr_size, insts, len, &count, + oplist, NULL, error); + if (ret != DW_DLE_NONE) { + free(oplist); + return (ret); + } + + *ret_oplist = oplist; + *ret_opcnt = count; + + return (DW_DLE_NONE); +} + +int +_dwarf_frame_regtable_copy(Dwarf_Debug dbg, Dwarf_Regtable3 **dest, + Dwarf_Regtable3 *src, Dwarf_Error *error) +{ + int i; + + assert(dest != NULL); + assert(src != NULL); + + if (*dest == NULL) { + if ((*dest = malloc(sizeof(Dwarf_Regtable3))) == NULL) { + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + (*dest)->rt3_reg_table_size = src->rt3_reg_table_size; + (*dest)->rt3_rules = malloc(src->rt3_reg_table_size * + sizeof(Dwarf_Regtable_Entry3)); + if ((*dest)->rt3_rules == NULL) { + free(*dest); + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + } + + memcpy(&(*dest)->rt3_cfa_rule, &src->rt3_cfa_rule, + sizeof(Dwarf_Regtable_Entry3)); + + 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); + } + + *ret_rt = rt; + *ret_row_pc = row_pc; + + return (DW_DLE_NONE); +} + +void +_dwarf_frame_cleanup(Dwarf_Debug dbg) +{ + Dwarf_Regtable3 *rt; + + assert(dbg != NULL && dbg->dbg_mode == DW_DLC_READ); + + if (dbg->dbg_internal_reg_table) { + rt = dbg->dbg_internal_reg_table; + free(rt->rt3_rules); + free(rt); + dbg->dbg_internal_reg_table = NULL; + } + + if (dbg->dbg_frame) { + _dwarf_frame_section_cleanup(dbg->dbg_frame); + dbg->dbg_frame = NULL; + } + + if (dbg->dbg_eh_frame) { + _dwarf_frame_section_cleanup(dbg->dbg_eh_frame); + dbg->dbg_eh_frame = NULL; + } +} + +int +_dwarf_frame_section_load(Dwarf_Debug dbg, Dwarf_Error *error) +{ + Dwarf_Section *ds; + + if ((ds = _dwarf_find_section(dbg, ".debug_frame")) != NULL) { + return (_dwarf_frame_section_init(dbg, &dbg->dbg_frame, + ds, 0, error)); + } + + return (DW_DLE_NONE); +} + +int +_dwarf_frame_section_load_eh(Dwarf_Debug dbg, Dwarf_Error *error) +{ + Dwarf_Section *ds; + + if ((ds = _dwarf_find_section(dbg, ".eh_frame")) != NULL) { + return (_dwarf_frame_section_init(dbg, &dbg->dbg_eh_frame, + ds, 1, error)); + } + + return (DW_DLE_NONE); +} + +void +_dwarf_frame_params_init(Dwarf_Debug dbg) +{ + + /* Initialise call frame related parameters. */ + dbg->dbg_frame_rule_table_size = DW_FRAME_LAST_REG_NUM; + dbg->dbg_frame_rule_initial_value = DW_FRAME_REG_INITIAL_VALUE; + dbg->dbg_frame_cfa_value = DW_FRAME_CFA_COL3; + dbg->dbg_frame_same_value = DW_FRAME_SAME_VAL; + dbg->dbg_frame_undefined_value = DW_FRAME_UNDEFINED_VAL; +} + +int +_dwarf_frame_interal_table_init(Dwarf_Debug dbg, Dwarf_Error *error) +{ + Dwarf_Regtable3 *rt; + + if (dbg->dbg_internal_reg_table != NULL) + return (DW_DLE_NONE); + + /* Initialise internal register table. */ + if ((rt = calloc(1, sizeof(Dwarf_Regtable3))) == NULL) { + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + + rt->rt3_reg_table_size = dbg->dbg_frame_rule_table_size; + if ((rt->rt3_rules = calloc(rt->rt3_reg_table_size, + sizeof(Dwarf_Regtable_Entry3))) == NULL) { + free(rt); + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + + dbg->dbg_internal_reg_table = rt; + + return (DW_DLE_NONE); +} + +#define _FDE_INST_INIT_SIZE 128 + +int +_dwarf_frame_fde_add_inst(Dwarf_P_Fde fde, Dwarf_Small op, Dwarf_Unsigned val1, + Dwarf_Unsigned val2, Dwarf_Error *error) +{ + Dwarf_P_Debug dbg; + uint8_t high2, low6; + int ret; + +#define ds fde +#define ds_data fde_inst +#define ds_cap fde_instcap +#define ds_size fde_instlen + + assert(fde != NULL && fde->fde_dbg != NULL); + dbg = fde->fde_dbg; + + if (fde->fde_inst == NULL) { + fde->fde_instcap = _FDE_INST_INIT_SIZE; + fde->fde_instlen = 0; + if ((fde->fde_inst = malloc((size_t) fde->fde_instcap)) == + NULL) { + DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); + return (DW_DLE_MEMORY); + } + } + assert(fde->fde_instcap != 0); + + 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); + } + + return (DW_DLE_NONE); + +gen_fail: + return (ret); + +#undef ds +#undef ds_data +#undef ds_cap +#undef ds_size +} + +static int +_dwarf_frame_gen_cie(Dwarf_P_Debug dbg, Dwarf_P_Section ds, Dwarf_P_Cie cie, + Dwarf_Error *error) +{ + Dwarf_Unsigned len; + uint64_t offset; + int ret; + + assert(dbg != NULL && ds != NULL && cie != NULL); + + cie->cie_offset = offset = ds->ds_size; + cie->cie_length = 0; + cie->cie_version = 1; + + /* Length placeholder. */ + RCHECK(WRITE_VALUE(cie->cie_length, 4)); + + /* .debug_frame use CIE id ~0. */ + RCHECK(WRITE_VALUE(~0U, 4)); + + /* .debug_frame version is 1. (DWARF2) */ + RCHECK(WRITE_VALUE(cie->cie_version, 1)); + + /* Write augmentation, if present. */ + if (cie->cie_augment != NULL) + RCHECK(WRITE_BLOCK(cie->cie_augment, + strlen((char *) cie->cie_augment) + 1)); + else + RCHECK(WRITE_VALUE(0, 1)); + + /* Write caf, daf and ra. */ + RCHECK(WRITE_ULEB128(cie->cie_caf)); + RCHECK(WRITE_SLEB128(cie->cie_daf)); + RCHECK(WRITE_VALUE(cie->cie_ra, 1)); + + /* Write initial instructions, if present. */ + if (cie->cie_initinst != NULL) + RCHECK(WRITE_BLOCK(cie->cie_initinst, cie->cie_instlen)); + + /* Add padding. */ + len = ds->ds_size - cie->cie_offset - 4; + cie->cie_length = roundup(len, dbg->dbg_pointer_size); + while (len++ < cie->cie_length) + RCHECK(WRITE_VALUE(DW_CFA_nop, 1)); + + /* Fill in the length field. */ + dbg->write(ds->ds_data, &offset, cie->cie_length, 4); + + return (DW_DLE_NONE); + +gen_fail: + return (ret); +} + +static int +_dwarf_frame_gen_fde(Dwarf_P_Debug dbg, Dwarf_P_Section ds, + Dwarf_Rel_Section drs, Dwarf_P_Fde fde, Dwarf_Error *error) +{ + Dwarf_Unsigned len; + uint64_t offset; + int ret; + + assert(dbg != NULL && ds != NULL && drs != NULL); + assert(fde != NULL && fde->fde_cie != NULL); + + fde->fde_offset = offset = ds->ds_size; + fde->fde_length = 0; + fde->fde_cieoff = fde->fde_cie->cie_offset; + + /* Length placeholder. */ + RCHECK(WRITE_VALUE(fde->fde_length, 4)); + + /* Write CIE pointer. */ + RCHECK(_dwarf_reloc_entry_add(dbg, drs, ds, dwarf_drt_data_reloc, 4, + ds->ds_size, 0, fde->fde_cieoff, ".debug_frame", error)); + + /* Write FDE initial location. */ + RCHECK(_dwarf_reloc_entry_add(dbg, drs, ds, dwarf_drt_data_reloc, + dbg->dbg_pointer_size, ds->ds_size, fde->fde_symndx, + fde->fde_initloc, NULL, 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)); + + /* Write FDE frame instructions. */ + RCHECK(WRITE_BLOCK(fde->fde_inst, fde->fde_instlen)); + + /* Add padding. */ + len = ds->ds_size - fde->fde_offset - 4; + fde->fde_length = roundup(len, dbg->dbg_pointer_size); + while (len++ < fde->fde_length) + RCHECK(WRITE_VALUE(DW_CFA_nop, 1)); + + /* Fill in the length field. */ + dbg->write(ds->ds_data, &offset, fde->fde_length, 4); + + return (DW_DLE_NONE); + +gen_fail: + return (ret); +} + +int +_dwarf_frame_gen(Dwarf_P_Debug dbg, Dwarf_Error *error) +{ + Dwarf_P_Section ds; + Dwarf_Rel_Section drs; + Dwarf_P_Cie cie; + Dwarf_P_Fde fde; + int ret; + + if (STAILQ_EMPTY(&dbg->dbgp_cielist)) + return (DW_DLE_NONE); + + /* Create .debug_frame section. */ + if ((ret = _dwarf_section_init(dbg, &ds, ".debug_frame", 0, error)) != + DW_DLE_NONE) + goto gen_fail0; + + /* Create relocation section for .debug_frame */ + RCHECK(_dwarf_reloc_section_init(dbg, &drs, ds, error)); + + /* Generate list of CIE. */ + STAILQ_FOREACH(cie, &dbg->dbgp_cielist, cie_next) + RCHECK(_dwarf_frame_gen_cie(dbg, ds, cie, error)); + + /* Generate list of FDE. */ + STAILQ_FOREACH(fde, &dbg->dbgp_fdelist, fde_next) + RCHECK(_dwarf_frame_gen_fde(dbg, ds, drs, fde, error)); + + /* Inform application the creation of .debug_frame ELF section. */ + RCHECK(_dwarf_section_callback(dbg, ds, SHT_PROGBITS, 0, 0, 0, error)); + + /* Finalize relocation section for .debug_frame */ + RCHECK(_dwarf_reloc_section_finalize(dbg, drs, error)); + + return (DW_DLE_NONE); + +gen_fail: + _dwarf_reloc_section_free(dbg, &drs); + +gen_fail0: + _dwarf_section_free(dbg, &ds); + + return (ret); +} + +void +_dwarf_frame_pro_cleanup(Dwarf_P_Debug dbg) +{ + Dwarf_P_Cie cie, tcie; + Dwarf_P_Fde fde, tfde; + + assert(dbg != NULL && dbg->dbg_mode == DW_DLC_WRITE); + + STAILQ_FOREACH_SAFE(cie, &dbg->dbgp_cielist, cie_next, tcie) { + STAILQ_REMOVE(&dbg->dbgp_cielist, cie, _Dwarf_Cie, cie_next); + if (cie->cie_augment) + free(cie->cie_augment); + if (cie->cie_initinst) + free(cie->cie_initinst); + free(cie); + } + dbg->dbgp_cielen = 0; + + STAILQ_FOREACH_SAFE(fde, &dbg->dbgp_fdelist, fde_next, tfde) { + STAILQ_REMOVE(&dbg->dbgp_fdelist, fde, _Dwarf_Fde, fde_next); + if (fde->fde_inst != NULL) + free(fde->fde_inst); + free(fde); + } + dbg->dbgp_fdelen = 0; +} |