 .gitignore                             |    2 +
 Makefile                               |   13 ++-
 libfiu/Makefile                        |    6 +-
 libfiu/backtrace.c                     |   66 ++++++++++++
 libfiu/fiu-control.h                   |    2 +-
 libfiu/fiu.c                           |   16 +++-
 libfiu/internal.h                      |    5 +-
 preload/posix/Makefile                 |    4 +
 preload/posix/codegen.c                |   39 ++++----
 preload/posix/codegen.h                |   29 ++++--
 preload/posix/function_list.in         |    2 -
 preload/posix/generate                 |    4 +-
 preload/posix/modules/libc.mm.custom.c |  113 ---------------------
 preload/posix/modules/libc.mm.mod      |   21 ++++
 preload/posix/modules/libc.str.mod     |    9 ++-
 preload/posix/modules/posix.custom.c   |   24 ++++-
 preload/posix/modules/posix.io.mod     |   18 ++--
 tests/Makefile                         |   29 ++++--
 tests/generated/Makefile               |   79 +++++++++++++++
 tests/generated/README                 |   18 ++++
 tests/generated/generate-test          |  170 ++++++++++++++++++++++++++++++++
 tests/generated/tests/kill.conf        |   10 ++
 tests/generated/tests/malloc.conf      |    9 ++
 tests/generated/tests/mmap.conf        |   12 +++
 tests/generated/tests/open.conf        |   10 ++
 tests/test-enable_stack.c              |    9 ++-
 tests/test-enable_stack_by_name.c      |   10 ++-
 tests/wrap-python                      |   59 +++++++++++
 28 files changed, 602 insertions(+), 186 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8b1881d..fa4df1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@ preload/run/build-needlibdl
 tests/*.o
 tests/build-flags
 tests/test-?
+tests/generated/build-flags
+tests/generated/tests/*.[oc]
diff --git a/Makefile b/Makefile
index bae9e6b..6749813 100644
--- a/Makefile
+++ b/Makefile
@@ -51,9 +51,16 @@ utils_install: utils
 utils_uninstall:
 	$(MAKE) -C utils uninstall
 
-test: libfiu bindings
+
+tests: test
+
+test: libfiu bindings preload
 	$(MAKE) -C tests
 
+test_clean:
+	$(MAKE) -C tests clean
+
+
 bindings: python2 python3
 
 bindings_install: python2_install python3_install
@@ -76,7 +83,7 @@ python_clean:
 	cd bindings/python && rm -rf build/
 
 
-clean: python_clean preload_clean libfiu_clean utils_clean
+clean: python_clean preload_clean libfiu_clean utils_clean test_clean
 
 
 .PHONY: default all clean install all_install uninstall all_uninstall \
@@ -85,6 +92,6 @@ clean: python_clean preload_clean libfiu_clean utils_clean
 	bindings bindings_install bindings_clean \
 	preload preload_clean preload_install preload_uninstall \
 	utils utils_clean utils_install utils_uninstall \
-	test
+	test tests test_clean
 
 
diff --git a/libfiu/Makefile b/libfiu/Makefile
index ea765bc..1288b8d 100644
--- a/libfiu/Makefile
+++ b/libfiu/Makefile
@@ -30,15 +30,15 @@ else
 	NICE_CC = $(CC)
 endif
 
-LIB_VER=0.90
+LIB_VER=0.91
 LIB_SO_VER=0
 
 
 # We attempt to detect if we have the extensions we use in backtrace.c by just
 # attempting to compile it normally, and fall back to the dummy version if
 # that fails. Not very sophisticated but should be safe.
-USE_DUMMY_BACKTRACE := $(shell $(CC) -c backtrace.c -o /dev/null 2>/dev/null || \
-	echo -DDUMMY_BACKTRACE)
+USE_DUMMY_BACKTRACE := $(shell $(CC) $(ALL_CFLAGS) -c backtrace.c \
+	-o /dev/null 2>/dev/null || echo -DDUMMY_BACKTRACE)
 ifndef USE_DUMMY_BACKTRACE
 # The real backtrace needs linking against libdl.
 USE_LIBDL = -ldl
diff --git a/libfiu/backtrace.c b/libfiu/backtrace.c
index fc44d9a..c85da04 100644
--- a/libfiu/backtrace.c
+++ b/libfiu/backtrace.c
@@ -7,6 +7,7 @@
 /* This is needed for some of the functions below. */
 #define _GNU_SOURCE
 
+#include <stdlib.h>	/* NULL */
 #include <execinfo.h>
 #include <dlfcn.h>
 #include <sys/procfs.h>
@@ -59,6 +60,7 @@ void *get_func_addr(const char *func_name)
 
 #include <stddef.h>	/* for NULL */
 
+
 int get_backtrace(void *buffer, int size)
 {
 	return 0;
@@ -80,3 +82,67 @@ void *get_func_addr(const char *func_name)
 }
 
 #endif // DUMMY_BACKTRACE
