Add type.c/.h, value.c/.h, value_dict.c/.h
- none of this is plugged in yet
- expr.c/.h is not in yet, so we still express array length with len_spec
integer. Some function are mocked out so the thing compiles
diff --git a/ChangeLog b/ChangeLog
index 0dffe52..51b9f9c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
2012-04-30 Petr Machata <pmachata@redhat.com>
+ * type.c, type.h: New module with routines related to handling of
+ types. Some of this was moved over from common.h.
+ * common.h: Adjust for this change.
+ * read_config_file.c: Likewise.
+ * display_args.c: Likewise.
+ * output.c: Likewise.
+ * sysdeps/linux-gnu/*/trace.c: Likewise.
+ * value.c, value.h: New module for values of arbitrary length.
+ * value_dict.c, value_dict.h: New module for value dictionaries.
+
+2012-04-30 Petr Machata <pmachata@redhat.com>
+
* library.h (arch_translate_address): First argument is ltelf.
(arch_translate_address_dyn): New function.
* sysdeps/linux-gnu/ppc/plt.c: Implement above, use .opd to
diff --git a/Makefile.am b/Makefile.am
index 0e91f71..ad93ef6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,7 +30,10 @@
library.c \
filter.c \
glob.c \
- vect.c
+ vect.c \
+ type.c \
+ value.c \
+ value_dict.c
libltrace_la_LIBADD = \
$(libelf_LIBS) \
@@ -67,7 +70,10 @@
library.h \
filter.h \
glob.h \
- vect.h
+ vect.h \
+ type.h \
+ value.h \
+ value_dict.h
dist_man1_MANS = \
ltrace.1
diff --git a/common.h b/common.h
index 621aa13..9db7e83 100644
--- a/common.h
+++ b/common.h
@@ -15,6 +15,7 @@
#include "ltrace-elf.h"
#include "read_config_file.h"
#include "proc.h"
+#include "forward.h"
#if defined HAVE_LIBSUPC__ || defined HAVE_LIBSTDC__
# define USE_CXA_DEMANGLE
@@ -27,77 +28,6 @@
extern int exiting; /* =1 if we have to exit ASAP */
-enum arg_type {
- ARGTYPE_UNKNOWN = -1,
- ARGTYPE_VOID,
- ARGTYPE_INT,
- ARGTYPE_UINT,
- ARGTYPE_LONG,
- ARGTYPE_ULONG,
- ARGTYPE_OCTAL,
- ARGTYPE_CHAR,
- ARGTYPE_SHORT,
- ARGTYPE_USHORT,
- ARGTYPE_FLOAT, /* float value, may require index */
- ARGTYPE_DOUBLE, /* double value, may require index */
- ARGTYPE_ADDR,
- ARGTYPE_FILE,
- ARGTYPE_FORMAT, /* printf-like format */
- ARGTYPE_STRING, /* NUL-terminated string */
- ARGTYPE_STRING_N, /* String of known maxlen */
- ARGTYPE_ARRAY, /* Series of values in memory */
- ARGTYPE_ENUM, /* Enumeration */
- ARGTYPE_STRUCT, /* Structure of values */
- ARGTYPE_POINTER, /* Pointer to some other type */
- ARGTYPE_COUNT /* number of ARGTYPE_* values */
-};
-
-typedef struct arg_type_info_t {
- enum arg_type type;
- union {
- /* ARGTYPE_ENUM */
- struct {
- size_t entries;
- char ** keys;
- int * values;
- } enum_info;
-
- /* ARGTYPE_ARRAY */
- struct {
- struct arg_type_info_t * elt_type;
- size_t elt_size;
- int len_spec;
- } array_info;
-
- /* ARGTYPE_STRING_N */
- struct {
- int size_spec;
- } string_n_info;
-
- /* ARGTYPE_STRUCT */
- struct {
- struct arg_type_info_t ** fields; /* NULL-terminated */
- size_t * offset;
- size_t size;
- } struct_info;
-
- /* ARGTYPE_POINTER */
- struct {
- struct arg_type_info_t * info;
- } ptr_info;
-
- /* ARGTYPE_FLOAT */
- struct {
- size_t float_index;
- } float_info;
-
- /* ARGTYPE_DOUBLE */
- struct {
- size_t float_index;
- } double_info;
- } u;
-} arg_type_info;
-
enum tof {
LT_TOF_NONE = 0,
LT_TOF_FUNCTION, /* A real library function */
@@ -110,9 +40,9 @@
typedef struct Function Function;
struct Function {
const char * name;
- arg_type_info * return_info;
+ struct arg_type_info *return_info;
int num_params;
- arg_type_info * arg_info[MAX_ARGS];
+ struct arg_type_info *arg_info[MAX_ARGS];
int params_right;
Function * next;
};
@@ -157,11 +87,10 @@
extern void handle_event(Event * event);
extern pid_t execute_program(const char * command, char ** argv);
-extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
+extern int display_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info);
extern void disable_all_breakpoints(Process * proc);
extern void show_summary(void);
-extern arg_type_info * lookup_prototype(enum arg_type at);
struct breakpoint;
struct library_symbol;
@@ -193,10 +122,12 @@
extern void continue_after_syscall(Process *proc, int sysnum, int ret_p);
extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp);
extern void continue_after_vfork(Process * proc);
-extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
+extern long gimme_arg(enum tof type, Process *proc, int arg_num,
+ struct arg_type_info *info);
extern void save_register_args(enum tof type, Process * proc);
extern int umovestr(Process * proc, void * addr, int len, void * laddr);
-extern int umovelong (Process * proc, void * addr, long * result, arg_type_info * info);
+extern int umovelong(Process *proc, void *addr, long *result,
+ struct arg_type_info *info);
extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count);
extern int ffcheck(void * maddr);
extern void * sym2addr(Process *, struct library_symbol *);
diff --git a/display_args.c b/display_args.c
index 5df34ca..872b1c9 100644
--- a/display_args.c
+++ b/display_args.c
@@ -6,13 +6,14 @@
#include "common.h"
#include "proc.h"
+#include "type.h"
static int display_char(int what);
static int display_string(enum tof type, Process *proc,
void* addr, size_t maxlen);
static int display_value(enum tof type, Process *proc,
- long value, arg_type_info *info,
- void *st, arg_type_info* st_info);
+ long value, struct arg_type_info *info,
+ void *st, struct arg_type_info *st_info);
static int display_unknown(enum tof type, Process *proc, long value);
static int display_format(enum tof type, Process *proc, int arg_num);
@@ -21,15 +22,16 @@
static long
get_length(enum tof type, Process *proc, int len_spec,
- void *st, arg_type_info* st_info) {
+ void *st, struct arg_type_info *st_info)
+{
long len;
- arg_type_info info;
+ struct arg_type_info info;
if (len_spec > 0)
return len_spec;
if (type == LT_TOF_STRUCT) {
- umovelong (proc, st + st_info->u.struct_info.offset[-len_spec-1],
- &len, st_info->u.struct_info.fields[-len_spec-1]);
+ umovelong(proc, st + st_info->u.struct_info.offset[-len_spec-1],
+ &len, st_info->u.struct_info.fields[-len_spec-1]);
return len;
}
@@ -39,9 +41,9 @@
static int
display_ptrto(enum tof type, Process *proc, long item,
- arg_type_info * info,
- void *st, arg_type_info* st_info) {
- arg_type_info temp;
+ struct arg_type_info * info,
+ void *st, struct arg_type_info *st_info) {
+ struct arg_type_info temp;
temp.type = ARGTYPE_POINTER;
temp.u.ptr_info.info = info;
return display_value(type, proc, item, &temp, st, st_info);
@@ -57,8 +59,8 @@
*/
static int
display_arrayptr(enum tof type, Process *proc,
- void *addr, arg_type_info * info,
- void *st, arg_type_info* st_info) {
+ void *addr, struct arg_type_info * info,
+ void *st, struct arg_type_info *st_info) {
int len = 0;
size_t i;
size_t array_len;
@@ -70,7 +72,7 @@
st, st_info);
len += fprintf(options.output, "[ ");
for (i = 0; i < options.arraylen && i < array_maxlength && i < array_len; i++) {
- arg_type_info *elt_type = info->u.array_info.elt_type;
+ struct arg_type_info *elt_type = info->u.array_info.elt_type;
size_t elt_size = info->u.array_info.elt_size;
if (i != 0)
len += fprintf(options.output, ", ");
@@ -91,9 +93,9 @@
*/
static int
display_structptr(enum tof type, Process *proc,
- void *addr, arg_type_info * info) {
+ void *addr, struct arg_type_info * info) {
int i;
- arg_type_info *field;
+ struct arg_type_info *field;
int len = 0;
if (addr == NULL)
@@ -119,10 +121,10 @@
static int
display_pointer(enum tof type, Process *proc, long value,
- arg_type_info * info,
- void *st, arg_type_info* st_info) {
+ struct arg_type_info * info,
+ void *st, struct arg_type_info *st_info) {
long pointed_to;
- arg_type_info *inner = info->u.ptr_info.info;
+ struct arg_type_info *inner = info->u.ptr_info.info;
if (inner->type == ARGTYPE_ARRAY) {
return display_arrayptr(type, proc, (void*) value, inner,
@@ -143,7 +145,7 @@
static int
display_enum(enum tof type, Process *proc,
- arg_type_info* info, long value) {
+ struct arg_type_info *info, long value) {
size_t ii;
for (ii = 0; ii < info->u.enum_info.entries; ++ii) {
if (info->u.enum_info.values[ii] == value)
@@ -166,8 +168,8 @@
*/
int
display_value(enum tof type, Process *proc,
- long value, arg_type_info *info,
- void *st, arg_type_info* st_info) {
+ long value, struct arg_type_info *info,
+ void *st, struct arg_type_info *st_info) {
int tmp;
switch (info->type) {
@@ -239,7 +241,9 @@
}
int
-display_arg(enum tof type, Process *proc, int arg_num, arg_type_info * info) {
+display_arg(enum tof type, Process *proc, int arg_num,
+ struct arg_type_info * info)
+{
long arg;
if (info->type == ARGTYPE_VOID) {
@@ -330,7 +334,7 @@
unsigned char *str1;
int i;
size_t len = 0;
- arg_type_info info;
+ struct arg_type_info info;
info.type = ARGTYPE_POINTER;
addr = (void *)gimme_arg(type, proc, arg_num, &info);
diff --git a/output.c b/output.c
index db6e93e..265993c 100644
--- a/output.c
+++ b/output.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "proc.h"
#include "library.h"
+#include "type.h"
/* TODO FIXME XXX: include in common.h: */
extern struct timeval current_time_spent;
@@ -161,7 +162,7 @@
{
const char *function_name = libsym->name;
Function *func;
- static arg_type_info *arg_unknown = NULL;
+ static struct arg_type_info *arg_unknown = NULL;
if (arg_unknown == NULL)
arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
@@ -225,7 +226,7 @@
{
const char *function_name = libsym->name;
Function *func = name2func(function_name);
- static arg_type_info *arg_unknown = NULL;
+ static struct arg_type_info *arg_unknown = NULL;
if (arg_unknown == NULL)
arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
diff --git a/read_config_file.c b/read_config_file.c
index b4b1b56..af19512 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -5,12 +5,13 @@
#include <ctype.h>
#include "common.h"
+#include "type.h"
static int line_no;
static char *filename;
static int error_count = 0;
-static arg_type_info *parse_type(char **str);
+static struct arg_type_info *parse_type(char **str);
Function *list_of_functions = NULL;
@@ -45,7 +46,7 @@
/* Array of prototype objects for each of the types. The order in this
* array must exactly match the list of enumerated values in
* common.h */
-static arg_type_info arg_type_prototypes[] = {
+static struct arg_type_info arg_type_prototypes[] = {
{ ARGTYPE_VOID },
{ ARGTYPE_INT },
{ ARGTYPE_UINT },
@@ -69,7 +70,7 @@
{ ARGTYPE_UNKNOWN }
};
-arg_type_info *
+struct arg_type_info *
lookup_prototype(enum arg_type at) {
if (at >= 0 && at <= ARGTYPE_COUNT)
return &arg_type_prototypes[at];
@@ -77,7 +78,7 @@
return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */
}
-static arg_type_info *
+static struct arg_type_info *
str2type(char **str) {
struct list_of_pt_t *tmp = &list_of_pt[0];
@@ -208,11 +209,11 @@
struct typedef_node_t {
char *name;
- arg_type_info *info;
+ struct arg_type_info *info;
struct typedef_node_t *next;
} *typedefs = NULL;
-static arg_type_info *
+static struct arg_type_info *
lookup_typedef(char **str) {
struct typedef_node_t *node;
char *end = *str;
@@ -234,7 +235,7 @@
static void
parse_typedef(char **str) {
char *name;
- arg_type_info *info;
+ struct arg_type_info *info;
struct typedef_node_t *binding;
(*str) += strlen("typedef");
@@ -267,7 +268,7 @@
}
static size_t
-arg_sizeof(arg_type_info * arg) {
+arg_sizeof(struct arg_type_info * arg) {
if (arg->type == ARGTYPE_CHAR) {
return sizeof(char);
} else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) {
@@ -295,7 +296,7 @@
#undef alignof
#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
static size_t
-arg_align(arg_type_info * arg) {
+arg_align(struct arg_type_info * arg) {
struct { char c; char C; } cC;
struct { char c; short s; } cs;
struct { char c; int i; } ci;
@@ -355,7 +356,7 @@
/* I'm sure this isn't completely correct, but just try to get most of
* them right for now. */
static void
-align_struct(arg_type_info* info) {
+align_struct(struct arg_type_info* info) {
size_t offset;
int i;
@@ -366,7 +367,7 @@
// various types.
offset = 0;
for (i = 0; info->u.struct_info.fields[i] != NULL; i++) {
- arg_type_info *field = info->u.struct_info.fields[i];
+ struct arg_type_info *field = info->u.struct_info.fields[i];
offset += align_skip(arg_align(field), offset);
info->u.struct_info.offset[i] = offset;
offset += arg_sizeof(field);
@@ -375,10 +376,10 @@
info->u.struct_info.size = offset;
}
-static arg_type_info *
+static struct arg_type_info *
parse_nonpointer_type(char **str) {
- arg_type_info *simple;
- arg_type_info *info;
+ struct arg_type_info *simple;
+ struct arg_type_info *info;
if (strncmp(*str, "typedef", 7) == 0) {
parse_typedef(str);
@@ -556,11 +557,11 @@
}
}
-static arg_type_info *
+static struct arg_type_info *
parse_type(char **str) {
- arg_type_info *info = parse_nonpointer_type(str);
+ struct arg_type_info *info = parse_nonpointer_type(str);
while (**str == '*') {
- arg_type_info *outer = malloc(sizeof(*info));
+ struct arg_type_info *outer = malloc(sizeof(*info));
outer->type = ARGTYPE_POINTER;
outer->u.ptr_info.info = info;
(*str)++;
diff --git a/sysdeps/linux-gnu/alpha/trace.c b/sysdeps/linux-gnu/alpha/trace.c
index 18fe395..3c15664 100644
--- a/sysdeps/linux-gnu/alpha/trace.c
+++ b/sysdeps/linux-gnu/alpha/trace.c
@@ -48,7 +48,8 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
if (arg_num == -1) { /* return value */
return ptrace(PTRACE_PEEKUSER, proc->pid, 0 /* REG_R0 */ , 0);
}
diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c
index 768e1a8..8cf5560 100644
--- a/sysdeps/linux-gnu/arm/trace.c
+++ b/sysdeps/linux-gnu/arm/trace.c
@@ -82,7 +82,8 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
proc_archdep *a = (proc_archdep *) proc->arch_ptr;
if (arg_num == -1) { /* return value */
diff --git a/sysdeps/linux-gnu/cris/trace.c b/sysdeps/linux-gnu/cris/trace.c
index b2b1ba8..2e7c586 100644
--- a/sysdeps/linux-gnu/cris/trace.c
+++ b/sysdeps/linux-gnu/cris/trace.c
@@ -51,7 +51,8 @@
return 0;
}
-long gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info)
+long gimme_arg(enum tof type, Process *proc, int arg_num,
+ struct arg_type_info *info)
{
int pid = proc->pid;
diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c
index f0c1e50..d024bc9 100644
--- a/sysdeps/linux-gnu/i386/trace.c
+++ b/sysdeps/linux-gnu/i386/trace.c
@@ -57,7 +57,8 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
if (arg_num == -1) { /* return value */
return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EAX, 0);
}
diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c
index 385fac1..0ef1e60 100644
--- a/sysdeps/linux-gnu/ia64/trace.c
+++ b/sysdeps/linux-gnu/ia64/trace.c
@@ -13,6 +13,7 @@
#include "proc.h"
#include "common.h"
+#include "type.h"
/* What we think of as a bundle, ptrace thinks of it as two unsigned
* longs */
@@ -245,7 +246,8 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
union {
long l;
float f;
diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c
index c63702d..b15e47d 100644
--- a/sysdeps/linux-gnu/m68k/trace.c
+++ b/sysdeps/linux-gnu/m68k/trace.c
@@ -47,7 +47,8 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
if (arg_num == -1) { /* return value */
return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D0, 0);
}
diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c
index 4b999e4..2c509d9 100644
--- a/sysdeps/linux-gnu/mipsel/trace.c
+++ b/sysdeps/linux-gnu/mipsel/trace.c
@@ -9,6 +9,7 @@
#include "proc.h"
#include "common.h"
#include "mipsel.h"
+#include "type.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
#endif
@@ -118,7 +119,8 @@
*/
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
long ret;
long addr;
debug(2,"type %d arg %d",type,arg_num);
diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c
index 742785a..18eb92e 100644
--- a/sysdeps/linux-gnu/ppc/trace.c
+++ b/sysdeps/linux-gnu/ppc/trace.c
@@ -13,6 +13,7 @@
#include "common.h"
#include "ptrace.h"
#include "breakpoint.h"
+#include "type.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
@@ -67,7 +68,8 @@
}
static long
-gimme_arg_regset(enum tof type, Process *proc, int arg_num, arg_type_info *info,
+gimme_arg_regset(enum tof type, Process *proc, int arg_num,
+ struct arg_type_info *info,
gregset_t *regs, fpregset_t *fpregs)
{
union { long val; float fval; double dval; } cvt;
@@ -110,7 +112,7 @@
}
static long
-gimme_retval(Process *proc, int arg_num, arg_type_info *info,
+gimme_retval(Process *proc, int arg_num, struct arg_type_info *info,
gregset_t *regs, fpregset_t *fpregs)
{
union { long val; float fval; double dval; } cvt;
@@ -130,7 +132,7 @@
/* Grab functions arguments based on the PPC64 ABI. */
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info)
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
{
proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
if (arch == NULL || !arch->valid)
@@ -173,7 +175,9 @@
/* Read a single long from the process's memory address 'addr'. */
int
-arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
+arch_umovelong (Process *proc, void *addr, long *result,
+ struct arg_type_info *info)
+{
long pointed_to;
errno = 0;
diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c
index 8c08f1f..6660b18 100644
--- a/sysdeps/linux-gnu/s390/trace.c
+++ b/sysdeps/linux-gnu/s390/trace.c
@@ -161,7 +161,8 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
long ret;
switch (arg_num) {
diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c
index e05c4d3..04efdf2 100644
--- a/sysdeps/linux-gnu/sparc/trace.c
+++ b/sysdeps/linux-gnu/sparc/trace.c
@@ -45,7 +45,8 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
proc_archdep *a = (proc_archdep *) proc->arch_ptr;
if (!a->valid) {
fprintf(stderr, "Could not get child registers\n");
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index bd5d826..f573f22 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -19,6 +19,7 @@
#include "breakpoint.h"
#include "proc.h"
#include "linux-gnu/trace.h"
+#include "type.h"
/* If the system headers did not provide the constants, hard-code the normal
values. */
@@ -50,13 +51,14 @@
#ifdef ARCH_HAVE_UMOVELONG
extern int arch_umovelong (Process *, void *, long *, arg_type_info *);
int
-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
+umovelong(Process *proc, void *addr, long *result, arg_type_info *info) {
return arch_umovelong (proc, addr, result, info);
}
#else
/* Read a single long from the process's memory address 'addr' */
int
-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
+umovelong(Process *proc, void *addr, long *result, struct arg_type_info *info)
+{
long pointed_to;
errno = 0;
@@ -66,7 +68,7 @@
*result = pointed_to;
if (info) {
- switch(info->type) {
+ switch (info->type) {
case ARGTYPE_INT:
*result &= 0x00000000ffffffffUL;
default:
diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c
index 0d3f693..ec53bfc 100644
--- a/sysdeps/linux-gnu/x86_64/trace.c
+++ b/sysdeps/linux-gnu/x86_64/trace.c
@@ -13,6 +13,7 @@
#include "common.h"
#include "ptrace.h"
#include "proc.h"
+#include "type.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
@@ -112,7 +113,7 @@
}
static long
-gimme_arg_regset(Process *proc, int arg_num, arg_type_info *info,
+gimme_arg_regset(Process *proc, int arg_num, struct arg_type_info *info,
struct user_regs_struct *regs,
struct user_fpregs_struct *fpregs)
{
@@ -148,7 +149,7 @@
}
}
static long
-gimme_retval(Process *proc, int arg_num, arg_type_info *info,
+gimme_retval(Process *proc, int arg_num, struct arg_type_info *info,
struct user_regs_struct *regs, struct user_fpregs_struct *fpregs)
{
if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE)
@@ -158,7 +159,9 @@
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num,
+ struct arg_type_info *info)
+{
if (proc->mask_32bit)
return (unsigned int)gimme_arg32(type, proc, arg_num);
diff --git a/type.c b/type.c
new file mode 100644
index 0000000..08d37e2
--- /dev/null
+++ b/type.c
@@ -0,0 +1,557 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2007,2008 Juan Cespedes
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+
+#include "type.h"
+#include "sysdep.h"
+
+static int
+expr_is_compile_constant(int len_spec)
+{
+ return len_spec > 0;
+}
+
+static int
+expr_eval_constant(int len_spec, long *result)
+{
+ if (len_spec <= 0)
+ return -1;
+ *result = len_spec;
+ return 0;
+}
+
+struct arg_type_info *
+type_get_simple(enum arg_type type)
+{
+#define HANDLE(T) { \
+ static struct arg_type_info t = { T }; \
+ case T: \
+ return &t; \
+ }
+
+ switch (type) {
+ HANDLE(ARGTYPE_UNKNOWN)
+ HANDLE(ARGTYPE_VOID)
+ HANDLE(ARGTYPE_INT)
+ HANDLE(ARGTYPE_UINT)
+ HANDLE(ARGTYPE_LONG)
+ HANDLE(ARGTYPE_ULONG)
+ HANDLE(ARGTYPE_OCTAL)
+ HANDLE(ARGTYPE_CHAR)
+ HANDLE(ARGTYPE_SHORT)
+ HANDLE(ARGTYPE_USHORT)
+ HANDLE(ARGTYPE_FLOAT)
+ HANDLE(ARGTYPE_DOUBLE)
+
+ HANDLE(ARGTYPE_ADDR)
+ HANDLE(ARGTYPE_FILE)
+ HANDLE(ARGTYPE_FORMAT)
+
+#undef HANDLE
+
+ case ARGTYPE_STRING:
+ case ARGTYPE_STRING_N:
+ case ARGTYPE_COUNT:
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_ENUM:
+ case ARGTYPE_STRUCT:
+ case ARGTYPE_POINTER:
+ assert(!"Not a simple type!");
+ };
+ abort();
+}
+
+struct enum_entry {
+ char *key;
+ int own_key;
+ int value;
+};
+
+void
+type_init_enum(struct arg_type_info *info)
+{
+ info->type = ARGTYPE_ENUM;
+ VECT_INIT(&info->u.entries, struct enum_entry);
+}
+
+int
+type_enum_add(struct arg_type_info *info,
+ const char *key, int own_key, int value)
+{
+ assert(info->type == ARGTYPE_ENUM);
+ struct enum_entry entry = { (char *)key, own_key, value };
+ return VECT_PUSHBACK(&info->u.entries, &entry);
+}
+
+size_t
+type_enum_size(struct arg_type_info *info)
+{
+ assert(info->type == ARGTYPE_ENUM);
+ return vect_size(&info->u.entries);
+}
+
+const char *
+type_enum_get(struct arg_type_info *info, int value)
+{
+ assert(info->type == ARGTYPE_ENUM);
+ size_t i;
+ for (i = 0; i < vect_size(&info->u.entries); ++i) {
+ struct enum_entry *entry = VECT_ELEMENT(&info->u.entries,
+ struct enum_entry, i);
+ if (value == entry->value)
+ return entry->key;
+ }
+ return NULL;
+}
+
+static void
+enum_entry_dtor(struct enum_entry *entry, void *data)
+{
+ if (entry->own_key)
+ free(entry->key);
+}
+
+static void
+type_enum_destroy(struct arg_type_info *info)
+{
+ VECT_DESTROY(&info->u.entries, struct enum_entry,
+ enum_entry_dtor, NULL);
+}
+
+struct struct_field {
+ struct arg_type_info *info;
+ int own_info;
+};
+
+void
+type_init_struct(struct arg_type_info *info)
+{
+ info->type = ARGTYPE_STRUCT;
+ VECT_INIT(&info->u.entries, struct struct_field);
+}
+
+int
+type_struct_add(struct arg_type_info *info,
+ struct arg_type_info *field_info, int own)
+{
+ assert(info->type == ARGTYPE_STRUCT);
+ struct struct_field field = { field_info, own };
+ return VECT_PUSHBACK(&info->u.entries, &field);
+}
+
+struct arg_type_info *
+type_struct_get(struct arg_type_info *info, size_t idx)
+{
+ assert(info->type == ARGTYPE_STRUCT);
+ struct struct_field *field = VECT_ELEMENT(&info->u.entries,
+ struct struct_field, idx);
+ if (field == NULL)
+ return NULL;
+ return field->info;
+}
+
+size_t
+type_struct_size(struct arg_type_info *info)
+{
+ assert(info->type == ARGTYPE_STRUCT);
+ return vect_size(&info->u.entries);
+}
+
+static void
+struct_field_dtor(struct struct_field *field, void *data)
+{
+ if (field->own_info) {
+ type_destroy(field->info);
+ free(field->info);
+ }
+}
+
+static void
+type_struct_destroy(struct arg_type_info *info)
+{
+ VECT_DESTROY(&info->u.entries, struct struct_field,
+ struct_field_dtor, NULL);
+}
+
+static int
+layout_struct(struct Process *proc, struct arg_type_info *info,
+ size_t *sizep, size_t *alignmentp, size_t *offsetofp)
+{
+ size_t sz = 0;
+ size_t max_alignment = 0;
+ size_t i;
+ size_t offsetof_field = (size_t)-1;
+ if (offsetofp != NULL)
+ offsetof_field = *offsetofp;
+
+ assert(info->type == ARGTYPE_STRUCT);
+ for (i = 0; i < vect_size(&info->u.entries); ++i) {
+ struct struct_field *field
+ = VECT_ELEMENT(&info->u.entries,
+ struct struct_field, i);
+
+ size_t alignment = type_alignof(proc, field->info);
+ if (alignment == (size_t)-1)
+ return -1;
+
+ /* Add padding to SZ to align the next element. */
+ sz = align(sz, alignment);
+ if (i == offsetof_field) {
+ *offsetofp = sz;
+ if (sizep == NULL && alignmentp == NULL)
+ return 0;
+ }
+
+ size_t size = type_sizeof(proc, field->info);
+ if (size == (size_t)-1)
+ return -1;
+ sz += size;
+
+ if (alignment > max_alignment)
+ max_alignment = alignment;
+ }
+
+ if (max_alignment > 0)
+ sz = align(sz, max_alignment);
+
+ if (sizep != NULL)
+ *sizep = sz;
+
+ if (alignmentp != NULL)
+ *alignmentp = max_alignment;
+
+ return 0;
+}
+
+void
+type_init_array(struct arg_type_info *info,
+ struct arg_type_info *element_info, int own_info,
+ int len_spec)
+{
+ info->type = ARGTYPE_ARRAY;
+ info->u.array_info.elt_type = element_info;
+ info->u.array_info.own_info = own_info;
+ info->u.array_info.len_spec = len_spec;
+}
+
+static void
+type_array_destroy(struct arg_type_info *info)
+{
+ if (info->u.array_info.own_info) {
+ type_destroy(info->u.array_info.elt_type);
+ free(info->u.array_info.elt_type);
+ }
+ /*
+ if (info->u.array_info.own_length) {
+ expr_destroy(info->u.array_info.length);
+ free(info->u.array_info.length);
+ }
+ */
+}
+
+void
+type_init_pointer(struct arg_type_info *info,
+ struct arg_type_info *pointee_info, int own_info)
+{
+ info->type = ARGTYPE_POINTER;
+ info->u.ptr_info.info = pointee_info;
+ info->u.ptr_info.own_info = own_info;
+}
+
+static void
+type_pointer_destroy(struct arg_type_info *info)
+{
+ if (info->u.ptr_info.own_info) {
+ type_destroy(info->u.ptr_info.info);
+ free(info->u.ptr_info.info);
+ }
+}
+
+void
+type_destroy(struct arg_type_info *info)
+{
+ if (info == NULL)
+ return;
+
+ switch (info->type) {
+ case ARGTYPE_ENUM:
+ return type_enum_destroy(info);
+
+ case ARGTYPE_STRUCT:
+ type_struct_destroy(info);
+ break;
+
+ case ARGTYPE_ARRAY:
+ type_array_destroy(info);
+ break;
+
+ case ARGTYPE_POINTER:
+ type_pointer_destroy(info);
+ break;
+
+ case ARGTYPE_UNKNOWN:
+ case ARGTYPE_VOID:
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_OCTAL:
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ break;
+
+ case ARGTYPE_ADDR:
+ case ARGTYPE_FILE:
+ case ARGTYPE_FORMAT:
+ case ARGTYPE_STRING:
+ case ARGTYPE_STRING_N:
+ case ARGTYPE_COUNT:
+ break;
+ }
+}
+
+#ifdef ARCH_HAVE_SIZEOF
+size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg);
+#else
+size_t
+arch_type_sizeof(struct Process *proc, struct arg_type_info * arg)
+{
+ /* Use default value. */
+ return (size_t)-2;
+}
+#endif
+
+#ifdef ARCH_HAVE_ALIGNOF
+size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg);
+#else
+size_t
+arch_type_alignof(struct Process *proc, struct arg_type_info * arg)
+{
+ /* Use default value. */
+ return (size_t)-2;
+}
+#endif
+
+/* We need to support alignments that are not power of two. E.g. long
+ * double on x86 has alignment of 12. */
+size_t
+align(size_t sz, size_t alignment)
+{
+ assert(alignment != 0);
+
+ if ((sz % alignment) != 0)
+ sz = ((sz / alignment) + 1) * alignment;
+
+ return sz;
+}
+
+size_t
+type_sizeof(struct Process *proc, struct arg_type_info *type)
+{
+ size_t arch_size = arch_type_sizeof(proc, type);
+ if (arch_size != (size_t)-2)
+ return arch_size;
+
+ switch (type->type) {
+ size_t size;
+ case ARGTYPE_CHAR:
+ return sizeof(char);
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return sizeof(short);
+
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_ENUM:
+ return sizeof(int);
+
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ return sizeof(long);
+
+ case ARGTYPE_FLOAT:
+ return sizeof(float);
+
+ case ARGTYPE_DOUBLE:
+ return sizeof(double);
+
+ case ARGTYPE_STRUCT:
+ if (layout_struct(proc, type, &size, NULL, NULL) < 0)
+ return (size_t)-1;
+ return size;
+
+ case ARGTYPE_POINTER:
+ return sizeof(void *);
+
+ case ARGTYPE_ARRAY:
+ if (expr_is_compile_constant(type->u.array_info.len_spec)) {
+ long l;
+ if (expr_eval_constant(type->u.array_info.len_spec,
+ &l) < 0)
+ return -1;
+
+ struct arg_type_info *elt_ti
+ = type->u.array_info.elt_type;
+
+ size_t elt_size = type_sizeof(proc, elt_ti);
+ if (elt_size == (size_t)-1)
+ return (size_t)-1;
+
+ return ((size_t)l) * elt_size;
+
+ } else {
+ /* Flexible arrays don't count into the
+ * sizeof. */
+ return 0;
+ }
+
+ case ARGTYPE_VOID:
+ return 0;
+
+ /* XXX these are in fact formatting conventions, not
+ * data types. They should be handled differently. */
+ case ARGTYPE_OCTAL:
+ case ARGTYPE_UNKNOWN:
+ return sizeof(long);
+
+ case ARGTYPE_ADDR:
+ case ARGTYPE_FILE:
+ case ARGTYPE_FORMAT:
+ case ARGTYPE_STRING:
+ case ARGTYPE_STRING_N:
+ case ARGTYPE_COUNT:
+ return -1;
+ }
+
+ abort();
+}
+
+#undef alignof
+#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
+
+size_t
+type_alignof(struct Process *proc, struct arg_type_info *type)
+{
+ size_t arch_alignment = arch_type_alignof(proc, type);
+ if (arch_alignment != (size_t)-2)
+ return arch_alignment;
+
+ struct { char c; char C; } cC;
+ struct { char c; short s; } cs;
+ struct { char c; int i; } ci;
+ struct { char c; long l; } cl;
+ struct { char c; void* p; } cp;
+ struct { char c; float f; } cf;
+ struct { char c; double d; } cd;
+
+ static size_t char_alignment = alignof(C, cC);
+ static size_t short_alignment = alignof(s, cs);
+ static size_t int_alignment = alignof(i, ci);
+ static size_t long_alignment = alignof(l, cl);
+ static size_t ptr_alignment = alignof(p, cp);
+ static size_t float_alignment = alignof(f, cf);
+ static size_t double_alignment = alignof(d, cd);
+
+ switch (type->type) {
+ size_t alignment;
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ return long_alignment;
+ case ARGTYPE_CHAR:
+ return char_alignment;
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return short_alignment;
+ case ARGTYPE_FLOAT:
+ return float_alignment;
+ case ARGTYPE_DOUBLE:
+ return double_alignment;
+ case ARGTYPE_POINTER:
+ return ptr_alignment;
+
+ case ARGTYPE_ARRAY:
+ return type_alignof(proc, type->u.array_info.elt_type);
+
+ case ARGTYPE_STRUCT:
+ if (layout_struct(proc, type, NULL, &alignment, NULL) < 0)
+ return (size_t)-1;
+ return alignment;
+
+ default:
+ return int_alignment;
+ }
+}
+
+size_t
+type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt)
+{
+ assert(type->type == ARGTYPE_STRUCT
+ || type->type == ARGTYPE_ARRAY);
+
+ switch (type->type) {
+ size_t alignment;
+ size_t size;
+ case ARGTYPE_ARRAY:
+ alignment = type_alignof(proc, type->u.array_info.elt_type);
+ if (alignment == (size_t)-1)
+ return (size_t)-1;
+
+ size = type_sizeof(proc, type->u.array_info.elt_type);
+ if (size == (size_t)-1)
+ return (size_t)-1;
+
+ return emt * align(size, alignment);
+
+ case ARGTYPE_STRUCT:
+ if (layout_struct(proc, type, NULL, NULL, &emt) < 0)
+ return (size_t)-1;
+ return emt;
+
+ default:
+ abort ();
+ }
+}
+
+struct arg_type_info *
+type_element(struct arg_type_info *info, size_t emt)
+{
+ assert(info->type == ARGTYPE_STRUCT
+ || info->type == ARGTYPE_ARRAY);
+
+ switch (info->type) {
+ case ARGTYPE_ARRAY:
+ return info->u.array_info.elt_type;
+
+ case ARGTYPE_STRUCT:
+ assert(emt < type_struct_size(info));
+ return type_struct_get(info, emt);
+
+ default:
+ abort ();
+ }
+}
diff --git a/type.h b/type.h
new file mode 100644
index 0000000..e6e8b74
--- /dev/null
+++ b/type.h
@@ -0,0 +1,181 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1997-2009 Juan Cespedes
+ *
+ * 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
+ */
+
+#ifndef TYPE_H
+#define TYPE_H
+
+#include <stddef.h>
+#include "forward.h"
+#include "vect.h"
+
+enum arg_type {
+ ARGTYPE_UNKNOWN = -1,
+ ARGTYPE_VOID,
+ ARGTYPE_INT,
+ ARGTYPE_UINT,
+ ARGTYPE_LONG,
+ ARGTYPE_ULONG,
+ ARGTYPE_OCTAL,
+ ARGTYPE_CHAR,
+ ARGTYPE_SHORT,
+ ARGTYPE_USHORT,
+ ARGTYPE_FLOAT, /* float value, may require index */
+ ARGTYPE_DOUBLE, /* double value, may require index */
+ ARGTYPE_ADDR,
+ ARGTYPE_FILE,
+ ARGTYPE_FORMAT, /* printf-like format */
+ ARGTYPE_STRING, /* NUL-terminated string */
+ ARGTYPE_STRING_N, /* String of known maxlen */
+ ARGTYPE_ARRAY, /* Series of values in memory */
+ ARGTYPE_ENUM, /* Enumeration */
+ ARGTYPE_STRUCT, /* Structure of values */
+ ARGTYPE_POINTER, /* Pointer to some other type */
+ ARGTYPE_COUNT /* number of ARGTYPE_* values */
+};
+
+struct arg_type_info {
+ enum arg_type type;
+ union {
+ struct vect entries;
+
+ /* ARGTYPE_ENUM */
+ struct {
+ size_t entries;
+ char **keys;
+ int *values;
+ } enum_info;
+
+ /* ARGTYPE_ARRAY */
+ struct {
+ struct arg_type_info *elt_type;
+ size_t elt_size;
+ int len_spec;
+ int own_info:1;
+ } array_info;
+
+ /* ARGTYPE_STRING_N */
+ struct {
+ int size_spec;
+ } string_n_info;
+
+ /* ARGTYPE_STRUCT */
+ struct {
+ struct arg_type_info **fields;
+ size_t * offset;
+ size_t size;
+ } struct_info;
+
+ /* ARGTYPE_POINTER */
+ struct {
+ struct arg_type_info *info;
+ int own_info:1;
+ } ptr_info;
+
+ /* ARGTYPE_FLOAT */
+ struct {
+ size_t float_index;
+ } float_info;
+
+ /* ARGTYPE_DOUBLE */
+ struct {
+ size_t float_index;
+ } double_info;
+ } u;
+};
+
+/* Return a type info for simple type TYPE (which shall not be array,
+ * struct, enum or pointer. Each call with the same TYPE yields the
+ * same arg_type_info pointer. */
+struct arg_type_info *type_get_simple(enum arg_type type);
+
+/* Initialize INFO so it becomes ARGTYPE_ENUM. Returns 0 on success
+ * or negative value on failure. */
+void type_init_enum(struct arg_type_info *info);
+
+/* Push another member of the enumeration, named KEY, with given
+ * VALUE. If OWN_KEY, KEY is owned and released after the type is
+ * destroyed. KEY is typed as const char *, but note that if
+ * OWN_KEY, this value will be freed. */
+int type_enum_add(struct arg_type_info *info,
+ const char *key, int own_key, int value);
+
+/* Return number of enum elements of type INFO. */
+size_t type_enum_size(struct arg_type_info *info);
+
+/* Look up enum key with given VALUE in INFO. */
+const char *type_enum_get(struct arg_type_info *info, int value);
+
+/* Initialize INFO so it becomes ARGTYPE_STRUCT. The created
+ * structure contains no fields. Use type_struct_add to populate the
+ * structure. */
+void type_init_struct(struct arg_type_info *info);
+
+/* Add a new field of type FIELD_INFO to a structure INFO. If OWN,
+ * the field type is owned and destroyed together with INFO. */
+int type_struct_add(struct arg_type_info *info,
+ struct arg_type_info *field_info, int own);
+
+/* Get IDX-th field of structure type INFO. */
+struct arg_type_info *type_struct_get(struct arg_type_info *info, size_t idx);
+
+/* Return number of fields of structure type INFO. */
+size_t type_struct_size(struct arg_type_info *info);
+
+/* Initialize INFO so it becomes ARGTYPE_ARRAY. The element type is
+ * passed in ELEMENT_INFO, and array length in LENGTH_EXPR. If,
+ * respectively, OWN_INFO and OWN_LENGTH are true, the pointee and
+ * length are owned and destroyed together with INFO. */
+void type_init_array(struct arg_type_info *info,
+ struct arg_type_info *element_info, int own_info,
+ int len_spec);
+
+/* Initialize INFO so it becomes ARGTYPE_POINTER. The pointee type is
+ * passed in POINTEE_INFO. If OWN_INFO, the pointee type is owned and
+ * destroyed together with INFO. */
+void type_init_pointer(struct arg_type_info *info,
+ struct arg_type_info *pointee_info, int own_info);
+
+/* Release any memory associated with INFO. Doesn't free INFO
+ * itself. */
+void type_destroy(struct arg_type_info *info);
+
+/* Compute a size of given type. Return (size_t)-1 for error. */
+size_t type_sizeof(struct Process *proc, struct arg_type_info *type);
+
+/* Compute an alignment necessary for elements of this type. Return
+ * (size_t)-1 for error. */
+size_t type_alignof(struct Process *proc, struct arg_type_info *type);
+
+/* Align value SZ to ALIGNMENT and return the result. */
+size_t align(size_t sz, size_t alignment);
+
+/* Return ELT-th element of compound type TYPE. This is useful for
+ * arrays and structures. */
+struct arg_type_info *type_element(struct arg_type_info *type, size_t elt);
+
+/* Compute an offset of EMT-th element of type TYPE. This works for
+ * arrays and structures. Return (size_t)-1 for error. */
+size_t type_offsetof(struct Process *proc,
+ struct arg_type_info *type, size_t elt);
+
+struct arg_type_info *lookup_prototype(enum arg_type at);
+
+#endif /* TYPE_H */
diff --git a/value.c b/value.c
new file mode 100644
index 0000000..3e4e8d6
--- /dev/null
+++ b/value.c
@@ -0,0 +1,372 @@
+/*
+ * 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"
+
+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);
+ 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);
+}
+
+void
+value_set_long(struct value *valp, long value)
+{
+ valp->where = VAL_LOC_WORD;
+ valp->u.value = value;
+}
+
+unsigned char *
+value_reserve(struct value *valp, size_t size)
+{
+ if (size <= sizeof(valp->u.value)) {
+ value_set_long(valp, 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);
+}
+
+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.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, 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;
+}
+
+#include "value_dict.h"
+static int
+expr_eval(int len_spec, struct value *val, struct value_dict *arguments,
+ struct value *result)
+{
+ if (len_spec > 0) {
+ value_init(result, val->inferior, NULL,
+ lookup_prototype(ARGTYPE_UINT), 0);
+ value_set_long(result, len_spec);
+ return 0;
+ }
+
+ struct value *v;
+ if (len_spec < 0)
+ v = val_dict_get_num(arguments, -len_spec);
+ else
+ v = val_dict_get_name(arguments, "retval");
+
+ if (v == NULL)
+ return -1;
+
+ *result = *v;
+ 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.len_spec, 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;
+
+ struct arg_type_info *elt_type = val->type->u.array_info.elt_type;
+ size_t elt_size = type_sizeof(val->inferior, 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.address = val->u.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;
+}
+
+int
+value_extract_word(struct value *value, long *retp, struct value_dict *arguments)
+{
+ union {
+ long val;
+ unsigned char buf[0];
+ } u;
+ memset(u.buf, 0, sizeof(u));
+ if (value_extract_buf(value, u.buf, arguments) < 0)
+ return -1;
+ *retp = u.val;
+ return 0;
+}
+
+int
+value_extract_buf(struct value *value, unsigned char *tgt,
+ struct value_dict *arguments)
+{
+ unsigned char *data = value_get_data(value, arguments);
+ if (data == NULL)
+ return -1;
+
+ size_t sz = type_sizeof(value->inferior, value->type);
+ if (sz == (size_t)-1)
+ return -1;
+
+ memcpy(tgt, data, sz);
+ return 0;
+}
+
+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;
+}
diff --git a/value.h b/value.h
new file mode 100644
index 0000000..e88b72a
--- /dev/null
+++ b/value.h
@@ -0,0 +1,152 @@
+/*
+ * 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
+ */
+
+#ifndef VALUE_H
+#define VALUE_H
+
+#include "forward.h"
+
+/* Values are objects that capture data fetched from an inferior.
+ * Typically a value is attached to a single inferior where it was
+ * extracted from, but it is possible to create a detached value.
+ * Each value is typed. Values support a number of routines, such as
+ * dereferencing if the value is of pointer type, array or structure
+ * access, etc.
+ *
+ * A value can be uninitialized, abstract or reified. Abstract values
+ * are just references into inferior, no transfer has taken place yet.
+ * Reified values have been copied out of the corresponding inferior,
+ * or otherwise set to some value. */
+
+enum value_location_t {
+ VAL_LOC_NODATA = 0, /* Uninitialized. */
+ VAL_LOC_INFERIOR, /* Value is in the inferior process. */
+ VAL_LOC_COPY, /* Value was copied out of the inferior. */
+ VAL_LOC_SHARED, /* Like VAL_LOC_COPY, but don't free. */
+ VAL_LOC_WORD, /* Like VAL_LOC_COPY, but small enough. */
+};
+
+struct value {
+ struct arg_type_info *type;
+ struct Process *inferior;
+ struct value *parent;
+ size_t size;
+ union {
+ void *address; /* VAL_LOC_CLIENT, VAL_LOC_COPY,
+ VAL_LOC_SHARED */
+ long value; /* VAL_LOC_WORD */
+ unsigned char buf[0];
+ } u;
+ enum value_location_t where;
+ int own_type;
+};
+
+/* Initialize VALUE. INFERIOR must not be NULL. PARENT is parental
+ * value, in case of compound types. It may be NULL. TYPE is a type
+ * of the value. It may be NULL if the type is not yet known. If
+ * OWN_TYPE, the passed-in type is owned and released by value. */
+void value_init(struct value *value, struct Process *inferior,
+ struct value *parent, struct arg_type_info *type,
+ int own_type);
+
+/* Initialize VALUE. This is like value_init, except that inferior is
+ * NULL. VALP is initialized as a detached value, without assigned
+ * process. You have to be careful not to use VAL_LOC_INFERIOR
+ * values if the value is detached. */
+void value_init_detached(struct value *value, struct value *parent,
+ struct arg_type_info *type, int own_type);
+
+/* Set TYPE. This releases old type if it was owned. TYPE is owned
+ * and released if OWN_TYPE. */
+void value_set_type(struct value *value,
+ struct arg_type_info *type, int own_type);
+
+/* Transfers the ownership of VALUE's type, if any, to the caller.
+ * This doesn't reset the VALUE's type, but gives up ownership if
+ * there was one. Previous ownership is passed in OWN_TYPE. */
+void value_take_type(struct value *value,
+ struct arg_type_info **type, int *own_type);
+
+/* Release the data held by VALP, if any, but not the type. */
+void value_release(struct value *valp);
+
+/* Destroy the value. This is like value_release, but it additionally
+ * frees the value type, if it's own_type. It doesn't free the VAL
+ * pointer itself. */
+void value_destroy(struct value *val);
+
+/* Set the data held by VALP to VALUE. This also sets the value's
+ * where to VAL_LOC_WORD. */
+void value_set_long(struct value *valp, long value);
+
+/* Set the data held by VALP to a buffer of size SIZE. This buffer
+ * may be allocated by malloc. Returns NULL on failure. */
+unsigned char *value_reserve(struct value *valp, size_t size);
+
+/* Access ELEMENT-th field of the compound value VALP, and store the
+ * result into the value RET_VAL. Returns 0 on success, or negative
+ * value on failure. */
+int value_init_element(struct value *ret_val, struct value *valp, size_t element);
+
+/* De-reference pointer value, and store the result into the value
+ * RET_VAL. Returns 0 on success, or negative value on failure. */
+int value_init_deref(struct value *ret_val, struct value *valp);
+
+/* If value is in inferior, copy it over to ltrace. Return 0 for
+ * success or negative value for failure. */
+int value_reify(struct value *val, struct value_dict *arguments);
+
+/* Return a pointer to the data of the value. This copies the data
+ * from the inferior to the tracer. Returns NULL on failure. */
+unsigned char *value_get_data(struct value *val, struct value_dict *arguments);
+
+/* Return a pointer to the raw data of the value. This shall not be
+ * called on a VAL_LOC_INFERIOR value. */
+unsigned char *value_get_raw_data(struct value *val);
+
+/* Copy value VAL into the area pointed-to by RETP. Return 0 on
+ * success or a negative value on failure. */
+int value_clone(struct value *retp, struct value *val);
+
+/* Give a size of given value. Return (size_t)-1 for error. This is
+ * a full size of the value. In particular for arrays, it returns
+ * actual length of the array, as computed by the length
+ * expression. */
+size_t value_size(struct value *val, struct value_dict *arguments);
+
+/* Extract at most word-sized datum from the value. Return 0 on
+ * success or negative value on failure. */
+int value_extract_word(struct value *val, long *retp,
+ struct value_dict *arguments);
+
+/* Copy contents of VAL to DATA. The buffer must be large enough to
+ * hold all the data inside. */
+int value_extract_buf(struct value *val, unsigned char *data,
+ struct value_dict *arguments);
+
+/* Find the most enclosing parental value that is a struct. Return
+ * NULL when there is no such parental value. */
+struct value *value_get_parental_struct(struct value *val);
+
+/* Determine whether this is all-zero value. Returns >0 if it is, ==0
+ * if it isn't, <0 on error. */
+int value_is_zero(struct value *val, struct value_dict *arguments);
+
+#endif /* VALUE_H */
diff --git a/value_dict.c b/value_dict.c
new file mode 100644
index 0000000..3f3880a
--- /dev/null
+++ b/value_dict.c
@@ -0,0 +1,147 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "value_dict.h"
+#include "value.h"
+
+struct named_value
+{
+ const char *name;
+ struct value value;
+ int own_name;
+};
+
+void
+val_dict_init(struct value_dict *dict)
+{
+ VECT_INIT(&dict->numbered, struct value);
+ VECT_INIT(&dict->named, struct named_value);
+}
+
+static int
+value_clone_cb(struct value *tgt, struct value *src, void *data)
+{
+ return value_clone(tgt, src);
+}
+
+static void
+value_dtor(struct value *val, void *data)
+{
+ value_destroy(val);
+}
+
+static int
+named_value_clone(struct named_value *tgt, struct named_value *src, void *data)
+{
+ tgt->name = strdup(src->name);
+ if (tgt->name == NULL)
+ return -1;
+ tgt->own_name = 1;
+ if (value_clone(&tgt->value, &src->value) < 0) {
+ free((char *)tgt->name);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+named_value_dtor(struct named_value *named, void *data)
+{
+ if (named->own_name)
+ free((char *)named->name);
+ value_destroy(&named->value);
+}
+
+int
+val_dict_clone(struct value_dict *target, struct value_dict *source)
+{
+ if (VECT_CLONE(&target->numbered, &source->numbered, struct value,
+ value_clone_cb, value_dtor, NULL) < 0)
+ return -1;
+
+ if (VECT_CLONE(&target->named, &source->named, struct named_value,
+ named_value_clone, named_value_dtor, NULL) < 0) {
+ VECT_DESTROY(&target->numbered, struct value, value_dtor, NULL);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+val_dict_push_next(struct value_dict *dict, struct value *val)
+{
+ return VECT_PUSHBACK(&dict->numbered, val);
+}
+
+int
+val_dict_push_named(struct value_dict *dict, struct value *val,
+ const char *name, int own_name)
+{
+ if (own_name && (name = strdup(name)) == NULL)
+ return -1;
+ struct named_value element = { name, *val, own_name };
+ if (VECT_PUSHBACK(&dict->named, &element) < 0) {
+ if (own_name)
+ free((char *)name);
+ return -1;
+ }
+ return 0;
+}
+
+size_t
+val_dict_count(struct value_dict *dict)
+{
+ return vect_size(&dict->numbered);
+}
+
+struct value *
+val_dict_get_num(struct value_dict *dict, size_t num)
+{
+ assert(num < vect_size(&dict->numbered));
+ return VECT_ELEMENT(&dict->numbered, struct value, num);
+}
+
+struct value *
+val_dict_get_name(struct value_dict *dict, const char *name)
+{
+ size_t i;
+ for (i = 0; i < vect_size(&dict->named); ++i) {
+ struct named_value *element
+ = VECT_ELEMENT(&dict->named, struct named_value, i);
+ if (strcmp(element->name, name) == 0)
+ return &element->value;
+ }
+ return NULL;
+}
+
+void
+val_dict_destroy(struct value_dict *dict)
+{
+ if (dict == NULL)
+ return;
+
+ VECT_DESTROY(&dict->numbered, struct value, value_dtor, NULL);
+ VECT_DESTROY(&dict->named, struct named_value, named_value_dtor, NULL);
+}
diff --git a/value_dict.h b/value_dict.h
new file mode 100644
index 0000000..f75ee37
--- /dev/null
+++ b/value_dict.h
@@ -0,0 +1,67 @@
+/*
+ * 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
+ */
+
+#ifndef ARGS_H
+#define ARGS_H
+
+#include "forward.h"
+#include "vect.h"
+
+/* Value dictionary is used to store actual function arguments. It
+ * supports both numbered and named arguments. */
+struct value_dict
+{
+ struct vect numbered;
+ struct vect named;
+};
+
+/* Initialize DICT. */
+void val_dict_init(struct value_dict *dict);
+
+/* Clone SOURCE into TARGET. Return 0 on success or a negative value
+ * on failure. */
+int val_dict_clone(struct value_dict *target, struct value_dict *source);
+
+/* Push next numbered value, VAL. The value is copied over and the
+ * dictionary becomes its owner, and is responsible for destroying it
+ * later. Returns 0 on success and a negative value on failure. */
+int val_dict_push_next(struct value_dict *dict, struct value *val);
+
+/* Return count of numbered arguments. */
+size_t val_dict_count(struct value_dict *dict);
+
+/* Push value VAL named NAME. See notes at val_dict_push_next about
+ * value ownership. The name is owned and freed if OWN_NAME is
+ * non-zero. */
+int val_dict_push_named(struct value_dict *dict, struct value *val,
+ const char *name, int own_name);
+
+/* Get NUM-th numbered argument, or NULL if there's not that much
+ * arguments. */
+struct value *val_dict_get_num(struct value_dict *dict, size_t num);
+
+/* Get argument named NAME, or NULL if there's no such argument. */
+struct value *val_dict_get_name(struct value_dict *dict, const char *name);
+
+/* Destroy the dictionary and all the values in it. Note that DICT
+ * itself (the pointer) is not freed. */
+void val_dict_destroy(struct value_dict *dict);
+
+#endif /* ARGS_H */