/* * This file is part of ltrace. * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include "value.h" #include "type.h" #include "common.h" #include "expr.h" #include "backend.h" static void value_common_init(struct value *valp, struct process *inferior, struct value *parent, struct arg_type_info *type, int own_type) { valp->type = type; valp->own_type = own_type; valp->inferior = inferior; memset(&valp->u, 0, sizeof(valp->u)); valp->where = VAL_LOC_NODATA; valp->parent = parent; valp->size = (size_t)-1; } void value_init(struct value *valp, struct process *inferior, struct value *parent, struct arg_type_info *type, int own_type) { assert(inferior != NULL); value_common_init(valp, inferior, parent, type, own_type); } void value_init_detached(struct value *valp, struct value *parent, struct arg_type_info *type, int own_type) { value_common_init(valp, NULL, parent, type, own_type); } void value_set_type(struct value *value, struct arg_type_info *type, int own_type) { if (value->own_type) { type_destroy(value->type); free(value->type); } value->type = type; value->own_type = own_type; } void value_take_type(struct value *value, struct arg_type_info **type, int *own_type) { *type = value->type; *own_type = value->own_type; value->own_type = 0; } void value_release(struct value *val) { if (val == NULL) return; if (val->where == VAL_LOC_COPY) { free(val->u.address); val->where = VAL_LOC_NODATA; } } void value_destroy(struct value *val) { if (val == NULL) return; value_release(val); value_set_type(val, NULL, 0); } unsigned char * value_reserve(struct value *valp, size_t size) { value_release(valp); if (size <= sizeof(valp->u.value)) { valp->where = VAL_LOC_WORD; valp->u.value = 0; } else { valp->where = VAL_LOC_COPY; valp->u.address = calloc(size, 1); if (valp->u.address == 0) return NULL; } return value_get_raw_data(valp); } void value_in_inferior(struct value *valp, arch_addr_t address) { value_release(valp); valp->where = VAL_LOC_INFERIOR; valp->u.address = address; } int value_reify(struct value *val, struct value_dict *arguments) { if (val->where != VAL_LOC_INFERIOR) return 0; assert(val->inferior != NULL); size_t size = value_size(val, arguments); if (size == (size_t)-1) return -1; void *data; enum value_location_t nloc; if (size <= sizeof(val->u.value)) { data = &val->u.value; nloc = VAL_LOC_WORD; } else { data = malloc(size); if (data == NULL) return -1; nloc = VAL_LOC_COPY; } if (umovebytes(val->inferior, val->u.inf_address, data, size) < size) { if (nloc == VAL_LOC_COPY) free(data); return -1; } val->where = nloc; if (nloc == VAL_LOC_COPY) val->u.address = data; return 0; } unsigned char * value_get_data(struct value *val, struct value_dict *arguments) { if (value_reify(val, arguments) < 0) return NULL; return value_get_raw_data(val); } unsigned char * value_get_raw_data(struct value *val) { switch (val->where) { case VAL_LOC_INFERIOR: abort(); case VAL_LOC_NODATA: return NULL; case VAL_LOC_COPY: case VAL_LOC_SHARED: return val->u.address; case VAL_LOC_WORD: return val->u.buf; } assert(!"Unexpected value of val->where"); abort(); } int value_clone(struct value *retp, const struct value *val) { *retp = *val; if (val->own_type) { retp->type = malloc(sizeof(struct arg_type_info)); if (type_clone (retp->type, val->type) < 0) { free(retp->type); return -1; } } if (val->where == VAL_LOC_COPY) { assert(val->inferior != NULL); size_t size = type_sizeof(val->inferior, val->type); if (size == (size_t)-1) { fail: if (retp->own_type) { type_destroy(retp->type); free(retp->type); } return -1; } retp->u.address = malloc(size); if (retp->u.address == NULL) goto fail; memcpy(retp->u.address, val->u.address, size); } return 0; } size_t value_size(struct value *val, struct value_dict *arguments) { if (val->size != (size_t)-1) return val->size; if (val->type->type != ARGTYPE_ARRAY) return val->size = type_sizeof(val->inferior, val->type); struct value length; if (expr_eval(val->type->u.array_info.length, val, arguments, &length) < 0) return (size_t)-1; size_t l; int o = value_extract_word(&length, (long *)&l, arguments); value_destroy(&length); if (o < 0) return (size_t)-1; size_t elt_size = type_sizeof(val->inferior, val->type->u.array_info.elt_type); if (elt_size == (size_t)-1) return (size_t)-1; return val->size = elt_size * l; } int value_init_element(struct value *ret_val, struct value *val, size_t element) { size_t off = type_offsetof(val->inferior, val->type, element); if (off == (size_t)-1) return -1; struct arg_type_info *e_info = type_element(val->type, element); if (e_info == NULL) return -1; value_common_init(ret_val, val->inferior, val, e_info, 0); switch (val->where) { case VAL_LOC_COPY: case VAL_LOC_SHARED: ret_val->u.address = val->u.address + off; ret_val->where = VAL_LOC_SHARED; return 0; case VAL_LOC_WORD: ret_val->u.address = value_get_raw_data(val) + off; ret_val->where = VAL_LOC_SHARED; return 0; case VAL_LOC_INFERIOR: ret_val->u.inf_address = val->u.inf_address + off; ret_val->where = VAL_LOC_INFERIOR; return 0; case VAL_LOC_NODATA: assert(!"Can't offset NODATA."); abort(); } abort(); } int value_init_deref(struct value *ret_val, struct value *valp) { assert(valp->type->type == ARGTYPE_POINTER); /* Note: extracting a pointer value should not need value_dict * with function arguments. */ long l; if (value_extract_word(valp, &l, NULL) < 0) return -1; /* We need "long" to be long enough to hold platform * pointers. */ (void)sizeof(char[1 - 2*(sizeof(l) < sizeof(void *))]); value_common_init(ret_val, valp->inferior, valp, valp->type->u.ptr_info.info, 0); ret_val->u.value = l; /* Set the address. */ ret_val->where = VAL_LOC_INFERIOR; return 0; } /* The functions value_extract_buf and value_extract_word assume that * data in VALUE is stored at the start of the internal buffer. For * value_extract_buf in particular there's no other reasonable * default. If we need to copy out four bytes, they need to be the * bytes pointed to by the buffer pointer. * * But actually the situation is similar for value_extract_word as * well. This function is used e.g. to extract characters from * strings. Those weren't stored by value_set_word, they might still * be in client for all we know. So value_extract_word has to assume * that the whole of data is data is stored at the buffer pointer. * * This is a problem on big endian machines, where 2-byte quantity * carried in 4- or 8-byte long is stored at the end of that long. * (Though that quantity itself is still big endian.) So we need to * make a little dance to shift the value to the right part of the * buffer. */ union word_data { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; long l; unsigned char buf[0]; } u; void value_set_word(struct value *value, long word) { size_t sz = type_sizeof(value->inferior, value->type); assert(sz != (size_t)-1); assert(sz <= sizeof(value->u.value)); value->where = VAL_LOC_WORD; union word_data u = {}; switch (sz) { case 0: u.l = 0; break; case 1: u.u8 = word; break; case 2: u.u16 = word; break; case 4: u.u32 = word; break; case 8: u.u64 = word; break; default: assert(sz != sz); abort(); } value->u.value = u.l; } static int value_extract_buf_sz(struct value *value, unsigned char *tgt, size_t sz, struct value_dict *arguments) { unsigned char *data = value_get_data(value, arguments); if (data == NULL) return -1; memcpy(tgt, data, sz); return 0; } int value_extract_word(struct value *value, long *retp, struct value_dict *arguments) { size_t sz = type_sizeof(value->inferior, value->type); if (sz == (size_t)-1) return -1; assert(sz <= sizeof(value->u.value)); if (sz == 0) { *retp = 0; return 0; } union word_data u = {}; if (value_extract_buf_sz(value, u.buf, sz, arguments) < 0) return -1; switch (sz) { case 1: *retp = (long)u.u8; return 0; case 2: *retp = (long)u.u16; return 0; case 4: *retp = (long)u.u32; return 0; case 8: *retp = (long)u.u64; return 0; default: assert(sz != sz); abort(); } } int value_extract_buf(struct value *value, unsigned char *tgt, struct value_dict *arguments) { size_t sz = type_sizeof(value->inferior, value->type); if (sz == (size_t)-1) return -1; return value_extract_buf_sz(value, tgt, sz, arguments); } struct value * value_get_parental_struct(struct value *val) { struct value *parent; for (parent = val->parent; parent != NULL; parent = parent->parent) if (parent->type->type == ARGTYPE_STRUCT) return parent; return NULL; } int value_is_zero(struct value *val, struct value_dict *arguments) { unsigned char *data = value_get_data(val, arguments); if (data == NULL) return -1; size_t sz = type_sizeof(val->inferior, val->type); if (sz == (size_t)-1) return -1; int zero = 1; size_t j; for (j = 0; j < sz; ++j) { if (data[j] != 0) { zero = 0; break; } } return zero; } int value_equal(struct value *val1, struct value *val2, struct value_dict *arguments) { size_t sz1 = type_sizeof(val1->inferior, val1->type); size_t sz2 = type_sizeof(val2->inferior, val2->type); if (sz1 == (size_t)-1 || sz2 == (size_t)-1) return -1; if (sz1 != sz2) return 0; unsigned char *data1 = value_get_data(val1, arguments); unsigned char *data2 = value_get_data(val2, arguments); if (data1 == NULL || data2 == NULL) return -1; return memcmp(data1, data2, sz1) == 0 ? 1 : 0; } int value_pass_by_reference(struct value *value) { assert(value != NULL); assert(value->type->type == ARGTYPE_STRUCT); struct arg_type_info *new_info = calloc(sizeof(*new_info), 1); if (new_info == NULL) return -1; int own; struct arg_type_info *orig; value_take_type(value, &orig, &own); type_init_pointer(new_info, orig, own); new_info->lens = orig->lens; value_set_type(value, new_info, 1); return 0; }