blob: 779f74239e0be53328f53af25cc21230197302da [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2015 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* Transformation of DWARF expressions to a portable form.
*
* Functions in this module use exceptions to report errors, see exceptions.h
*/
#include <tcf/config.h>
#if ENABLE_ELF && ENABLE_DebugContext
#include <assert.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/exceptions.h>
#include <tcf/framework/cache.h>
#include <tcf/services/dwarf.h>
#include <tcf/services/dwarfecomp.h>
#include <tcf/services/dwarfreloc.h>
#include <tcf/services/elf-loader.h>
#include <tcf/services/elf-symbols.h>
#include <tcf/services/stacktrace.h>
#include <tcf/services/dwarfecomp-ext.h>
typedef struct JumpInfo {
U1_T op;
I2_T delta;
I2_T jump_offs;
size_t size;
size_t src_pos;
size_t dst_pos;
struct JumpInfo * next;
} JumpInfo;
static U1_T * buf = NULL;
static size_t buf_pos = 0;
static size_t buf_max = 0;
static JumpInfo * jumps = NULL;
static DWARFExpressionInfo * expr = NULL;
static size_t expr_pos = 0;
static Context * expr_ctx = NULL;
static int expr_frame = 0;
static ContextAddress expr_code_addr = 0;
static ContextAddress expr_code_size = 0;
static int expr_big_endian = 0;
static ObjectInfo ** call_site_buf = NULL;
static unsigned call_site_cnt = 0;
static unsigned call_site_max = 0;
static void add(unsigned n) {
if (buf_pos >= buf_max) {
buf_max *= 2;
buf = (U1_T *)tmp_realloc(buf, buf_max);
}
buf[buf_pos++] = (U1_T)n;
}
static void copy(size_t n) {
while (n > 0) {
if (expr_pos >= expr->expr_size) exception(ERR_INV_DWARF);
add(expr->expr_addr[expr_pos++]);
n--;
}
}
static void copy_leb128(void) {
for (;;) {
U1_T n = expr->expr_addr[expr_pos++];
add(n);
if ((n & 0x80) == 0) break;
}
}
static void add_uleb128(U8_T x) {
for (;;) {
U1_T n = (U1_T)(x & 0x7Fu);
x = x >> 7;
if (x == 0) {
add(n);
break;
}
add(n | 0x80u);
}
}
static U4_T read_u4leb128(void) {
U4_T v = 0;
int i = 0;
for (;; i += 7) {
U1_T n = expr->expr_addr[expr_pos++];
v |= (U4_T)(n & 0x7f) << i;
if ((n & 0x80) == 0) break;
}
return v;
}
static U8_T read_u8leb128(void) {
U8_T v = 0;
int i = 0;
for (;; i += 7) {
U1_T n = expr->expr_addr[expr_pos++];
v |= (U8_T)(n & 0x7f) << i;
if ((n & 0x80) == 0) break;
}
return v;
}
static I8_T read_i8leb128(void) {
U8_T v = 0;
int i = 0;
for (;; i += 7) {
U1_T n = expr->expr_addr[expr_pos++];
v |= (U8_T)(n & 0x7Fu) << i;
if ((n & 0x80) == 0) {
v |= -(I8_T)(n & 0x40) << i;
break;
}
}
return (I8_T)v;
}
static void add_sleb128(I8_T x) {
for (;;) {
U1_T n = (U1_T)(x & 0x7Fu);
x = x >> 7;
if ((x == 0 && (n & 0x40) == 0) ||
(x == -1 && (n & 0x40) != 0)) {
add(n);
break;
}
add(n | 0x80u);
}
}
static void set_u2(size_t pos, U2_T v) {
if (expr_big_endian) {
buf[pos++] = (U1_T)((v >> 8) & 0xffu);
buf[pos++] = (U1_T)(v & 0xffu);
}
else {
buf[pos++] = (U1_T)(v & 0xffu);
buf[pos++] = (U1_T)((v >> 8) & 0xffu);
}
}
static void add_expression(DWARFExpressionInfo * info);
static int get_num_prop(ObjectInfo * obj, U2_T at, U8_T * res) {
Trap trap;
PropertyValue v;
if (!set_trap(&trap)) return 0;
read_and_evaluate_dwarf_object_property(expr_ctx, STACK_NO_FRAME, obj, at, &v);
*res = get_numeric_property_value(&v);
clear_trap(&trap);
return 1;
}
static void op_addr(void) {
ContextAddress addr = 0;
ELF_Section * section = NULL;
U8_T pos = 0;
int rt = 0;
expr_pos++;
pos = expr->expr_addr + expr_pos - (U1_T *)expr->section->data;
dio_EnterSection(&expr->object->mCompUnit->mDesc, expr->section, pos);
switch (expr->object->mCompUnit->mDesc.mAddressSize) {
case 2: {
U2_T x = dio_ReadU2();
drl_relocate_in_context(expr_ctx, expr->section, pos, &x, sizeof(x), &section, &rt);
addr = x;
break;
}
case 4: {
U4_T x = dio_ReadU4();
drl_relocate_in_context(expr_ctx, expr->section, pos, &x, sizeof(x), &section, &rt);
addr = x;
break;
}
case 8: {
U8_T x = dio_ReadU8();
drl_relocate_in_context(expr_ctx, expr->section, pos, &x, sizeof(x), &section, &rt);
addr = x;
break;
}
default:
str_exception(ERR_INV_DWARF, "Invalid data size");
return;
}
expr_pos += (size_t)(dio_GetPos() - pos);
dio_ExitSection();
if (expr_pos < expr->expr_size && expr->expr_addr[expr_pos] == OP_GNU_push_tls_address) {
/* Bug in some versions of GCC: OP_addr used instead of OP_const, use link-time value */
}
else if (!rt) {
addr = elf_map_to_run_time_address(expr_ctx, expr->object->mCompUnit->mFile, section, addr);
if (errno) str_exception(errno, "Cannot get object run-time address");
}
add(OP_constu);
add_uleb128(addr);
}
static ObjectInfo * get_parent_function(ObjectInfo * info) {
while (info != NULL) {
switch (info->mTag) {
case TAG_global_subroutine:
case TAG_inlined_subroutine:
case TAG_subroutine:
case TAG_subprogram:
case TAG_entry_point:
return info;
}
info = get_dwarf_parent(info);
}
return NULL;
}
static int check_section(CompUnit * unit, ELF_Section * sec_obj, ELF_Section * sec_addr) {
if (sec_obj == NULL) sec_obj = unit->mTextSection;
if (sec_obj == NULL) return 1;
if (sec_addr == NULL) return 1;
return sec_obj == sec_addr;
}
int dwarf_check_in_range(ObjectInfo * obj, ELF_Section * sec, U8_T addr) {
if (obj->mFlags & DOIF_ranges) {
Trap trap;
if (set_trap(&trap)) {
CompUnit * unit = obj->mCompUnit;
DWARFCache * cache = get_dwarf_cache(unit->mFile);
ELF_Section * debug_ranges = cache->mDebugRanges;
if (debug_ranges != NULL) {
ContextAddress base = unit->mObject->u.mCode.mLowPC;
int res = 0;
#if 0
U8_T entry_pc = 0;
if (obj->mTag == TAG_inlined_subroutine &&
get_num_prop(obj, AT_entry_pc, &entry_pc))
base = (ContextAddress)entry_pc;
#endif
dio_EnterSection(&unit->mDesc, debug_ranges, obj->u.mCode.mHighPC.mRanges);
for (;;) {
U8_T AddrMax = ~(U8_T)0;
ELF_Section * x_sec = NULL;
ELF_Section * y_sec = NULL;
U8_T x = dio_ReadAddress(&x_sec);
U8_T y = dio_ReadAddress(&y_sec);
if (x == 0 && y == 0) break;
if (unit->mDesc.mAddressSize < 8) AddrMax = ((U8_T)1 << unit->mDesc.mAddressSize * 8) - 1;
if (x == AddrMax) {
base = (ContextAddress)y;
}
else if (check_section(unit, x_sec, sec) && check_section(unit, y_sec, sec)) {
x = base + x;
y = base + y;
if (x <= addr && addr < y) {
res = 1;
break;
}
}
}
dio_ExitSection();
clear_trap(&trap);
return res;
}
clear_trap(&trap);
}
return 0;
}
if (obj->u.mCode.mHighPC.mAddr > obj->u.mCode.mLowPC && check_section(obj->mCompUnit, obj->u.mCode.mSection, sec)) {
return addr >= obj->u.mCode.mLowPC && addr < obj->u.mCode.mHighPC.mAddr;
}
return 0;
}
static ObjectInfo * get_function_by_addr(ObjectInfo * parent, ELF_Section * sec, U8_T addr) {
ObjectInfo * obj = get_dwarf_children(parent);
while (obj != NULL) {
switch (obj->mTag) {
case TAG_global_subroutine:
case TAG_subroutine:
case TAG_subprogram:
if (dwarf_check_in_range(obj, sec, addr)) return obj;
break;
}
obj = obj->mSibling;
}
return NULL;
}
static void add_fbreg_expression(DWARFExpressionInfo * info, I8_T offs) {
size_t pos = buf_pos;
switch (*info->expr_addr) {
case OP_reg:
add(OP_basereg);
{
unsigned i = 1;
while (i < info->object->mCompUnit->mDesc.mAddressSize + 1u) {
add(info->expr_addr[i++]);
}
if (info->expr_size != i) break;
}
add_sleb128(offs);
return;
case OP_regx:
add(OP_bregx);
{
unsigned i = 1;
for (;;) {
U1_T n = info->expr_addr[i++];
add(n);
if ((n & 0x80) == 0) break;
}
if (info->expr_size != i) break;
}
add_sleb128(offs);
return;
case OP_reg0:
case OP_reg1:
case OP_reg2:
case OP_reg3:
case OP_reg4:
case OP_reg5:
case OP_reg6:
case OP_reg7:
case OP_reg8:
case OP_reg9:
case OP_reg10:
case OP_reg11:
case OP_reg12:
case OP_reg13:
case OP_reg14:
case OP_reg15:
case OP_reg16:
case OP_reg17:
case OP_reg18:
case OP_reg19:
case OP_reg20:
case OP_reg21:
case OP_reg22:
case OP_reg23:
case OP_reg24:
case OP_reg25:
case OP_reg26:
case OP_reg27:
case OP_reg28:
case OP_reg29:
case OP_reg30:
case OP_reg31:
if (info->expr_size != 1) break;
add(OP_breg0 + (*info->expr_addr - OP_reg0));
add_sleb128(offs);
return;
}
buf_pos = pos;
add_expression(info);
if (offs == 0) return;
add(OP_consts);
add_sleb128(offs);
add(OP_add);
}
static void add_code_range(DWARFExpressionInfo * info) {
if (info->code_size) {
if (expr_code_size) {
if (info->code_addr > expr_code_addr) {
U8_T d = info->code_addr - expr_code_addr;
assert(expr_code_size > d);
expr_code_addr += d;
expr_code_size -= d;
}
if (info->code_addr + info->code_size < expr_code_addr + expr_code_size) {
U8_T d = (expr_code_addr + expr_code_size) - (info->code_addr + info->code_size);
assert(expr_code_size > d);
expr_code_size -= d;
}
}
else {
expr_code_addr = info->code_addr;
expr_code_size = info->code_size;
}
}
}
static U8_T get_frame_pc(void) {
uint64_t pc = 0;
StackFrame * frame = NULL;
if (expr_frame == STACK_NO_FRAME) str_exception(ERR_INV_CONTEXT, "Need stack frame");
if (get_frame_info(expr_ctx, expr_frame, &frame) < 0) exception(errno);
if (read_reg_value(frame, get_PC_definition(expr_ctx), &pc) < 0) exception(errno);
return pc;
}
static void add_expression_list(DWARFExpressionInfo * info, int fbreg, I8_T offs) {
int peer_version = 1;
Channel * c = cache_channel();
RegisterDefinition * pc = get_PC_definition(expr_ctx);
if (c != NULL) {
int i;
peer_version = 0;
for (i = 0; i < c->peer_service_cnt; i++) {
char * nm = c->peer_service_list[i];
if (strcmp(nm, "SymbolsProxyV1") == 0) peer_version = 1;
}
}
if (info->code_size > 0 && (peer_version == 0 || pc == NULL || pc->dwarf_id < 0)) {
/* The peer does not support OP_TCF_switch */
U8_T pc = get_frame_pc();
while (info != NULL && info->code_size > 0 && (info->code_addr > pc || pc - info->code_addr >= info->code_size)) {
info = info->next;
}
if (info == NULL) {
str_exception(ERR_OTHER, "Object is not available at this location in the code");
}
if (!fbreg) add_expression(info);
else add_fbreg_expression(info, offs);
add_code_range(info);
}
else if (info->code_size > 0) {
size_t switch_pos;
add(OP_bregx);
add_uleb128(pc->dwarf_id);
add_uleb128(0);
add(OP_TCF_switch);
add(0);
add(0);
switch_pos = buf_pos;
while (info != NULL) {
if (expr_code_size == 0 || info->code_size == 0 ||
(expr_code_addr + expr_code_size > info->code_addr &&
expr_code_addr < info->code_addr + info->code_size)) {
size_t case_pos;
U8_T org_expr_code_addr = expr_code_addr;
U8_T org_expr_code_size = expr_code_size;
add_code_range(info);
add(0);
add(0);
case_pos = buf_pos;
add_uleb128(info->code_addr);
add_uleb128(info->code_size);
if (!fbreg) add_expression(info);
else add_fbreg_expression(info, offs);
expr_code_addr = org_expr_code_addr;
expr_code_size = org_expr_code_size;
set_u2(case_pos - 2, (U2_T)(buf_pos - case_pos));
if (info->code_size == 0) break;
}
info = info->next;
}
add(0);
add(0);
if (buf_pos - switch_pos > 0xffff)
str_exception(ERR_INV_DWARF, "Location expression is too large");
set_u2(switch_pos - 2, (U2_T)(buf_pos - switch_pos));
}
else if (fbreg) {
add_fbreg_expression(info, offs);
}
else {
assert(offs == 0);
add_expression(info);
}
}
static void op_fbreg(void) {
PropertyValue fp;
DWARFExpressionInfo * info;
ObjectInfo * parent = get_parent_function(expr->object);
I8_T offs = 0;
Trap trap;
expr_pos++;
offs = read_i8leb128();
memset(&fp, 0, sizeof(fp));
if (parent == NULL && expr->object->mTag == TAG_subrange_type && expr->object->mParent != NULL) {
/* Workaround for invalid DWARF generated by GCC for
* C99-style dynamic arrays */
ObjectInfo * obj = get_dwarf_children(expr->object->mCompUnit->mObject);
while (obj != NULL && parent == NULL) {
if (obj->mTag == TAG_subprogram) {
ObjectInfo * arg = get_dwarf_children(obj);
while (arg != NULL && parent == NULL) {
if (arg->mType == expr->object->mParent) parent = obj;
arg = arg->mSibling;
}
}
obj = obj->mSibling;
}
}
if (parent == NULL) str_exception(ERR_INV_DWARF, "OP_fbreg: no parent function");
if (set_trap(&trap)) {
read_dwarf_object_property(expr_ctx, STACK_NO_FRAME, parent, AT_frame_base, &fp);
clear_trap(&trap);
dwarf_get_expression_list(&fp, &info);
add_expression_list(info, 1, offs);
}
else if (trap.error != ERR_SYM_NOT_FOUND) {
str_exception(trap.error, "OP_fbreg: cannot read AT_frame_base");
}
else {
U8_T pc = get_frame_pc();
ELF_File * file = NULL;
ELF_Section * sec = NULL;
ObjectInfo * func = NULL;
U8_T lt = elf_map_to_link_time_address(expr_ctx, pc, 1, &file, &sec);
if (file == NULL) str_exception(ERR_INV_CONTEXT, "Cannot get link-time address of the stack frame");
func = get_function_by_addr(expr->object->mCompUnit->mObject, sec, lt);
if (func == NULL) {
str_exception(ERR_INV_DWARF, "OP_fbreg: no parent function");
}
else if (set_trap(&trap)) {
read_dwarf_object_property(expr_ctx, STACK_NO_FRAME, func, AT_frame_base, &fp);
clear_trap(&trap);
dwarf_get_expression_list(&fp, &info);
while (info != NULL && info->code_size > 0 && (info->code_addr > pc || pc - info->code_addr >= info->code_size)) {
info = info->next;
}
if (info == NULL) {
str_exception(ERR_OTHER, "Object is not available at this location in the code");
}
add_fbreg_expression(info, offs);
}
else {
str_exception(trap.error, "OP_fbreg: cannot read AT_frame_base");
}
}
}
static void op_implicit_pointer(void) {
PropertyValue pv;
U1_T op = expr->expr_addr[expr_pos];
CompUnit * unit = expr->object->mCompUnit;
int arg_size = unit->mDesc.m64bit ? 8 : 4;
ObjectInfo * ref_obj = NULL;
ContextAddress ref_id = 0;
U8_T offset = 0;
U8_T dio_pos = 0;
expr_pos++;
if (op == OP_GNU_implicit_pointer && unit->mDesc.mVersion < 3) arg_size = unit->mDesc.mAddressSize;
dio_pos = expr->expr_addr + expr_pos - (U1_T *)expr->section->data;
dio_EnterSection(&expr->object->mCompUnit->mDesc, expr->section, dio_pos);
ref_id = dio_ReadAddressX(NULL, arg_size);
offset = dio_ReadU8LEB128();
expr_pos += (size_t)(dio_GetPos() - dio_pos);
dio_ExitSection();
ref_obj = find_object(unit->mDesc.mSection, ref_id);
if (ref_obj == NULL) str_exception(ERR_INV_DWARF, "OP_implicit_pointer: invalid object reference");
memset(&pv, 0, sizeof(pv));
if (ref_obj->mFlags & DOIF_location) {
DWARFExpressionInfo * info = NULL;
read_dwarf_object_property(expr_ctx, STACK_NO_FRAME, ref_obj, AT_location, &pv);
dwarf_get_expression_list(&pv, &info);
add_expression_list(info, 0, 0);
}
else if (ref_obj->mFlags & DOIF_const_value) {
size_t i;
union {
U4_T u4;
U8_T u8;
U1_T arr[8];
} buf;
read_dwarf_object_property(expr_ctx, STACK_NO_FRAME, ref_obj, AT_const_value, &pv);
switch (pv.mForm) {
case FORM_SDATA:
case FORM_UDATA:
pv.mAddr = buf.arr;
if (unit->mFile->elf64) {
pv.mSize = 8;
buf.u8 = pv.mValue;
}
else {
pv.mSize = 4;
buf.u4 = (U4_T)pv.mValue;
}
if (unit->mFile->byte_swap) swap_bytes(buf.arr, pv.mSize);
break;
}
if (pv.mAddr == NULL) str_exception(ERR_INV_DWARF, "Invalid implicit pointer");
add(OP_implicit_value);
add_uleb128(pv.mSize);
for (i = 0; i < pv.mSize; i++) {
add(*((U1_T *)pv.mAddr + i));
}
}
else {
str_exception(ERR_INV_DWARF, "Invalid implicit pointer");
}
add(OP_TCF_offset);
add_uleb128(offset);
}
static void op_push_tls_address(void) {
expr_pos++;
if (expr_pos == 1 && expr_pos < expr->expr_size) {
/* This looks like a bug in GCC: offset sometimes is emitted after OP_GNU_push_tls_address */
U1_T op = expr->expr_addr[expr_pos];
switch (op) {
case OP_const4u: copy(5); break;
case OP_const8u: copy(9); break;
case OP_constu: add(op); expr_pos++; copy_leb128(); break;
}
}
if (!context_has_state(expr_ctx)) str_exception(ERR_INV_CONTEXT,
"Thread local variable, but context is not a thread");
COMPUTE_TLS_ADDRESS;
}
static void op_call(void) {
U8_T ref_id = 0;
DIO_UnitDescriptor * desc = &expr->object->mCompUnit->mDesc;
U8_T dio_pos = 0;
U1_T opcode = 0;
ObjectInfo * ref_obj = NULL;
DWARFExpressionInfo * info = NULL;
PropertyValue pv;
opcode = expr->expr_addr[expr_pos++];
dio_pos = expr->expr_addr + expr_pos - (U1_T *)expr->section->data;
dio_EnterSection(desc, expr->section, dio_pos);
switch (opcode) {
case OP_call2:
ref_id = desc->mSection->addr + desc->mUnitOffs + dio_ReadU2();
break;
case OP_call4:
ref_id = desc->mSection->addr + desc->mUnitOffs + dio_ReadU4();
break;
case OP_call_ref:
{
ELF_Section * section = NULL;
int size = desc->m64bit ? 8 : 4;
if (desc->mVersion < 3) size = desc->mAddressSize;
ref_id = dio_ReadAddressX(&section, size);
}
break;
}
expr_pos += (size_t)(dio_GetPos() - dio_pos);
dio_ExitSection();
ref_obj = find_object(expr->object->mCompUnit->mDesc.mSection, ref_id);
if (ref_obj == NULL) str_exception(ERR_INV_DWARF, "Invalid reference in OP_call");
read_dwarf_object_property(expr_ctx, STACK_NO_FRAME, ref_obj, AT_location, &pv);
dwarf_get_expression_list(&pv, &info);
add_expression_list(info, 0, 0);
}
static void add_call_sites(ObjectInfo * obj, U8_T addr, U8_T size) {
while (obj != NULL) {
switch (obj->mTag) {
case TAG_subprogram:
case TAG_subroutine:
case TAG_inlined_subroutine:
case TAG_lexical_block:
add_call_sites(get_dwarf_children(obj), addr, size);
break;
case TAG_GNU_call_site:
if ((obj->mFlags & DOIF_low_pc) != 0 && (size == 0 ||
(obj->u.mCode.mLowPC >= addr && obj->u.mCode.mLowPC < addr + size))) {
if (call_site_cnt >= call_site_max) {
call_site_max *= 2;
call_site_buf = (ObjectInfo **)tmp_realloc(call_site_buf,
sizeof(ObjectInfo *) * call_site_max);
}
call_site_buf[call_site_cnt++] = obj;
}
break;
}
obj = obj->mSibling;
}
}
static void find_call_sites(CompUnit * unit, U8_T addr, U8_T size) {
ObjectInfo * obj = unit->mObject->mChildren;
call_site_cnt = 0;
call_site_max = 16;
call_site_buf = (ObjectInfo **)tmp_alloc(sizeof(ObjectInfo *) * call_site_max);
while (obj != NULL) {
if (obj->mTag == TAG_subprogram) {
add_call_sites(get_dwarf_children(obj), addr, size);
}
obj = obj->mSibling;
}
}
static void op_entry_value(void) {
size_t size = 0;
size_t size_pos = 0;
DWARFExpressionInfo info;
add(expr->expr_addr[expr_pos++]);
size_pos = buf_pos;
add(0);
add(0);
add(0);
size = read_u4leb128();
memset(&info, 0, sizeof(info));
info.object = expr->object;
info.section = expr->section;
info.expr_addr = expr->expr_addr + expr_pos;
info.expr_size = size;
add_expression(&info);
expr_pos += size;
size = buf_pos - size_pos - 3;
buf[size_pos++] = (U1_T)((size & 0x7f) | 0x80);
buf[size_pos++] = (U1_T)(((size >> 7) & 0x7f) | 0x80);
buf[size_pos++] = (U1_T)((size >> 14) & 0x7f);
}
static void op_parameter_ref(void) {
U8_T ref_id = 0;
size_t size = 0;
size_t size_pos = 0;
U8_T dio_pos = 0;
DIO_UnitDescriptor * desc = &expr->object->mCompUnit->mDesc;
unsigned n;
DWARFExpressionInfo * info = NULL;
DWARFExpressionInfo ** info_last = &info;
PropertyValue pv;
expr_pos++;
add(OP_GNU_entry_value);
size_pos = buf_pos;
add(0);
add(0);
add(0);
dio_pos = expr->expr_addr + expr_pos - (U1_T *)expr->section->data;
dio_EnterSection(desc, expr->section, dio_pos);
ref_id = desc->mSection->addr + desc->mUnitOffs + dio_ReadU4();
expr_pos += (size_t)(dio_GetPos() - dio_pos);
dio_ExitSection();
find_call_sites(expr->object->mCompUnit, expr->code_addr, expr_code_size);
for (n = 0; n < call_site_cnt; n++) {
ObjectInfo * site = call_site_buf[n];
ObjectInfo * args = get_dwarf_children(site);
while (args != NULL) {
if (args->mTag == TAG_GNU_call_site_parameter && args->mFlags & DOIF_abstract_origin) {
read_and_evaluate_dwarf_object_property(expr_ctx, STACK_NO_FRAME, args, AT_abstract_origin, &pv);
if (get_numeric_property_value(&pv) == ref_id) {
read_dwarf_object_property(expr_ctx, STACK_NO_FRAME, args, AT_GNU_call_site_value, &pv);
dwarf_get_expression_list(&pv, info_last);
while (*info_last != NULL) {
DWARFExpressionInfo * e = *info_last;
if (e->code_size == 0 ||
(e->code_addr <= site->u.mCode.mLowPC &&
e->code_addr + e->code_size > site->u.mCode.mLowPC)) {
e->code_addr = site->u.mCode.mLowPC;
e->code_size = 1;
info_last = &e->next;
}
else {
*info_last = e->next;
}
}
}
}
args = args->mSibling;
}
}
if (info == NULL) {
str_exception(ERR_OTHER, "Object is not available at this location in the code");
}
add_expression_list(info, 0, 0);
size = buf_pos - size_pos - 3;
buf[size_pos++] = (U1_T)((size & 0x7f) | 0x80);
buf[size_pos++] = (U1_T)(((size >> 7) & 0x7f) | 0x80);
buf[size_pos++] = (U1_T)((size >> 14) & 0x7f);
}
static void adjust_jumps(void) {
JumpInfo * i = jumps;
while (i != NULL) {
if (i->op == OP_bra || i->op == OP_skip) {
int delta = 0;
JumpInfo * j = jumps;
while (j != NULL) {
if (i->jump_offs > 0) {
if (j->src_pos > i->src_pos && j->src_pos < i->src_pos + i->jump_offs) {
delta += j->delta;
}
}
else {
if (j->src_pos > i->src_pos + i->jump_offs && j->src_pos < i->src_pos) {
delta -= j->delta;
}
}
j = j->next;
}
if (delta != 0) {
U2_T new_offs = (U2_T)(i->jump_offs + delta);
set_u2(i->dst_pos + 1, new_offs);
}
}
i = i->next;
}
}
static void check_frame(void) {
if (expr_frame != STACK_NO_FRAME) return;
str_exception(ERR_OTHER, "Object location is relative to a stack frame");
}
static void add_expression(DWARFExpressionInfo * info) {
DWARFExpressionInfo * org_expr = expr;
size_t org_expr_pos = expr_pos;
JumpInfo * org_jumps = jumps;
expr = info;
expr_pos = 0;
jumps = NULL;
if (expr->object->mCompUnit->mFile->big_endian != expr_big_endian) {
str_exception(ERR_OTHER, "Invalid endianness of location expression");
}
while (expr_pos < info->expr_size) {
size_t op_src_pos = expr_pos;
size_t op_dst_pos = buf_pos;
U1_T op = info->expr_addr[expr_pos];
switch (op) {
case OP_const1u:
case OP_const1s:
case OP_pick:
case OP_deref_size:
case OP_xderef_size:
copy(2);
break;
case OP_const:
case OP_reg:
copy(1 + info->object->mCompUnit->mDesc.mAddressSize);
break;
case OP_basereg:
check_frame();
copy(1 + info->object->mCompUnit->mDesc.mAddressSize);
break;
case OP_const2u:
case OP_const2s:
copy(3);
break;
case OP_const4u:
case OP_const4s:
copy(5);
break;
case OP_const8u:
case OP_const8s:
copy(9);
break;
case OP_breg0:
case OP_breg1:
case OP_breg2:
case OP_breg3:
case OP_breg4:
case OP_breg5:
case OP_breg6:
case OP_breg7:
case OP_breg8:
case OP_breg9:
case OP_breg10:
case OP_breg11:
case OP_breg12:
case OP_breg13:
case OP_breg14:
case OP_breg15:
case OP_breg16:
case OP_breg17:
case OP_breg18:
case OP_breg19:
case OP_breg20:
case OP_breg21:
case OP_breg22:
case OP_breg23:
case OP_breg24:
case OP_breg25:
case OP_breg26:
case OP_breg27:
case OP_breg28:
case OP_breg29:
case OP_breg30:
case OP_breg31:
check_frame();
add(op);
expr_pos++;
copy_leb128();
break;
case OP_regx:
case OP_constu:
case OP_consts:
case OP_plus_uconst:
case OP_piece:
add(op);
expr_pos++;
copy_leb128();
if (op == OP_constu && op_src_pos == 0 && expr_pos == info->expr_size && info->attr == AT_data_member_location) {
/* GCC bug - missing OP_add */
add(OP_add);
}
break;
case OP_bra:
case OP_skip:
{
U2_T x0 = info->expr_addr[expr_pos + 1];
U2_T x1 = info->expr_addr[expr_pos + 2];
U2_T offs = expr_big_endian ? (x0 << 8) | x1 : x0 | (x1 << 8);
if (offs != 0) {
JumpInfo * i = (JumpInfo *)tmp_alloc_zero(sizeof(JumpInfo));
i->op = op;
i->jump_offs = (I2_T)offs;
i->src_pos = op_src_pos;
i->dst_pos = op_dst_pos;
i->next = jumps;
jumps = i;
copy(3);
}
}
break;
case OP_bregx:
check_frame();
add(op);
expr_pos++;
copy_leb128();
copy_leb128();
break;
case OP_bit_piece:
add(op);
expr_pos++;
copy_leb128();
copy_leb128();
break;
case OP_implicit_value:
{
unsigned i = 0;
size_t j = expr_pos + 1u;
size_t size = 0;
for (;; i += 7) {
U1_T n = info->expr_addr[j++];
size |= (n & 0x7Fu) << i;
if ((n & 0x80) == 0) break;
}
copy(j + size - expr_pos);
}
break;
case OP_fbreg:
check_frame();
op_fbreg();
break;
case OP_addr:
op_addr();
break;
case OP_implicit_pointer:
case OP_GNU_implicit_pointer:
op_implicit_pointer();
break;
case OP_form_tls_address:
case OP_GNU_push_tls_address:
op_push_tls_address();
break;
case OP_call2:
case OP_call4:
case OP_call_ref:
op_call();
break;
case OP_GNU_entry_value:
check_frame();
op_entry_value();
break;
case OP_GNU_parameter_ref:
check_frame();
op_parameter_ref();
break;
case OP_GNU_const_type:
expr_pos++;
{
U8_T type = read_u8leb128();
unsigned size = expr->expr_addr[expr_pos++];
int sign = 0;
ObjectInfo * obj = find_object(
expr->object->mCompUnit->mDesc.mSection,
expr->object->mCompUnit->mDesc.mSection->addr +
expr->object->mCompUnit->mDesc.mUnitOffs + type);
if (obj == NULL) str_exception(ERR_INV_DWARF, "Invalid reference in OP_GNU_const_type");
if (obj->mTag != TAG_base_type) str_exception(ERR_INV_DWARF, "Invalid object tag in OP_GNU_const_type");
switch (obj->u.mFundType) {
case ATE_address:
case ATE_unsigned:
case ATE_unsigned_char:
case ATE_unsigned_fixed:
case ATE_UTF:
break;
case ATE_signed:
case ATE_signed_char:
case ATE_signed_fixed:
sign = 1;
break;
default:
str_exception(ERR_INV_DWARF, "Unsupported type in OP_GNU_const_type");
break;
}
switch (size) {
case 1: add(sign ? OP_const1s : OP_const1u); break;
case 2: add(sign ? OP_const2s : OP_const2u); break;
case 4: add(sign ? OP_const4s : OP_const4u); break;
case 8: add(sign ? OP_const8s : OP_const8u); break;
default: str_exception(ERR_INV_DWARF, "Invalid size in OP_GNU_const_type");
}
copy(size);
}
break;
case OP_GNU_regval_type:
expr_pos++;
{
U8_T size = 0;
U4_T reg = read_u4leb128();
U8_T type = read_u8leb128();
ObjectInfo * obj = find_object(
expr->object->mCompUnit->mDesc.mSection,
expr->object->mCompUnit->mDesc.mSection->addr +
expr->object->mCompUnit->mDesc.mUnitOffs + type);
if (obj == NULL) str_exception(ERR_INV_DWARF, "Invalid reference in OP_GNU_regval_type");
if (!get_num_prop(obj, AT_byte_size, &size)) str_exception(ERR_INV_DWARF, "Invalid reference in OP_GNU_regval_type");
add(OP_regx);
add_uleb128(reg);
add(OP_piece);
add_uleb128(size);
}
break;
case OP_GNU_convert:
expr_pos++;
{
U8_T type = read_u8leb128();
if (type == 0) {
/* 0 means to cast the value back to the "implicit" type */
}
else {
U8_T size = 0;
ObjectInfo * obj = find_object(
expr->object->mCompUnit->mDesc.mSection,
expr->object->mCompUnit->mDesc.mSection->addr +
expr->object->mCompUnit->mDesc.mUnitOffs + type);
if (obj != NULL && obj->mTag == TAG_base_type && get_num_prop(obj, AT_byte_size, &size)) {
switch (obj->u.mFundType) {
case ATE_unsigned:
if (size < 8) {
add(OP_constu);
add_uleb128(((U8_T)1 << (size * 8)) - 1);
add(OP_and);
}
break;
default:
str_exception(ERR_INV_DWARF, "Unsupported type in OP_GNU_convert");
break;
}
break;
}
str_exception(ERR_INV_DWARF, "Invalid reference in OP_GNU_convert");
}
}
break;
default:
if (op >= OP_lo_user) {
str_fmt_exception(ERR_OTHER, "Unsupported DWARF expression op 0x%02x", op);
}
add(op);
expr_pos++;
break;
}
if (buf_pos - op_dst_pos != expr_pos - op_src_pos) {
JumpInfo * i = (JumpInfo *)tmp_alloc_zero(sizeof(JumpInfo));
i->op = op;
i->delta = (I2_T)(buf_pos - op_dst_pos) - (I2_T)(expr_pos - op_src_pos);
i->src_pos = op_src_pos;
i->dst_pos = op_dst_pos;
i->next = jumps;
jumps = i;
}
}
adjust_jumps();
expr = org_expr;
expr_pos = org_expr_pos;
jumps = org_jumps;
}
static void transform_expression(void * args) {
add_expression_list((DWARFExpressionInfo *)args, 0, 0);
}
void dwarf_transform_expression(Context * ctx, int frame, DWARFExpressionInfo * info) {
int error = 0;
/* Save state - some expressions need to make recursive calls to symbols API */
U1_T * org_buf = buf;
size_t org_buf_pos = buf_pos;
size_t org_buf_max = buf_max;
JumpInfo * org_jumps = jumps;
DWARFExpressionInfo * org_expr = expr;
size_t org_expr_pos = expr_pos;
Context * org_expr_ctx = expr_ctx;
int org_expr_frame = expr_frame;
ContextAddress org_expr_code_addr = expr_code_addr;
ContextAddress org_expr_code_size = expr_code_size;
int org_expr_big_endian = expr_big_endian;
/* Initialize state */
buf_pos = 0;
buf_max = info->expr_size * 2;
buf = (U1_T *)tmp_alloc(buf_max);
expr_ctx = ctx;
expr_frame = frame;
expr_code_addr = 0;
expr_code_size = 0;
expr_big_endian = info->object->mCompUnit->mFile->big_endian;
expr = NULL;
jumps = NULL;
/* Run transformation */
if (elf_save_symbols_state(transform_expression, info) < 0) error = errno;
memset(info, 0, sizeof(DWARFExpressionInfo));
info->code_addr = expr_code_addr;
info->code_size = expr_code_size;
info->expr_addr = buf;
info->expr_size = buf_pos;
/* Restore state */
buf = org_buf;
buf_pos = org_buf_pos;
buf_max = org_buf_max;
jumps = org_jumps;
expr = org_expr;
expr_pos = org_expr_pos;
expr_ctx = org_expr_ctx;
expr_frame = org_expr_frame;
expr_code_addr = org_expr_code_addr;
expr_code_size = org_expr_code_size;
expr_big_endian = org_expr_big_endian;
if (error) exception(error);
}
#endif