Add private data field to struct tcb

We need to be able to store private data in the struct tcb across it's
lifetime.  To ensure proper deallocation of this stored data a callback
must be provided along with the data.  The callback is executed
automatically on exiting syscall, and when the life of the tcb ends.

* defs.h (struct tcb): Add _priv_data and _free_priv_data fields.
(get_tcb_priv_data, set_tcb_priv_data, free_tcb_priv_data):
New prototypes.
(get_tcb_priv_ulong, set_tcb_priv_ulong): New static inline functions.
* strace.c (get_tcb_priv_data, set_tcb_priv_data, free_tcb_priv_data):
New functions.
(droptcb): Call free_tcb_priv_data.
* syscall.c (trace_syscall_exiting): Call free_tcb_priv_data
when clearing TCB_INSYSCALL flag.

Signed-off-by: Patrik Jakobsson <patrik.jakobsson@linux.intel.com>
Signed-off-by: Dmitry V. Levin <ldv@altlinux.org>
diff --git a/defs.h b/defs.h
index cf55f11..4b7e8ed 100644
--- a/defs.h
+++ b/defs.h
@@ -314,6 +314,8 @@
 	int curcol;		/* Output column for this process */
 	FILE *outf;		/* Output file for this process */
 	const char *auxstr;	/* Auxiliary info from syscall (see RVAL_STR) */
+	void *_priv_data;	/* Private data for syscall decoding functions */
+	void (*_free_priv_data)(void *); /* Callback for freeing priv_data */
 	const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad scno */
 	const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" msg */
 	struct timeval stime;	/* System time usage as of last process wait */
@@ -531,6 +533,21 @@
 extern void temporarily_clear_syserror(struct tcb *);
 extern void restore_cleared_syserror(struct tcb *);
 
+extern void *get_tcb_priv_data(const struct tcb *);
+extern int set_tcb_priv_data(struct tcb *, void *priv_data,
+			     void (*free_priv_data)(void *));
+extern void free_tcb_priv_data(struct tcb *);
+
+static inline unsigned long get_tcb_priv_ulong(const struct tcb *tcp)
+{
+	return (unsigned long) get_tcb_priv_data(tcp);
+}
+
+static inline int set_tcb_priv_ulong(struct tcb *tcp, unsigned long val)
+{
+	return set_tcb_priv_data(tcp, (void *) val, 0);
+}
+
 extern int umoven(struct tcb *, long, unsigned int, void *);
 #define umove(pid, addr, objp)	\
 	umoven((pid), (addr), sizeof(*(objp)), (void *) (objp))
diff --git a/strace.c b/strace.c
index 564f1cc..9e2fc08 100644
--- a/strace.c
+++ b/strace.c
@@ -746,12 +746,45 @@
 	error_msg_and_die("bug in alloctcb");
 }
 
+void *
+get_tcb_priv_data(const struct tcb *tcp)
+{
+	return tcp->_priv_data;
+}
+
+int
+set_tcb_priv_data(struct tcb *tcp, void *const priv_data,
+		  void (*const free_priv_data)(void *))
+{
+	if (tcp->_priv_data)
+		return -1;
+
+	tcp->_free_priv_data = free_priv_data;
+	tcp->_priv_data = priv_data;
+
+	return 0;
+}
+
+void
+free_tcb_priv_data(struct tcb *tcp)
+{
+	if (tcp->_priv_data) {
+		if (tcp->_free_priv_data) {
+			tcp->_free_priv_data(tcp->_priv_data);
+			tcp->_free_priv_data = NULL;
+		}
+		tcp->_priv_data = NULL;
+	}
+}
+
 static void
 droptcb(struct tcb *tcp)
 {
 	if (tcp->pid == 0)
 		return;
 
+	free_tcb_priv_data(tcp);
+
 #ifdef USE_LIBUNWIND
 	if (stack_trace_enabled) {
 		unwind_tcb_fin(tcp);
diff --git a/syscall.c b/syscall.c
index c61f827..5c83818 100644
--- a/syscall.c
+++ b/syscall.c
@@ -904,6 +904,7 @@
 		line_ended();
 		tcp->flags &= ~TCB_INSYSCALL;
 		tcp->sys_func_rval = 0;
+		free_tcb_priv_data(tcp);
 		return res;
 	}
 	tcp->s_prev_ent = tcp->s_ent;
@@ -1085,6 +1086,7 @@
  ret:
 	tcp->flags &= ~TCB_INSYSCALL;
 	tcp->sys_func_rval = 0;
+	free_tcb_priv_data(tcp);
 	return 0;
 }