+
+/* Ugly but useful conversion from function pointer to void *.
+ * This is not guaranteed by the standard, but has to work on all platforms
+ * where we support backtrace(), because that function assumes it so. */
+static void *fp_to_voidp(void (*funcp)())
+{
+	unsigned char **p;
+	p = (unsigned char **) &funcp;
+	return *p;
+}
+
+int backtrace_works(void (*caller)())
+{
+	/* We remember the result so we don't have to compute it over an over
+	 * again, we know it doesn't change. */
+	static int works = -1;
+
+	void *start = NULL;
+	void *end = NULL;
+	void *bt_buffer[100];
+	void *pc;
+	int nptrs, i;
+
+	/* Return the result if we know it. */
+	if (works >= 0)
+		return works;
+
+	nptrs = get_backtrace(bt_buffer, 100);
+	if (nptrs <= 0) {
+		works = 0;
+		return works;
+	}
+
+	/* We will detect if it works by looking for the caller in the
+	 * backtrace. */
+	start = get_func_start(fp_to_voidp(caller));
+	end = get_func_end(fp_to_voidp(caller));
+
+	if (start == NULL && end == NULL) {
+		works = 0;
+		return works;
+	}
+
+	for (i = 0; i < nptrs; i++) {
+		pc = bt_buffer[i];
+
+		/* On some platforms, we have everything except
+		 * get_func_end(), and that's ok. */
+		if (end) {
+			if (pc >= start && pc <= end) {
+				works = 1;
+				return works;
+			}
+		} else {
+			if (get_func_start(pc) == start) {
+				works = 1;
+				return works;
+			}
+		}
+	}
+
+	works = 0;
+	return works;
+}
diff --git a/libfiu/fiu-control.h b/libfiu/fiu-control.h
index 7e54ce3..88686cf 100644
--- a/libfiu/fiu-control.h
+++ b/libfiu/fiu-control.h
@@ -136,7 +136,7 @@ int fiu_rc_fifo(const char *basename);
  * At the moment, this function is exported for consumption by the libfiu
  * utilities.
  *
- * @param command:  A zero-terminated string with the command to apply.
+ * @param cmd:  A zero-terminated string with the command to apply.
  * @param error:  In case of an error, it will point to a human-readable error
  *			message.
  * @returns  0 if success, < 0 otherwise.
diff --git a/libfiu/fiu.c b/libfiu/fiu.c
index cff61bc..68cab04 100644
--- a/libfiu/fiu.c
+++ b/libfiu/fiu.c
@@ -180,8 +180,8 @@ static int pc_in_func(struct pf_info *pf, void *pc)
 	/* We don't know if the platform allows us to know func_end,
 	 * so we use different methods depending on its availability. */
 	if (pf->minfo.stack.func_end) {
-		return (pc > pf->minfo.stack.func_start &&
-				pc < pf->minfo.stack.func_end);
+		return (pc >= pf->minfo.stack.func_start &&
+				pc <= pf->minfo.stack.func_end);
 	} else {
 		return pf->minfo.stack.func_start == get_func_start(pc);
 	}
