//
// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// interpose.c --
//
// Author           : Thierry Delisle
// Created On       : Wed Mar 29 16:10:31 2017
// Last Modified By : Peter A. Buhr
// Last Modified On : Fri Jul 21 22:27:33 2017
// Update Count     : 1
//

#include <stdarg.h>
#include <stddef.h>

extern "C" {
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>
}

#include "libhdr/libdebug.h"
#include "libhdr/libtools.h"
#include "startup.h"

void interpose_startup(void)  __attribute__(( constructor( STARTUP_PRIORITY_CORE ) ));

typedef void (*generic_fptr_t)(void);
generic_fptr_t interpose_symbol( const char* symbol, const char *version ) {
	const char * error;

	static void * library;
	if ( ! library ) {
		#if defined( RTLD_NEXT )
			library = RTLD_NEXT;
		#else
			// missing RTLD_NEXT => must hard-code library name, assuming libstdc++
			library = dlopen( "libc.so.6", RTLD_LAZY );
			error = dlerror();
			if ( error ) {
				abortf( "interpose_symbol : failed to open libc, %s\n", error );
			}
		#endif
	} // if

	union { generic_fptr_t fptr; void* ptr; } originalFunc;

	#if defined( _GNU_SOURCE )
		if ( version ) {
			originalFunc.ptr = dlvsym( library, symbol, version );
		} else {
			originalFunc.ptr = dlsym( library, symbol );
		}
	#else
		originalFunc.ptr = dlsym( library, symbol );
	#endif // _GNU_SOURCE

	error = dlerror();
	if ( error ) abortf( "interpose_symbol : internal error, %s\n", error );

	return originalFunc.fptr;
}


__typeof__( exit ) libc_exit __attribute__(( noreturn ));
__typeof__( abort ) libc_abort __attribute__(( noreturn ));

// #define INIT_REALRTN( x, ver ) libc_##x = (__typeof__(libc_##x))interpose_symbol( #x, ver )

forall(dtype T)
static inline void assign_ptr( T** symbol_ptr, const char * symbol_name, const char * version) {
	union {
		generic_fptr_t gp;
		T* tp;
	} u;

	u.gp = interpose_symbol( symbol_name, version );

	*symbol_ptr = u.tp;
}

#define INIT_REALRTN( x, ver ) assign_ptr( (void**)&libc_##x, #x, ver)

void interpose_startup() {
	const char *version = NULL;

	INIT_REALRTN( abort, version );
	INIT_REALRTN( exit, version );
}

extern "C" {
	void abort (void) __attribute__ ((__nothrow__, __leaf__, __noreturn__)) {
		abortf( NULL );
	}

	void exit (int __status) __attribute__ ((__nothrow__, __leaf__, __noreturn__)) {
		libc_exit(__status);
	}
}

void abort( const char *fmt, va_list argp ) __attribute__ ((__nothrow__, __leaf__, __noreturn__)) {
	abortf( fmt, argp );
}

void * kernel_abort    (void) __attribute__ ((__nothrow__, __leaf__, __weak__)) { return NULL; }
void   kernel_abort_msg(void * data, char * buffer, int size) __attribute__ ((__nothrow__, __leaf__, __weak__)) {}

enum { abort_text_size = 1024 };
static char abort_text[ abort_text_size ];

extern "C" {
	void abortf( const char fmt[], ... ) __attribute__ ((__nothrow__, __leaf__, __noreturn__)) {
		void * kernel_data = kernel_abort();

		int len;

		if( fmt ) {
			va_list args;
			va_start( args, fmt );

			len = vsnprintf( abort_text, abort_text_size, fmt, args );

			va_end( args );

			__lib_debug_write( STDERR_FILENO, abort_text, len );
			__lib_debug_write( STDERR_FILENO, "\n", 1 );
		}

		len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld)\n", (long int)getpid() ); // use UNIX pid (versus getPid)
		__lib_debug_write( STDERR_FILENO, abort_text, len );


		kernel_abort_msg( kernel_data, abort_text, abort_text_size );

		libc_abort();
	}
}

// Local Variables: //
// mode: c //
// tab-width: 4 //
// End: //
