summaryrefslogtreecommitdiff
path: root/value.c
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2013-01-15 08:32:18 -0800
committerAnas Nashif <anas.nashif@intel.com>2013-01-15 08:32:18 -0800
commit689b9dbb8d7f88ab91e7741932ed000b6e49be9a (patch)
tree463f5a1df8b2d35644c260e7bf6c8e0a26198af1 /value.c
parent59749d048d9e452f049f9151735b5256756919c3 (diff)
downloadltrace-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.c485
1 files changed, 485 insertions, 0 deletions
diff --git a/value.c b/value.c
new file mode 100644
index 0000000..f7950da
--- /dev/null
+++ b/value.c
@@ -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;
+}