git » libfiu » stop_at_exit » tree

[stop_at_exit] / preload / posix / codegen.c

#include <stdio.h>
#include <dlfcn.h>
#include <sys/time.h>
#include <stdlib.h>
#include "codegen.h"
#include "build-env.h"

/* Recursion counter, per-thread */
int __thread _fiu_called = 0;

/* Let the user know if there is no constructor priorities support, just in
 * case there are bugs when building/running without them */
#ifdef NO_CONSTRUCTOR_PRIORITIES
#warning "Building without using constructor priorities"
#endif

/* 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)
{
#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;

	if (_fiu_libc == NULL) {
		_fiu_libc = dlopen(LIBC_SONAME, RTLD_NOW);
		if (_fiu_libc == NULL) {
			fprintf(stderr, "Error loading libc: %s\n", dlerror());
			exit(1);
		}
	}

	return dlsym(_fiu_libc, symbol);
#endif
}

/* This runs after all function-specific constructors */
static void constructor_attr(250) _fiu_init_final(void)
{
	struct timeval tv;

	rec_inc();

	fiu_init(0);

	/* since we use random() in the wrappers, we need to seed it */
	gettimeofday(&tv, NULL);
	srandom(tv.tv_usec);

	rec_dec();
}

/* Stop failing things when atexit() begins.
 *
 * The first time we fail some function, we register the function below, so we
 * stop failing points at exiting time.
 *
 * TODO: explain why we want this.
 * TODO: explain why we do it this way (on the first failure).
 *  -> anything before main() is useless for gcov
 *  -> this is not 100% bullet proof, but chances are if you are failing
 *  points before main(), then you know what you're doing and will be less
 *  surprised if writing to coverage fails.
 *
 * This is not thread-safe, but it's harmless, as the worst that can happen is
 * that we run atexit() more than once.
 */
void _fiu_atexit(void)
{
	rec_inc();
}

static int _fiu_atexit_called = 0;

void _fiu_register_atexit(void)
{
	/* TODO: optionally (how? environment?) do not do this. */
	if (_fiu_atexit_called == 0) {
		_fiu_atexit_called++;
		printd("registering atexit\n");
		atexit(_fiu_atexit);
	}
}