@@ -517,11 +517,17 @@ int fiu_enable_stack(const char *name, int failnum, void *failinfo,
 	if (func_pos_in_stack != -1)
 		return -1;
 
+	if (backtrace_works((void (*)()) fiu_enable_stack) == 0)
+		return -1;
+
 	pf = insert_new_fail(name, failnum, failinfo, flags, PF_STACK);
 	if (pf == NULL)
 		return -1;
 
 	pf->minfo.stack.func_start = func;
+
+	/* Note get_func_end(func) can return NULL and we would still be able
+	 * to make it work, see pc_in_func() above. */
 	pf->minfo.stack.func_end = get_func_end(func);
 	pf->minfo.stack.func_pos_in_stack = func_pos_in_stack;
 	return 0;
@@ -534,6 +540,12 @@ int fiu_enable_stack_by_name(const char *name, int failnum, void *failinfo,
 {
 	void *fp;
 
+	/* We need to check this here instead of relying on the test within
+	 * fiu_enable_stack() in case it is inlined; that would fail the check
+	 * because fiu_enable_stack() would not be in the stack. */
+	if (backtrace_works((void (*)()) fiu_enable_stack_by_name) == 0)
+		return -1;
+
 	fp = get_func_addr(func_name);
 	if (fp == NULL)
 		return -1;
diff --git a/libfiu/internal.h b/libfiu/internal.h
index 1cb95ff..6e25554 100644
--- a/libfiu/internal.h
+++ b/libfiu/internal.h
@@ -7,7 +7,6 @@
 /* Recursion count, used both in fiu.c and fiu-rc.c */
 extern __thread int rec_count;
 
-
 /* Gets a stack trace. The pointers are stored in the given buffer, which must
  * be of the given size. The number of entries is returned.
  * It's a wrapper around glibc's backtrace(). */
@@ -23,5 +22,9 @@ void *get_func_start(void *func);
 /* Returns a pointer to the function given by name. */
 void *get_func_addr(const char *func_name);
 
+/* Do the above backtrace-related functions work?
+ * Takes a pointer to the caller so it can verify it's on the stack. */
+int backtrace_works(void (*caller)());
+
 #endif
 
diff --git a/preload/posix/Makefile b/preload/posix/Makefile
index b2afa58..b269781 100644
--- a/preload/posix/Makefile
+++ b/preload/posix/Makefile
@@ -60,6 +60,10 @@ $(OBJS): build-flags
 .c.o:
 	$(NICE_CC) $(ALL_CFLAGS) -c $< -o $@
 
+# We define _GNU_SOURCE to get RTLD_NEXT if available; on non-GNU
+# platforms it should be harmless.
+codegen.o: codegen.c
+	$(NICE_CC) $(ALL_CFLAGS) -D_GNU_SOURCE -c $< -o $@
 
 # some platforms do not have libdl, we only use it if available
 build-needlibdl:
diff --git a/preload/posix/codegen.c b/preload/posix/codegen.c
index 177fdcd..691d6f0 100644
--- a/preload/posix/codegen.c
+++ b/preload/posix/codegen.c
@@ -6,9 +6,6 @@
 #include "codegen.h"
 #include "build-env.h"
 
-/* Dynamically load libc */
-void *_fiu_libc;
-
 /* Recursion counter, per-thread */
 int __thread _fiu_called = 0;
 
@@ -18,30 +15,32 @@ int __thread _fiu_called = 0;
 #warning "Building without using constructor priorities"
 #endif
 
-void constructor_attr(200) _fiu_init(void)
+/* Get a symbol from libc.
+ * This function is a wrapper around dlsym(libc, ...), that we use to abstract
+ * away how we get the libc wrapper, because on some platforms there are
+ * better shortcuts. */
+void *libc_symbol(const char *symbol)
 {
-	static int initialized = 0;
-
-	/* When built without constructor priorities, we could be called more
-	 * than once during the initialization phase: one because we're marked
-	 * as a constructor, and another when one of the other constructors
-	 * sees that it doesn't have _fiu_libc set. */
-
-	printd("_fiu_init() start (%d)\n", initialized);
-	if (initialized)
-		goto exit;
+#ifdef RTLD_NEXT
+	return dlsym(RTLD_NEXT, symbol);
+#else
+	/* We don't want to get this over and over again, so we set it once
+	 * and reuse it afterwards. */
+	static void *_fiu_libc = NULL;
 
-	_fiu_libc = dlopen(LIBC_SONAME, RTLD_NOW);
 	if (_fiu_libc == NULL) {
-		fprintf(stderr, "Error loading libc: %s\n", dlerror());
-		exit(1);
+		_fiu_libc = dlopen(LIBC_SONAME, RTLD_NOW);
+		if (_fiu_libc == NULL) {
+			fprintf(stderr, "Error loading libc: %s\n", dlerror());
+			exit(1);
+		}
 	}
-	initialized = 1;
 
-exit:
-	printd("_fiu_init() done\n");
+	return dlsym(_fiu_libc, symbol);
+#endif
 }
 
+
 /* this runs after all function-specific constructors */
 static void constructor_attr(250) _fiu_init_final(void)
 {
diff --git a/preload/posix/codegen.h b/preload/posix/codegen.h
index 80b3344..efee7bc 100644
--- a/preload/posix/codegen.h
+++ b/preload/posix/codegen.h
@@ -2,17 +2,15 @@
 #ifndef _FIU_CODEGEN
 #define _FIU_CODEGEN
 
-#include <dlfcn.h>		/* dlsym() */
 #include <fiu.h>		/* fiu_* */
 #include <stdlib.h>		/* NULL, random() */
 
-/* Pointer to the dynamically loaded library */
-extern void *_fiu_libc;
-void _fiu_init(void);
-
 /* Recursion counter, per-thread */
 extern int __thread _fiu_called;
 
+/* Get a symbol from libc */
+void *libc_symbol(const char *symbol);
+
 /* Some compilers support constructor priorities. Since we don't rely on them,
  * but use them for clarity purposes, use a macro so libfiu builds on systems
  * where they're not supported.
@@ -76,18 +74,20 @@ extern int __thread _fiu_called;
  */
 
 /* Generates the common top of the wrapped function */
-#define mkwrap_top(RTYPE, NAME, PARAMS, PARAMSN, PARAMST)	\
+#define mkwrap_top(RTYPE, NAME, PARAMS, PARAMSN, PARAMST, ON_ERR) \
 	static RTYPE (*_fiu_orig_##NAME) PARAMS = NULL;		\
 								\
+	static int _fiu_in_init_##NAME = 0;			\
+								\
 	static void constructor_attr(201) _fiu_init_##NAME(void) \
 	{							\
 		rec_inc();					\
-								\
-		if (_fiu_libc == NULL)				\
-			_fiu_init();				\
+		_fiu_in_init_##NAME++;				\
 								\
 		_fiu_orig_##NAME = (RTYPE (*) PARAMST)		\
-				dlsym(_fiu_libc, #NAME);	\
+				libc_symbol(#NAME);		\
+								\
+		_fiu_in_init_##NAME--;				\
 		rec_dec();					\
 	}							\
 								\
@@ -97,6 +97,15 @@ extern int __thread _fiu_called;
 		int fstatus;					\
 								\
 		if (_fiu_called) {				\
+			if (_fiu_orig_##NAME == NULL) {		\
+				if (_fiu_in_init_##NAME) {	\
+					printd("fail on init\n"); \
+					return ON_ERR;		\
+				} else {			\
+					printd("get orig\n");	\
+					_fiu_init_##NAME();	\
+				}				\
+			}					\
 			printd("orig\n");			\
 			return (*_fiu_orig_##NAME) PARAMSN;	\
 		}						\
diff --git a/preload/posix/function_list.in b/preload/posix/function_list.in
index d82bf59..94e71d5 100644
--- a/preload/posix/function_list.in
+++ b/preload/posix/function_list.in
@@ -8,8 +8,6 @@ It is not set in stone, although it shouldn't change frequently.
 Manually written
 ----------------
 
-malloc				libc/mm/malloc
-realloc				libc/mm/realloc
 open				posix/io/oc/open
 
 
diff --git a/preload/posix/generate b/preload/posix/generate
index e7aac43..9c93ef6 100755
--- a/preload/posix/generate
+++ b/preload/posix/generate
@@ -100,9 +100,9 @@ class Function:
 		paramst = ', '.join([i[0] for i in self.params_info])
 		paramsn = ', '.join([i[1] for i in self.params_info])
 
-		f.write('mkwrap_top(%s, %s, (%s), (%s), (%s))\n' % \
+		f.write('mkwrap_top(%s, %s, (%s), (%s), (%s), (%s))\n' % \
 				(self.ret_type, self.name, self.params,
-					paramsn, paramst) )
+					paramsn, paramst, self.on_error) )
 
 		if self.reduce:
 			f.write('mkwrap_body_reduce("%s/reduce", %s)\n' % \
diff --git a/preload/posix/modules/libc.mm.custom.c b/preload/posix/modules/libc.mm.custom.c
deleted file mode 100644
index e94dec3..0000000
--- a/preload/posix/modules/libc.mm.custom.c
+++ /dev/null
@@ -1,113 +0,0 @@
-
-/*
- * Custom-made wrappers for malloc() and realloc().
- *
- * They can't be made generic because, at least on glibc, they're used before
- * constructors are called.
- *
- * We use __malloc_hook, the glibc-specific interface, so this is glibc-only.
- */
-
-/* We need to include features.h, which in turns defines __GLIBC__. However,
- * features.h is glibc-specific and does not exist on other platforms, so we
- * can't include it directly or the file won't build.
- * Instead, we have to resort to this ugly trick of including limits.h, which
- * is standard and we know glibc's implementation includes features.h.
- */
-#include <limits.h>
-
-#ifndef __GLIBC__
-  #warning "Not using glibc, so no malloc() wrappers will be available"
-#else
-
-#include "codegen.h"
-
-#include <malloc.h>
-
-
-/* Original glibc's hooks, saved in our init function */
-static void *(*old_malloc_hook)(size_t, const void *);
-static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);
-
-/* Our own hooks, that will replace glibc's */
-static void *fiu_malloc_hook(size_t size, const void *caller);
-static void *fiu_realloc_hook(void *ptr, size_t size, const void *caller);
-
-
-/* We are not going to use __malloc_initialize_hook because it would be called
- * before our library initialization functions, which include fiu_init().
- * Instead, we will use a constructor just like the other wrappers, but it
- * will run after them just to make things tidier (NOT because it is
- * necessary). */
-
-static void constructor_attr(202) fiu_init_malloc(void)
-{
-	/* Save original hooks, used in ours to prevent unwanted recursion */
-	old_malloc_hook = __malloc_hook;
-	old_realloc_hook = __realloc_hook;
-
-	__malloc_hook = fiu_malloc_hook;
-	__realloc_hook = fiu_realloc_hook;
-}
-
-
-static void *fiu_malloc_hook(size_t size, const void *caller)
-{
-	void *r;
-	int fstatus;
-
-	/* fiu_fail() may call anything */
-	rec_inc();
-
-	/* See __malloc_hook(3) for details */
-	__malloc_hook = old_malloc_hook;
-
-	fstatus = fiu_fail("libc/mm/malloc");
-	if (fstatus != 0) {
-		r = NULL;
-		goto exit;
-	}
-
-	printd("calling orig\n");
-	r = malloc(size);
-
-exit:
-	old_malloc_hook = __malloc_hook;
-	__malloc_hook = fiu_malloc_hook;
-
-	rec_dec();
-	return r;
-}
-
-
-static void *fiu_realloc_hook(void *ptr, size_t size, const void *caller)
-{
-	void *r;
-	int fstatus;
-
-	/* fiu_fail() may call anything */
-	rec_inc();
-
-	/* See __malloc_hook(3) for details */
-	__realloc_hook = old_realloc_hook;
-
-	fstatus = fiu_fail("libc/mm/realloc");
-	if (fstatus != 0) {
-		r = NULL;
-		goto exit;
-	}
-
-	printd("calling orig\n");
-	r = realloc(ptr, size);
-
-exit:
-	old_realloc_hook = __realloc_hook;
-	__realloc_hook = fiu_realloc_hook;
-
-	rec_dec();
-	return r;
-}
-
-
-#endif // defined __GLIBC__
-
diff --git a/preload/posix/modules/libc.mm.mod b/preload/posix/modules/libc.mm.mod
new file mode 100644
index 0000000..20f9f04
--- /dev/null
+++ b/preload/posix/modules/libc.mm.mod
@@ -0,0 +1,21 @@
+
+include: <errno.h>
+include: <stdlib.h>
+
+fiu name base: libc/mm/
+
+
+void *malloc(size_t size);
+	on error: NULL
+	valid errnos: ENOMEM
+
+void *calloc(size_t nmemb, size_t size);
+	on error: NULL
+	valid errnos: ENOMEM
+
+void *realloc(void *ptr, size_t size);
+	on error: NULL
+	valid errnos: ENOMEM
+
+# Note we don't wrap free() as it does not return anything.
+
diff --git a/preload/posix/modules/libc.str.mod b/preload/posix/modules/libc.str.mod
index 3873d19..b9c7690 100644
--- a/preload/posix/modules/libc.str.mod
+++ b/preload/posix/modules/libc.str.mod
@@ -1,15 +1,22 @@
 
 include: <string.h>
 include: <errno.h>
+include: <stdlib.h>
 
 fiu name base: libc/str/
 
+# glibc, when _GNU_SOURCE is defined, can have macros for strdup/strndup, so
+# we need to avoid those.
+
+v: #ifndef strdup
 char *strdup(const char *s);
 	on error: NULL
 	valid errnos: ENOMEM
+v: #endif
 
+v: #ifndef strndup
 char *strndup(const char *s, size_t n);
 	on error: NULL
 	valid errnos: ENOMEM
-
+v: #endif
 
diff --git a/preload/posix/modules/posix.custom.c b/preload/posix/modules/posix.custom.c
index 1c709cd..959c58b 100644
--- a/preload/posix/modules/posix.custom.c
+++ b/preload/posix/modules/posix.custom.c
@@ -18,15 +18,17 @@
  * of arguments */
 static int (*_fiu_orig_open) (const char *pathname, int flags, ...) = NULL;
 
+static int _fiu_in_init_open = 0;
+
 static void constructor_attr(201) _fiu_init_open(void)
 {
 	rec_inc();
-
-	if (_fiu_libc == NULL)
-		_fiu_init();
+	_fiu_in_init_open++;
 
 	_fiu_orig_open = (int (*) (const char *, int, ...))
-			 dlsym(_fiu_libc, "open");
+		libc_symbol("open");
+
+	_fiu_in_init_open--;
 	rec_dec();
 }
 
@@ -56,13 +58,23 @@ int open(const char *pathname, int flags, ...)
 		mode = 0;
 	}
 
+	/* Differences from the generated code end here */
+
 	if (_fiu_called) {
+		if (_fiu_orig_open == NULL) {
+			if (_fiu_in_init_open) {
+				printd("fail on init\n");
+				return -1;
+			} else {
+				printd("get orig\n");
+				_fiu_init_open();
+			}
+		}
+
 		printd("orig\n");
 		return (*_fiu_orig_open) (pathname, flags, mode);
 	}
 
-	/* Differences from the generated code end here */
-
 	printd("fiu\n");
 
 	/* fiu_fail() may call anything */
diff --git a/preload/posix/modules/posix.io.mod b/preload/posix/modules/posix.io.mod
index 4d31077..0f4d4a5 100644
--- a/preload/posix/modules/posix.io.mod
+++ b/preload/posix/modules/posix.io.mod
@@ -11,6 +11,7 @@ include: <poll.h>
 include: <fcntl.h>
 include: <errno.h>
 
+
 fiu name base: posix/io/oc/
 
 # open() has its own custom wrapper
@@ -111,31 +112,32 @@ int rename(const char *oldpath, const char *newpath);
 		ENOENT ENOMEM ENOSPC ENOTDIR ENOTEMPTY EPERM EROFS EXDEV
 
 
-# NOTE: These are disabled because the stat family function is usually defined
-# within one of the standard headers, which (besides being ugly) makes
-# overriding them this way much harder. The definitions below are commented
-# out for reference and testing purposes.
+# NOTE: These are commented because stat() and friends are usually defined as
+# either macros or inline functions within one of the standard headers, which
+# makes overriding them this way much harder.
 #
 #fiu name base: posix/io/stat/
 #
-#include: <sys/types.h>
-#include: <sys/stat.h>
-#include: <unistd.h>
-#
+#v: #ifndef stat
 #int stat(const char *path, struct stat *buf);
 #	on error: -1
 #	valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \
 #		ENOTDIR EOVERFLOW
+#v: #endif
 #
+#v: #ifndef fstat
 #int fstat(int fd, struct stat *buf);
 #	on error: -1
 #	valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \
 #		ENOTDIR EOVERFLOW
+#v: #endif
 #
+#v: #ifndef lstat
 #int lstat(const char *path, struct stat *buf);
 #	on error: -1
 #	valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \
 #		ENOTDIR EOVERFLOW
+#v: #endif
 
 
 fiu name base: posix/io/net/
diff --git a/tests/Makefile b/tests/Makefile
index 9fb7875..41d63d3 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -14,21 +14,18 @@ endif
 ifneq ($(V), 1)
 	NICE_CC = @echo "  CC  $@"; $(CC)
 	NICE_RUN = @echo "  RUN $<"; LD_LIBRARY_PATH=../libfiu/
-	NICE_PY_RUN = @echo "  PY  $<"; \
-		      LD_LIBRARY_PATH=../libfiu/ \
-		      PYTHONPATH=../bindings/python/
+	NICE_PY = @echo "  PY  $<"; ./wrap-python 2
 else
 	NICE_CC = $(CC)
 	NICE_RUN = LD_LIBRARY_PATH=../libfiu/
-	NICE_PY_RUN = LD_LIBRARY_PATH=../libfiu/ \
-		      PYTHONPATH=../bindings/python/
+	NICE_PY = ./wrap-python 2
 endif
 
 default: tests
 
 all: tests
 
-tests: c-tests py-tests
+tests: c-tests py-tests gen-tests
 
 #
 # C tests
@@ -69,15 +66,27 @@ PY_TESTS := $(wildcard test-*.py)
 py-tests: $(patsubst %.py,py-run-%,$(PY_TESTS))
 
 py-run-%: %.py
-	$(NICE_PY_RUN) python ./$<
+	$(NICE_PY) ./$<
 
+#
+# Generated tests
+#
+
+gen-tests:
+	$(MAKE) -C generated
+
+
+#
+# Cleanup
+#
 
+# Normally, $C_OBJS and $C_BINS are removed by make after building,
+# since here they're considered "intermediate files"; however we
+# also remove them when cleaning just in case.
 clean:
-	# Normally, $C_OBJS and $C_BINS are removed by make after building,
-	# since here they're considered "intermediate files"; however we
-	# also remove them when cleaning just in case.
 	rm -f $(C_OBJS) $(C_BINS)
 	rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out build-flags
+	$(MAKE) -C generated clean
 
 FORCE:
 
diff --git a/tests/generated/Makefile b/tests/generated/Makefile
new file mode 100644
index 0000000..201f58b
--- /dev/null
+++ b/tests/generated/Makefile
@@ -0,0 +1,79 @@
+
+CFLAGS += -std=c99 -pedantic -Wall -rdynamic
+ALL_CFLAGS = -I../../libfiu/ -L../../libfiu/ \
+	-D_XOPEN_SOURCE=600 -D_GNU_SOURCE -fPIC -DFIU_ENABLE=1 $(CFLAGS)
+
+ifdef DEBUG
+ALL_CFLAGS += -g
+endif
+
+ifdef PROFILE
+ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage
+endif
+
+ifneq ($(V), 1)
+	NICE_CC = @echo "  CC  $@"; $(CC)
+	NICE_RUN = @echo "  RUN $<"; \
+		   LD_LIBRARY_PATH=../../libfiu/ \
+		   LD_PRELOAD="../../preload/run/fiu_run_preload.so \
+		   	../../preload/posix/fiu_posix_preload.so"
+	NICE_GEN = @echo "  GEN $@"; ./generate-test
+else
+	NICE_CC = $(CC)
+	NICE_RUN = LD_LIBRARY_PATH=../../libfiu/ \
+		   LD_PRELOAD="../../preload/run/fiu_run_preload.so \
+		   	../../preload/posix/fiu_posix_preload.so"
+	NICE_GEN = ./generate-test
+endif
+
+default: tests
+
+all: tests
+
+
+CONF := $(wildcard tests/*.conf)
+GEN_BIN := $(patsubst %.conf,%.bin,$(CONF))
+
+tests: $(patsubst %,%-run,$(GEN_BIN))
+
+%-run: %
+	$(NICE_RUN) ./$<
+
+# .bin from .c
+%.bin: %.c build-flags
+	$(NICE_CC) $(ALL_CFLAGS) $< -lfiu -o $@
+
+# .c from .conf
+%.c: %.conf generate-test
+	$(NICE_GEN) -c $< -o $@
+
+# Useful for manually generating the C-files, that otherwise get deleted as
+# intermediates.
+c-files: $(patsubst %.conf,%.c,$(CONF))
+
+
+BF = $(ALL_CFLAGS) ~ $(PREFIX)
+build-flags: .force-build-flags
+	@if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \
+		if [ -f build-flags ]; then \
+			echo "build flags changed, rebuilding"; \
+		fi; \
+		echo "$(BF)" > build-flags; \
+	fi
+
+
+#
+# Cleanup
+#
+
+clean:
+	rm -f $(GEN_BIN) $(patsubst %.bin,%.c,$(GEN_BIN))
+	rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out build-flags
+
+FORCE:
+
+.PHONY: default all clean \
+	tests c-tests py-tests \
+	.force-build-flags
+
+
diff --git a/tests/generated/README b/tests/generated/README
new file mode 100644
index 0000000..6c672c1
--- /dev/null
+++ b/tests/generated/README
@@ -0,0 +1,18 @@
+
+These are autogenerated tests for testing the posix preload, to make sure the
+functions are getting properly wrapped.
+
+Each test is configured by a .conf file in the conf/ directory.
+
+These are the allowed options ('-' means mandatory, '*' optional):
+ - fp: failure point corresponding to the function.
+ - call: how to call the function.
+ * prep: preparation steps (usually declaring variables).
+ * include: list of files to #include, space separated.
+ * success_cond: condition we expect to be true if the call succeeds.
+ * failure_cond: condition we expect to be true after we simulate a failure
+	(if not set, we will only validate that we simulated the failure).
+ * errno_on_fail: simulate this errno on failure.
+
+It is mandatory that a single section is defined; however, the name is only
+for description purposes.
diff --git a/tests/generated/generate-test b/tests/generated/generate-test
new file mode 100755
index 0000000..8d5dae8
--- /dev/null
+++ b/tests/generated/generate-test
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+
+"""
+Generate C testcases based on the given configuration file.
+
+It can be useful for testing many C functions in the same way, like when
+testing a wrapper.
+"""
+
+import sys
+import ConfigParser
+import argparse
+from string import Template
+
+
+def listify(x):
+	if isinstance(x, (str, unicode)):
+		return [x]
+	return x
+
+
+TEST_SUCCESS_TMPL = r"""
+int success(void) {
+	$prep
+
+	fiu_disable("$fp");
+
+	$call
+
+	if (! ($success_cond) ) {
+		printf("$fp - success condition is false\n");
+		return -1;
+	}
+
+	return 0;
+}
+"""
+
+TEST_FAILURE_TMPL = r"""
+static int external_cb_was_called = 0;
+int external_cb(const char *name, int *failnum,
+		void **failinfo, unsigned int *flags) {
+	external_cb_was_called++;
+
+	*failinfo = (void *) $errno_on_fail;
+
+	return *failnum;
+}
+
+int failure(void) {
+	$prep
+
+	fiu_enable_external("$fp", 1, NULL, 0, external_cb);
+
+	$call
+
+	fiu_disable("$fp");
+
+	if (external_cb_was_called != 1) {
+		printf("$fp - external callback not invoked\n");
+		return -1;
+	}
+
+	if (! ($errno_cond) ) {
+		printf("$fp - errno not set appropriately: ");
+		printf("errno:%d, cond:$errno_cond\n", errno);
+		return -1;
+	}
+
+	if (! ($failure_cond) ) {
+		printf("$fp - failure condition is false\n");
+		return -1;
+	}
+
+	return 0;
+}
+"""
+
+TEST_MAIN_TMPL = r"""
+int main(void) {
+	int s, f;
+
+	s = success();
+	f = failure();
+
+	return s + f;
+}
+"""
+
+TEST_SKIPPED_TMPL = r"""
+int main(void) {
+	printf("$fp: skipping test\n");
+	return 0;
+}
+"""
+
+def generate(options, outfile):
+	outfile.write("/* AUTOGENERATED FILE - DO NOT EDIT */\n\n")
+
+	outfile.write("#include <fiu.h>\n")
+	outfile.write("#include <fiu-control.h>\n")
+	outfile.write("#include <stdio.h>\n")
+	outfile.write("#include <errno.h>\n")
+
+	includes = options.get("include", [])
+	if isinstance(includes, (str, unicode)):
+		includes = includes.split()
+	for i in includes:
+		outfile.write("#include <%s>\n" % i)
+	else:
+		outfile.write("\n\n")
+
+	if options['ifdef']:
+		outfile.write("#ifdef %s\n" % options['ifdef'])
+
+	if 'errno_on_fail' in options:
+		options['errno_cond'] = \
+				'errno == %s' % options['errno_on_fail']
+	else:
+		# Default the cond to true, and set failinfo to 0 in case it's
+		# used.
+		options['errno_cond'] = '1'
+		options['errno_on_fail'] = '0'
+
+	outfile.write(Template(TEST_SUCCESS_TMPL).substitute(options))
+	outfile.write(Template(TEST_FAILURE_TMPL).substitute(options))
+	outfile.write(Template(TEST_MAIN_TMPL).substitute(options))
+
+	if options['ifdef']:
+		outfile.write("#else\n")
+		outfile.write(Template(TEST_SKIPPED_TMPL).substitute(options))
+		outfile.write("#endif\n")
+
+def main():
+	parser = argparse.ArgumentParser(
+			description ="Generate C testcases")
+	parser.add_argument("-c", "--conf", metavar = "C",
+			required = True, type = file,
+			help = "configuration file")
+	parser.add_argument("-o", "--out", metavar = "F",
+			required = True,
+			help = "generated file")
+
+	args = parser.parse_args()
+
+	# Defaults for the optional configuration parameters.
+	conf_defaults = {
+		'include': (),
+		'prep': '',
+
+		'ifdef': '',
+
+		# These are C conditions that are always true.
+		'success_cond': '1',
+		'failure_cond': '1',
+
+		# For errno_on_fail, we have a smarter logic so don't do
+		# anything.
+	}
+
+	conf = ConfigParser.SafeConfigParser(conf_defaults)
+	conf.readfp(args.conf)
+
+	section = conf.sections()[0]
+
+	outfile = open(args.out, 'w')
+	generate(dict(conf.items(section)), outfile)
+
+if __name__ == "__main__":
+	main()
diff --git a/tests/generated/tests/kill.conf b/tests/generated/tests/kill.conf
new file mode 100644
index 0000000..83961e3
--- /dev/null
+++ b/tests/generated/tests/kill.conf
@@ -0,0 +1,10 @@
+
+[kill]
+fp: posix/proc/kill
+include: sys/types.h signal.h
+prep: int r;
+call: r = kill(0, 0);
+success_cond: r != -1
+failure_cond: r == -1
+errno_on_fail: ESRCH
+
diff --git a/tests/generated/tests/malloc.conf b/tests/generated/tests/malloc.conf
new file mode 100644
index 0000000..db5f480
--- /dev/null
+++ b/tests/generated/tests/malloc.conf
@@ -0,0 +1,9 @@
+
+[malloc]
+fp: libc/mm/malloc
+include: stdlib.h
+prep: void *p = NULL;
+call: p = malloc(8000);
+success_cond: p != NULL
+failure_cond: p == NULL
+
diff --git a/tests/generated/tests/mmap.conf b/tests/generated/tests/mmap.conf
new file mode 100644
index 0000000..68af0ca
--- /dev/null
+++ b/tests/generated/tests/mmap.conf
@@ -0,0 +1,12 @@
+
+[mmap]
+fp: posix/mm/mmap
+include: sys/mman.h unistd.h
+ifdef: _POSIX_MAPPED_FILES
+prep: void *p;
+call: p = mmap(NULL, 4092, PROT_READ | PROT_WRITE,
+		MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+success_cond: p != MAP_FAILED
+failure_cond: p == MAP_FAILED
+errno_on_fail: ETXTBSY
+
diff --git a/tests/generated/tests/open.conf b/tests/generated/tests/open.conf
new file mode 100644
index 0000000..0bf367a
--- /dev/null
+++ b/tests/generated/tests/open.conf
@@ -0,0 +1,10 @@
+
+[open]
+fp: posix/io/oc/open
+include: sys/types.h sys/stat.h fcntl.h
+prep: int fd = -1;
+call: fd = open("/dev/null", O_RDONLY);
+success_cond: fd != -1
+failure_cond: fd == -1
+errno_on_fail: ELOOP
+
diff --git a/tests/test-enable_stack.c b/tests/test-enable_stack.c
index 588390c..e3e94f5 100644
--- a/tests/test-enable_stack.c
+++ b/tests/test-enable_stack.c
@@ -29,13 +29,18 @@ void func2(int should_fail)
 
 int main(void)
 {
+	int r;
+
 	fiu_init(0);
-	fiu_enable_stack("fp-1", 1, NULL, 0, (void *) &func2, -1);
+	r = fiu_enable_stack("fp-1", 1, NULL, 0, (void *) &func2, -1);
+	if (r != 0) {
+		printf("NOTE: fiu_enable_stack() failed, skipping test\n");
+		return 0;
+	}
 
 	func1(0);
 	func2(1);
 
-
 	fiu_disable("fp-1");
 
 	func1(0);
diff --git a/tests/test-enable_stack_by_name.c b/tests/test-enable_stack_by_name.c
index 78c56ce..0968aba 100644
--- a/tests/test-enable_stack_by_name.c
+++ b/tests/test-enable_stack_by_name.c
@@ -29,13 +29,19 @@ void func2(int should_fail)
 
 int main(void)
 {
+	int r;
+
 	fiu_init(0);
-	fiu_enable_stack_by_name("fp-1", 1, NULL, 0, "func2", -1);
+	r = fiu_enable_stack_by_name("fp-1", 1, NULL, 0, "func2", -1);
+	if (r != 0) {
+		printf("NOTE: fiu_enable_stack_by_name() failed, "
+				"skipping test\n");
+		return 0;
+	}
 
 	func1(0);
 	func2(1);
 
-
 	fiu_disable("fp-1");
 
 	func1(0);
diff --git a/tests/wrap-python b/tests/wrap-python
new file mode 100755
index 0000000..aab0667
--- /dev/null
+++ b/tests/wrap-python
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+
+# Python wrapper, which makes it able to import the built (and not the
+# installed) version of libfiu.
+#
+# The first parameter must be the python version (2 or 3)
+
+import sys
+import os
+import glob
+
+
+if len(sys.argv) < 2 or sys.argv[1] not in ("2", "3"):
+	sys.stderr.write("Error: the first argument must be the " +
+				"version (2 or 3)\n")
+	sys.exit(1)
+
+py_ver = sys.argv[1]
+
+
+# Find the path where the library was built and add it to the lookup paths, so
+# we run against it
+lib_bin = os.path.dirname(sys.argv[0]) + "/../libfiu/libfiu.so"
+
+if not os.path.exists(lib_bin):
+	sys.stderr.write("Can't find library (run make)\n")
+	sys.exit(1)
+
+lib_path = os.path.dirname(os.path.abspath(lib_bin))
+os.environ["LD_LIBRARY_PATH"] = ":".join([lib_path, \
+				os.environ.get("LD_LIBRARY_PATH", "")])
+
+
+# Find out the corresponding module path for the desired python version. The
+# path must be absolute
+mod_bins = glob.glob(os.path.dirname(sys.argv[0]) +
+			"/../bindings/python/build/lib*-%s.*/fiu_ll.so" \
+				% py_ver)
+if not mod_bins:
+	sys.stderr.write(("Can't find python%s bindings, run " +
+				"make python%s\n") % (py_ver, py_ver))
+	sys.exit(1)
+
+if len(mod_bins) > 1:
+	sys.stderr.write("Found too many matching python%s bindings" \
+				% py_ver)
+	sys.exit(1)
+
+mod_path = os.path.dirname(os.path.abspath(mod_bins[0]))
+os.environ["PYTHONPATH"] = ":".join([mod_path,
+					os.environ.get("PYTHONPATH", "")])
+
+if py_ver == '2':
+	py_bin = "python"
+else:
+	py_bin = "python3"
+
+os.execvp(py_bin, [py_bin] + sys.argv[2:])
+
