static int get_member_offset(Dwarf_Die *memdie, Dwarf_Word *off_out) { Dwarf_Attribute loc_attr; Dwarf_Block block; if (dwarf_attr_integrate(memdie, DW_AT_data_member_location, &loc_attr) == NULL) dwarf_err(EX_DATAERR, "dwarf_attr_integrate(%s/loc)", dwarf_diename(memdie)); switch (dwarf_whatform(&loc_attr)) { case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: if (dwarf_formblock(&loc_attr, &block)) dwarf_err(EX_DATAERR, "dwarf_formblock(%s)", dwarf_diename(memdie)); assert(block.data[0] == DW_OP_plus_uconst || block.data[0] == DW_OP_constu); get_uleb128(off_out, &block.data[1]); return (0); default: printf("ZZZ!\n"); return (-1); } }
/* Handles only a location expression. If called on a loclist, just returns one of those. Cannot not handle a real loclist. It returns the location expression as a loclist with a single entry. See dwarf_loclist_n() which handles any number of location list entries. This is the original definition, and it simply does not work for loclists. Nor does it work on DWARF4 nor on some versions of gcc generating DWARF4. Kept for compatibility. */ int dwarf_loclist(Dwarf_Attribute attr, Dwarf_Locdesc ** llbuf, Dwarf_Signed * listlen, Dwarf_Error * error) { Dwarf_Debug dbg; /* Dwarf_Attribute that describes the DW_AT_location in die, if present. */ Dwarf_Attribute loc_attr = attr; /* Dwarf_Block that describes a single location expression. */ Dwarf_Block_c loc_block; /* A pointer to the current Dwarf_Locdesc read. */ 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 = DW_DLV_ERROR; int setup_res = DW_DLV_ERROR; int cuvstamp = 0; /* ***** BEGIN CODE ***** */ setup_res = _dwarf_setup_loc(attr, &dbg, &cucontext, &form, error); if (setup_res != DW_DLV_OK) { return setup_res; } cuvstamp = cucontext->cc_version_stamp; address_size = cucontext->cc_address_size; /* If this is a form_block then it's a location expression. If it's DW_FORM_data4 or DW_FORM_data8 it's a loclist offset */ if (((cuvstamp == DW_CU_VERSION2 || cuvstamp == DW_CU_VERSION3) && (form == DW_FORM_data4 || form == DW_FORM_data8)) || ((cuvstamp == DW_CU_VERSION4) && form == DW_FORM_sec_offset)) { /* A reference to .debug_loc, with an offset in .debug_loc of a loclist. */ Dwarf_Unsigned loclist_offset = 0; int off_res = DW_DLV_ERROR; off_res = _dwarf_get_loclist_header_start(dbg, attr, &loclist_offset, error); if (off_res != DW_DLV_OK) { return off_res; } /* With dwarf_loclist, just read a single entry */ blkres = _dwarf_read_loc_section(dbg, &loc_block, &lowpc, &highpc, loclist_offset, address_size, error); if (blkres != DW_DLV_OK) { return (blkres); } } else { if( form == DW_FORM_exprloc) { blkres = dwarf_formexprloc(loc_attr,&loc_block.bl_len, &loc_block.bl_data,error); if(blkres != DW_DLV_OK) { return blkres; } loc_block.bl_from_loclist = 0; loc_block.bl_section_offset = (char *)loc_block.bl_data - (char *)dbg->de_debug_info.dss_data; } else { Dwarf_Block *tblock = 0; /* If DWARF5 this will surely fail, get an error. */ blkres = dwarf_formblock(loc_attr, &tblock, error); if (blkres != DW_DLV_OK) { return (blkres); } loc_block.bl_len = tblock->bl_len;; loc_block.bl_data = tblock->bl_data; loc_block.bl_from_loclist = tblock->bl_from_loclist; loc_block.bl_section_offset = tblock->bl_section_offset; /* We copied tblock contents to the stack var, so can dealloc tblock now. Avoids leaks. */ dwarf_dealloc(dbg, tblock, DW_DLA_BLOCK); } lowpc = 0; /* HACK */ highpc = (Dwarf_Unsigned) (-1LL); /* HACK */ } /* An empty location description (block length 0) means the code generator emitted no variable, the variable was not generated, it was unused or perhaps never tested after being set. Dwarf2, section 2.4.1 In other words, it is not an error, and we don't test for block length 0 specially here. See *dwarf_loclist_n() which handles the general case, this case handles only a single location expression. */ blkres = _dwarf_get_locdesc(dbg, &loc_block, address_size, cucontext->cc_length_size, cucontext->cc_version_stamp, lowpc, highpc, &locdesc, error); if (blkres != DW_DLV_OK) { /* low level error already set: let it be passed back */ return blkres; } *llbuf = locdesc; *listlen = 1; return (DW_DLV_OK); }
/* Handles simple location entries and loclists. Returns all the Locdesc's thru llbuf. Will not work properly for DWARF5 and may not work for some recent versions of gcc or llvm emitting DWARF4 with location extensions. Does not work for .debug_loc.dwo Use dwarf_get_loclist_b() and associated functions. */ int dwarf_loclist_n(Dwarf_Attribute attr, Dwarf_Locdesc *** llbuf_out, Dwarf_Signed * listlen_out, Dwarf_Error * error) { Dwarf_Debug dbg; /* Dwarf_Attribute that describes the DW_AT_location in die, if present. */ Dwarf_Attribute loc_attr = attr; /* Dwarf_Block that describes a single location expression. */ Dwarf_Block_c loc_block; /* A pointer to the current Dwarf_Locdesc read. */ 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 cuvstamp = 0; Dwarf_Bool is_cu = FALSE; int blkres = DW_DLV_ERROR; int setup_res = DW_DLV_ERROR; /* ***** BEGIN CODE ***** */ setup_res = _dwarf_setup_loc(attr, &dbg,&cucontext, &form, error); if (setup_res != DW_DLV_OK) { return setup_res; } cuvstamp = cucontext->cc_version_stamp; address_size = cucontext->cc_address_size; /* If this is a form_block then it's a location expression. If it's DW_FORM_data4 or DW_FORM_data8 in DWARF2 or DWARF3 (or in DWARF4 or 5 a DW_FORM_sec_offset) it's a loclist offset */ if (cuvstamp == DW_CU_VERSION5) { /* Use a newer interface. */ _dwarf_error(dbg, error, DW_DLE_LOCLIST_INTERFACE_ERROR); return (DW_DLV_ERROR); } if (((cuvstamp == DW_CU_VERSION2 || cuvstamp == DW_CU_VERSION3) && (form == DW_FORM_data4 || form == DW_FORM_data8)) || ((cuvstamp == DW_CU_VERSION4) && form == DW_FORM_sec_offset)) { setup_res = context_is_cu_not_tu(cucontext,&is_cu,error); if(setup_res != DW_DLV_OK) { return setup_res; } /* A reference to .debug_loc, with an offset in .debug_loc of a loclist */ Dwarf_Unsigned loclist_offset = 0; int off_res = DW_DLV_ERROR; int count_res = DW_DLV_ERROR; int loclist_count = 0; int lli = 0; 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, address_size, &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) { int lres = 0; blkres = _dwarf_read_loc_section(dbg, &loc_block, &lowpc, &highpc, loclist_offset, address_size, error); if (blkres != DW_DLV_OK) { _dwarf_cleanup_llbuf(dbg, llbuf, lli); return (blkres); } lres = _dwarf_get_locdesc(dbg, &loc_block, address_size, cucontext->cc_length_size, cucontext->cc_version_stamp, lowpc, highpc, &locdesc, error); if (lres != DW_DLV_OK) { _dwarf_cleanup_llbuf(dbg, llbuf, lli); /* low level error already set: let it be passed back */ return lres; } llbuf[lli] = locdesc; /* Now get to next loclist entry offset. */ loclist_offset = loc_block.bl_section_offset + loc_block.bl_len; } } else { if( form == DW_FORM_exprloc) { blkres = dwarf_formexprloc(loc_attr,&loc_block.bl_len, &loc_block.bl_data,error); if(blkres != DW_DLV_OK) { return blkres; } loc_block.bl_from_loclist = 0; loc_block.bl_section_offset = (char *)loc_block.bl_data - (char *)dbg->de_debug_info.dss_data; } else { Dwarf_Block *tblock = 0; blkres = dwarf_formblock(loc_attr, &tblock, error); if (blkres != DW_DLV_OK) { return (blkres); } loc_block.bl_len = tblock->bl_len;; loc_block.bl_data = tblock->bl_data; loc_block.bl_from_loclist = tblock->bl_from_loclist; loc_block.bl_section_offset = tblock->bl_section_offset; loc_block.bl_locdesc_offset = 0; /* not relevent */ /* We copied tblock contents to the stack var, so can dealloc tblock now. Avoids leaks. */ dwarf_dealloc(dbg, tblock, DW_DLA_BLOCK); } listlen = 1; /* One by definition of a location entry. */ lowpc = 0; /* HACK */ highpc = (Dwarf_Unsigned) (-1LL); /* HACK */ /* An empty location description (block length 0) means the code generator emitted no variable, the variable was not generated, it was unused or perhaps never tested after being set. Dwarf2, section 2.4.1 In other words, it is not an error, and we don't test for block length 0 specially here. */ blkres = _dwarf_get_locdesc(dbg, &loc_block, address_size, cucontext->cc_length_size, cucontext->cc_version_stamp, lowpc, highpc, &locdesc, error); if (blkres != DW_DLV_OK) { /* low level error already set: let it be passed back */ return blkres; } llbuf = (Dwarf_Locdesc **) _dwarf_get_alloc(dbg, DW_DLA_LIST, listlen); if (!llbuf) { /* Free the locdesc we allocated but won't use. */ 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); }
/* New October 2015 This interface requires the use of interface functions to get data from Dwarf_Locdesc_c. The structures are not visible to callers. */ int dwarf_get_loclist_c(Dwarf_Attribute attr, Dwarf_Loc_Head_c * ll_header_out, Dwarf_Unsigned * listlen_out, Dwarf_Error * error) { Dwarf_Debug dbg; /* Dwarf_Attribute that describes the DW_AT_location in die, if present. */ Dwarf_Attribute loc_attr = attr; /* Dwarf_Block that describes a single location expression. */ Dwarf_Block_c loc_block; Dwarf_Half form = 0; Dwarf_Addr lowpc = 0; Dwarf_Addr highpc = 0; Dwarf_Unsigned listlen = 0; Dwarf_Locdesc_c llbuf = 0; Dwarf_Loc_Head_c llhead = 0; Dwarf_CU_Context cucontext = 0; unsigned address_size = 0; int cuvstamp = 0; Dwarf_Bool is_cu = FALSE; int blkres = DW_DLV_ERROR; int setup_res = DW_DLV_ERROR; /* ***** BEGIN CODE ***** */ setup_res = _dwarf_setup_loc(attr, &dbg,&cucontext, &form, error); if (setup_res != DW_DLV_OK) { return setup_res; } cuvstamp = cucontext->cc_version_stamp; address_size = cucontext->cc_address_size; /* If this is a form_block then it's a location expression. If it's DW_FORM_data4 or DW_FORM_data8 in DWARF2 or DWARF3 (or in DWARF4 or 5 a DW_FORM_sec_offset) it's a loclist offset */ if (((cuvstamp == DW_CU_VERSION2 || cuvstamp == DW_CU_VERSION3) && (form == DW_FORM_data4 || form == DW_FORM_data8)) || ((cuvstamp == DW_CU_VERSION4 || cuvstamp == DW_CU_VERSION5) && form == DW_FORM_sec_offset)) { /* Here we have a loclist to deal with. */ setup_res = context_is_cu_not_tu(cucontext,&is_cu,error); if(setup_res != DW_DLV_OK) { return setup_res; } if (cucontext->cc_is_dwo) { /* dwo loclist. If this were a skeleton CU (ie, in the base, not dwo/dwp) then it could not have a loclist. */ /* A reference to .debug_loc.dwo, with an offset in .debug_loc.dwo of a loclist */ Dwarf_Unsigned loclist_offset = 0; int off_res = DW_DLV_ERROR; int count_res = DW_DLV_ERROR; int loclist_count = 0; Dwarf_Unsigned lli = 0; 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_dwo(dbg, loclist_offset, address_size, &loclist_count, error); if (count_res != DW_DLV_OK) { return count_res; } listlen = loclist_count; if (loclist_count == 0) { return DW_DLV_NO_ENTRY; } llhead = (Dwarf_Loc_Head_c)_dwarf_get_alloc(dbg, DW_DLA_LOC_HEAD_C, 1); if (!llhead) { _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); return (DW_DLV_ERROR); } listlen = loclist_count; llbuf = (Dwarf_Locdesc_c) _dwarf_get_alloc(dbg, DW_DLA_LOCDESC_C, listlen); if (!llbuf) { dwarf_loc_head_c_dealloc(llhead); _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); return (DW_DLV_ERROR); } llhead->ll_locdesc = llbuf; llhead->ll_locdesc_count = listlen; llhead->ll_from_loclist = 2; llhead->ll_context = cucontext; llhead->ll_dbg = dbg; /* New get loc ops, DWO version */ for (lli = 0; lli < listlen; ++lli) { int lres = 0; Dwarf_Half lle_op = 0; Dwarf_Bool at_end = 0; blkres = _dwarf_read_loc_section_dwo(dbg, &loc_block, &lowpc, &highpc, &at_end, &lle_op, loclist_offset, address_size, error); if (blkres != DW_DLV_OK) { dwarf_loc_head_c_dealloc(llhead); return blkres; } /* Fills in the locdesc at index lli */ lres = _dwarf_get_locdesc_c(dbg, lli, llhead, &loc_block, address_size, cucontext->cc_length_size, cucontext->cc_version_stamp, lowpc, highpc, error); if (lres != DW_DLV_OK) { dwarf_loc_head_c_dealloc(llhead); /* low level error already set: let it be passed back */ return lres; } /* Override the syntesized lle value with the real one. */ llhead->ll_locdesc[lli].ld_lle_value = lle_op; /* Now get to next loclist entry offset. */ loclist_offset = loc_block.bl_section_offset + loc_block.bl_len; } } else { /* Non-dwo loclist. If this were a skeleton CU (ie, in the base, not dwo/dwp) then it could not have a loclist. */ /* A reference to .debug_loc, with an offset in .debug_loc of a loclist */ Dwarf_Unsigned loclist_offset = 0; int off_res = DW_DLV_ERROR; int count_res = DW_DLV_ERROR; int loclist_count = 0; Dwarf_Unsigned lli = 0; 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, address_size, &loclist_count, error); listlen = loclist_count; if (count_res != DW_DLV_OK) { return count_res; } if (loclist_count == 0) { return DW_DLV_NO_ENTRY; } llhead = (Dwarf_Loc_Head_c)_dwarf_get_alloc(dbg, DW_DLA_LOC_HEAD_C, 1); if (!llhead) { _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); return (DW_DLV_ERROR); } listlen = loclist_count; llbuf = (Dwarf_Locdesc_c) _dwarf_get_alloc(dbg, DW_DLA_LOCDESC_C, listlen); if (!llbuf) { dwarf_loc_head_c_dealloc(llhead); _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); return (DW_DLV_ERROR); } llhead->ll_locdesc = llbuf; llhead->ll_locdesc_count = listlen; llhead->ll_from_loclist = 1; llhead->ll_context = cucontext; llhead->ll_dbg = dbg; /* New locdesc and Loc, non-DWO, so old format */ for (lli = 0; lli < listlen; ++lli) { int lres = 0; Dwarf_Block_c c; blkres = _dwarf_read_loc_section(dbg, &c, &lowpc, &highpc, loclist_offset, address_size, error); if (blkres != DW_DLV_OK) { dwarf_loc_head_c_dealloc(llhead); return (blkres); } loc_block.bl_len = c.bl_len; loc_block.bl_data = c.bl_data; loc_block.bl_from_loclist = c.bl_from_loclist; loc_block.bl_section_offset = c.bl_section_offset; loc_block.bl_locdesc_offset = loclist_offset; /* Fills in the locdesc at index lli */ lres = _dwarf_get_locdesc_c(dbg, lli, llhead, &loc_block, address_size, cucontext->cc_length_size, cucontext->cc_version_stamp, lowpc, highpc, error); if (lres != DW_DLV_OK) { dwarf_loc_head_c_dealloc(llhead); /* low level error already set: let it be passed back */ return lres; } /* Now get to next loclist entry offset. */ loclist_offset = loc_block.bl_section_offset + loc_block.bl_len; } } } else { llhead = (Dwarf_Loc_Head_c) _dwarf_get_alloc(dbg, DW_DLA_LOC_HEAD_C, 1); if (!llhead) { _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); return (DW_DLV_ERROR); } if( form == DW_FORM_exprloc) { blkres = dwarf_formexprloc(loc_attr,&loc_block.bl_len, &loc_block.bl_data,error); if(blkres != DW_DLV_OK) { dwarf_loc_head_c_dealloc(llhead); return blkres; } loc_block.bl_from_loclist = 0; loc_block.bl_section_offset = (char *)loc_block.bl_data - (char *)dbg->de_debug_info.dss_data; loc_block.bl_locdesc_offset = 0; /* not relevant */ } else { Dwarf_Block *tblock = 0; blkres = dwarf_formblock(loc_attr, &tblock, error); if (blkres != DW_DLV_OK) { dwarf_loc_head_c_dealloc(llhead); return (blkres); } loc_block.bl_len = tblock->bl_len; loc_block.bl_data = tblock->bl_data; loc_block.bl_from_loclist = tblock->bl_from_loclist; loc_block.bl_section_offset = tblock->bl_section_offset; loc_block.bl_locdesc_offset = 0; /* not relevant */ /* We copied tblock contents to the stack var, so can dealloc tblock now. Avoids leaks. */ dwarf_dealloc(dbg, tblock, DW_DLA_BLOCK); } listlen = 1; /* One by definition of a location entry. */ /* This hack ensures that the Locdesc_c is marked DW_LLE_start_end_entry */ lowpc = 0; /* HACK */ highpc = (Dwarf_Unsigned) (-1LL); /* HACK */ llbuf = (Dwarf_Locdesc_c) _dwarf_get_alloc(dbg, DW_DLA_LOCDESC_C, listlen); if (!llbuf) { dwarf_loc_head_c_dealloc(llhead); _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); return (DW_DLV_ERROR); } llhead->ll_locdesc = llbuf; llhead->ll_locdesc = llbuf; llhead->ll_locdesc_count = listlen; llhead->ll_from_loclist = 0; llhead->ll_context = cucontext; llhead->ll_dbg = dbg; /* An empty location description (block length 0) means the code generator emitted no variable, the variable was not generated, it was unused or perhaps never tested after being set. Dwarf2, section 2.4.1 In other words, it is not an error, and we don't test for block length 0 specially here. */ /* Fills in the locdesc at index 0 */ blkres = _dwarf_get_locdesc_c(dbg, 0, /* fake locdesc is index 0 */ llhead, &loc_block, address_size, cucontext->cc_length_size, cucontext->cc_version_stamp, lowpc, highpc, error); if (blkres != DW_DLV_OK) { dwarf_loc_head_c_dealloc(llhead); /* low level error already set: let it be passed back */ return blkres; } } *ll_header_out = llhead; *listlen_out = listlen; return (DW_DLV_OK); }
static void print_expr (Dwarf_Attribute *attr, Dwarf_Op *expr, Dwarf_Addr addr) { uint8_t atom = expr->atom; const char *opname = dwarf_opcode_string (atom); assert (opname != NULL); switch (atom) { case DW_OP_deref: 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_lit0 ... DW_OP_lit31: case DW_OP_reg0 ... DW_OP_reg31: case DW_OP_nop: case DW_OP_stack_value: /* No arguments. */ printf ("%s", opname); break; case DW_OP_form_tls_address: /* No arguments. Special. Pops an address and pushes the corresponding address in the current thread local storage. Uses the thread local storage block of the defining module (executable, shared library). */ printf ("%s", opname); break; case DW_OP_GNU_push_tls_address: /* No arguments. Special. Not the same as DW_OP_form_tls_address. Pops an offset into the current thread local strorage and pushes back the actual address. */ printf ("%s", opname); break; case DW_OP_call_frame_cfa: /* No arguments. Special. Pushes Call Frame Address as computed by CFI data (dwarf_cfi_addrframe will fetch that info (either from the .eh_frame or .debug_frame CFI) and dwarf_frame_cfa translatesr the CFI instructions into a plain DWARF expression. Never used in CFI itself. */ if (attr == NULL) error (EXIT_FAILURE, 0, "%s used in CFI", opname); printf ("%s ", opname); if (cfi_eh == NULL && cfi_debug == NULL) error (EXIT_FAILURE, 0, "DW_OP_call_frame_cfa used but no cfi found."); Dwarf_Frame *frame; if (dwarf_cfi_addrframe (cfi_eh, addr + cfi_eh_bias, &frame) != 0 && dwarf_cfi_addrframe (cfi_debug, addr, &frame) != 0) error (EXIT_FAILURE, 0, "dwarf_cfi_addrframe 0x%" PRIx64 ": %s", addr, dwarf_errmsg (-1)); Dwarf_Op *cfa_ops; size_t cfa_nops; if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0) error (EXIT_FAILURE, 0, "dwarf_frame_cfa 0x%" PRIx64 ": %s", addr, dwarf_errmsg (-1)); if (cfa_nops < 1) error (EXIT_FAILURE, 0, "dwarf_frame_cfa no ops"); print_expr_block (NULL, cfa_ops, cfa_nops, 0); free (frame); break; case DW_OP_push_object_address: /* No arguments. Special. Pushes object address explicitly. Normally only done implicitly by DW_AT_data_member_location. Never used in CFI. */ if (attr == NULL) error (EXIT_FAILURE, 0, "%s used in CFI", opname); printf ("%s", opname); break; case DW_OP_addr: /* 1 address argument. */ printf ("%s(0x%" PRIx64 ")", opname, (Dwarf_Addr) expr->number); break; case DW_OP_const1u: case DW_OP_const2u: case DW_OP_const4u: case DW_OP_const8u: case DW_OP_constu: case DW_OP_pick: case DW_OP_plus_uconst: case DW_OP_regx: case DW_OP_piece: case DW_OP_deref_size: case DW_OP_xderef_size: /* 1 numeric unsigned argument. */ printf ("%s(%" PRIu64 ")", opname, expr->number); break; case DW_OP_call2: case DW_OP_call4: case DW_OP_call_ref: /* 1 DIE offset argument for more ops in location attribute of DIE. Never used in CFI. */ { if (attr == NULL) error (EXIT_FAILURE, 0, "%s used in CFI", opname); Dwarf_Attribute call_attr; if (dwarf_getlocation_attr (attr, expr, &call_attr) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for %s error %s", opname, dwarf_errmsg (-1)); Dwarf_Die call_die; if (dwarf_getlocation_die (attr, expr, &call_die) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_die for %s error %s", opname, dwarf_errmsg (-1)); Dwarf_Op *call_ops; size_t call_len; if (dwarf_getlocation (&call_attr, &call_ops, &call_len) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation for entry: %s", dwarf_errmsg (-1)); printf ("%s([%" PRIx64 "]) ", opname, dwarf_dieoffset (&call_die)); print_expr_block (&call_attr, call_ops, call_len, addr); } break; case DW_OP_const1s: case DW_OP_const2s: case DW_OP_const4s: case DW_OP_const8s: case DW_OP_consts: case DW_OP_skip: case DW_OP_bra: case DW_OP_breg0 ... DW_OP_breg31: /* 1 numeric signed argument. */ printf ("%s(%" PRId64 ")", opname, (Dwarf_Sword) expr->number); break; case DW_OP_fbreg: /* 1 numeric signed argument. Offset from frame base. */ if (attr == NULL) error (EXIT_FAILURE, 0, "%s used in CFI", opname); if (! has_frame_base) error (EXIT_FAILURE, 0, "DW_OP_fbreg used without a frame base"); printf ("%s(%" PRId64 ")", opname, (Dwarf_Sword) expr->number); break; case DW_OP_bregx: /* 2 arguments, unsigned register number, signed offset. */ printf ("%s(%" PRIu64 ",%" PRId64 ")", opname, expr->number, (Dwarf_Sword) expr->number2); break; case DW_OP_bit_piece: /* 2 arguments, unsigned size, unsigned offset. */ printf ("%s(%" PRIu64 ",%" PRIu64 ")", opname, expr->number, expr->number2); break; case DW_OP_implicit_value: /* Special, unsigned size plus block. */ { Dwarf_Attribute const_attr; Dwarf_Block block; if (dwarf_getlocation_attr (attr, expr, &const_attr) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_attr: %s", dwarf_errmsg (-1)); if (dwarf_formblock (&const_attr, &block) != 0) error (EXIT_FAILURE, 0, "dwarf_formblock: %s", dwarf_errmsg (-1)); /* This is the "old" way. Check they result in the same. */ Dwarf_Block block_impl; if (dwarf_getlocation_implicit_value (attr, expr, &block_impl) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_implicit_value: %s", dwarf_errmsg (-1)); assert (expr->number == block.length); assert (block.length == block_impl.length); printf ("%s(%" PRIu64 "){", opname, block.length); for (size_t i = 0; i < block.length; i++) { printf ("%02x", block.data[i]); assert (block.data[i] == block_impl.data[i]); } printf("}"); } break; case DW_OP_GNU_implicit_pointer: /* Special, DIE offset, signed offset. Referenced DIE has a location or const_value attribute. */ { if (attr == NULL) error (EXIT_FAILURE, 0, "%s used in CFI", opname); Dwarf_Attribute attrval; if (dwarf_getlocation_implicit_pointer (attr, expr, &attrval) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_implicit_pointer: %s", dwarf_errmsg (-1)); // Sanity check, results should be the same. Dwarf_Attribute attrval2; if (dwarf_getlocation_attr (attr, expr, &attrval2) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_attr: %s", dwarf_errmsg (-1)); assert (dwarf_whatattr (&attrval) == dwarf_whatattr (&attrval2)); assert (dwarf_whatform (&attrval) == dwarf_whatform (&attrval2)); // In theory two different valp pointers could point to the same // value. But here we really expect them to be the equal. assert (attrval.valp == attrval2.valp); Dwarf_Die impl_die; if (dwarf_getlocation_die (attr, expr, &impl_die) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_due: %s", dwarf_errmsg (-1)); printf ("%s([%" PRIx64 "],%" PRId64 ") ", opname, dwarf_dieoffset (&impl_die), expr->number2); if (dwarf_whatattr (&attrval) == DW_AT_const_value) printf ("<constant value>"); // Lookup type... else { // Lookup the location description at the current address. Dwarf_Op *exprval; size_t exprval_len; int locs = dwarf_getlocation_addr (&attrval, addr, &exprval, &exprval_len, 1); if (locs == 0) printf ("<no location>"); // This means "optimized out". else if (locs == 1) print_expr_block (&attrval, exprval, exprval_len, addr); else error (EXIT_FAILURE, 0, "dwarf_getlocation_addr attrval at addr 0x%" PRIx64 ", locs (%d): %s", addr, locs, dwarf_errmsg (-1)); } } break; case DW_OP_GNU_entry_value: /* Special, unsigned size plus expression block. All registers inside the block should be interpreted as they had on entering the function. dwarf_getlocation_attr will return an attribute containing the block as locexpr which can be retrieved with dwarf_getlocation. */ { Dwarf_Attribute entry_attr; if (dwarf_getlocation_attr (attr, expr, &entry_attr) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_attr: %s", dwarf_errmsg (-1)); Dwarf_Op *entry_ops; size_t entry_len; if (dwarf_getlocation (&entry_attr, &entry_ops, &entry_len) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation for entry: %s", dwarf_errmsg (-1)); printf ("%s(%zd) ", opname, entry_len); print_expr_block (attr, entry_ops, entry_len, addr); } break; case DW_OP_GNU_parameter_ref: /* Special, unsigned CU relative DIE offset pointing to a DW_TAG_formal_parameter. The value that parameter had at the call site of the current function will be put on the DWARF stack. The value can be retrieved by finding the DW_TAG_GNU_call_site_parameter which has as DW_AT_abstract_origin the same formal parameter DIE. */ { Dwarf_Die param; if (dwarf_getlocation_die (attr, expr, ¶m) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s", dwarf_errmsg (-1)); // XXX actually lookup DW_TAG_GNU_call_site_parameter printf ("%s[%" PRIx64 "]", opname, dwarf_dieoffset (¶m)); assert (expr->number == dwarf_cuoffset (¶m)); assert (dwarf_tag (¶m) == DW_TAG_formal_parameter); } break; case DW_OP_GNU_convert: case DW_OP_GNU_reinterpret: /* Special, unsigned CU relative DIE offset pointing to a DW_TAG_base_type. Pops a value, converts or reinterprets the value to the given type. When the argument is zero the value becomes untyped again. */ { Dwarf_Die type; Dwarf_Off off = expr->number; if (off != 0) { if (dwarf_getlocation_die (attr, expr, &type) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s", dwarf_errmsg (-1)); off = dwarf_dieoffset (&type); assert (expr->number == dwarf_cuoffset (&type)); printf ("%s", opname); print_base_type (&type); } else printf ("%s[%" PRIu64 "]", opname, off); } break; case DW_OP_GNU_regval_type: /* Special, unsigned register number plus unsigned CU relative DIE offset pointing to a DW_TAG_base_type. */ { Dwarf_Die type; if (dwarf_getlocation_die (attr, expr, &type) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s", dwarf_errmsg (-1)); assert (expr->number2 == dwarf_cuoffset (&type)); // XXX check size against base_type size? printf ("%s(reg%" PRIu64 ")", opname, expr->number); print_base_type (&type); } break; case DW_OP_GNU_deref_type: /* Special, unsigned size plus unsigned CU relative DIE offset pointing to a DW_TAG_base_type. */ { Dwarf_Die type; if (dwarf_getlocation_die (attr, expr, &type) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s", dwarf_errmsg (-1)); assert (expr->number2 == dwarf_cuoffset (&type)); // XXX check size against base_type size? printf ("%s(%" PRIu64 ")", opname, expr->number); print_base_type (&type); } break; case DW_OP_GNU_const_type: /* Special, unsigned CU relative DIE offset pointing to a DW_TAG_base_type, an unsigned size length plus a block with the constant value. */ { Dwarf_Die type; if (dwarf_getlocation_die (attr, expr, &type) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s", dwarf_errmsg (-1)); assert (expr->number == dwarf_cuoffset (&type)); Dwarf_Attribute const_attr; if (dwarf_getlocation_attr (attr, expr, &const_attr) != 0) error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for type: %s", dwarf_errmsg (-1)); Dwarf_Block block; if (dwarf_formblock (&const_attr, &block) != 0) error (EXIT_FAILURE, 0, "dwarf_formblock for type: %s", dwarf_errmsg (-1)); printf ("%s", opname); print_base_type (&type); printf ("(%" PRIu64 ")[", block.length); for (size_t i = 0; i < block.length; i++) printf ("%02x", block.data[i]); printf("]"); } break; default: error (EXIT_FAILURE, 0, "unhandled opcode: DW_OP_%s (0x%x)", opname, atom); } }
static struct variable* analyze_variable(Dwarf_Die *die, Dwarf_Files *files, struct expr_context *ctx) { int ret; Dwarf_Attribute at; struct variable* var; /* ignore declarations */ if (dwarf_attr_integrate(die, DW_AT_declaration, &at) != NULL) { bool flag; ret = dwarf_formflag(&at, &flag); fail_if(ret == -1, "dwarf_formflag"); if (flag) return NULL; } var = xalloc(sizeof(struct variable)); analyze_name_location(die, files, &var->name, &var->loc); if (dwarf_attr_integrate(die, DW_AT_type, &at) != NULL) { Dwarf_Die type_die; if (dwarf_formref_die(&at, &type_die) == NULL) fail("dwarf_formref_die"); analyze_type(&type_die, &(var->type)); } if (dwarf_attr_integrate(die, DW_AT_const_value, &at) != NULL) { Dwarf_Word w; Dwarf_Block bl; unsigned int form = dwarf_whatform(&at); debug("variable %s has constant value of form %x", var->name, form); if (dwarf_formudata(&at, &w) == 0) { fail_if(sizeof(w) < var->type.width, "constant value too small"); var->value = xalloc(var->type.width); memcpy(var->value, &w, var->type.width); } else if (dwarf_formblock(&at, &bl) == 0) { fail_if(bl.length < var->type.width, "constant value too small"); var->value = xalloc(var->type.width); memcpy(var->value, bl.data, var->type.width); } else { warn("unable to get constant value of variable %x (form %x)", var->name, form); } } else if (dwarf_attr_integrate(die, DW_AT_location, &at) != NULL) { size_t exprlen; Dwarf_Op *expr; ret = dwarf_getlocation_addr(&at, ctx->ip, &expr, &exprlen, 1); if (ret != 1) { if (ret == -1) /* it seems that elfutils have some kind of problem with * DW_OP_GNU_entry_value but that operation is useless for us * anyway */ warn("cannot get location for variable %s (ip: %lx), %s", var->name, ctx->ip, dwarf_errmsg(-1)); else if (ret == 0) debug("no location available for variable %s (ip: %lx)", var->name, ctx->ip); else fail("unreachable reached"); return var; } var->value = evaluate_loc_expr(expr, exprlen, ctx, var->type.width); } return var; }