diff options
author | Anas Nashif <anas.nashif@intel.com> | 2013-01-15 08:32:18 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2013-01-15 08:32:18 -0800 |
commit | 689b9dbb8d7f88ab91e7741932ed000b6e49be9a (patch) | |
tree | 463f5a1df8b2d35644c260e7bf6c8e0a26198af1 /value.c | |
parent | 59749d048d9e452f049f9151735b5256756919c3 (diff) | |
download | ltrace-689b9dbb8d7f88ab91e7741932ed000b6e49be9a.tar.gz ltrace-689b9dbb8d7f88ab91e7741932ed000b6e49be9a.tar.bz2 ltrace-689b9dbb8d7f88ab91e7741932ed000b6e49be9a.zip |
Imported Upstream version 0.7.2upstream/0.7.2
Diffstat (limited to 'value.c')
-rw-r--r-- | value.c | 485 |
1 files changed, 485 insertions, 0 deletions
@@ -0,0 +1,485 @@ +/* + * 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 <string.h> +#include <assert.h> +#include <stdlib.h> + +#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->where == VAL_LOC_COPY) { + assert(val->inferior != NULL); + size_t size = type_sizeof(val->inferior, val->type); + if (size == (size_t)-1) + return -1; + + retp->u.address = malloc(size); + if (retp->u.address == NULL) + return -1; + + 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. */ + typedef char assert__long_enough_long[-(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; +} |