
/*
* KDUMA - Red-Zone memory allocator.
*
* Copyright (C) 2006 Michael Eddington <meddington@gmail.com>
* Copyright (C) 2002-2005 Hayati Ayguen <h_ayguen@web.de>, Procitec GmbH
* Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
*
* License: GNU GPL (GNU General Public License, see COPYING-GPL)
*
* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*
* FILE CONTENTS:
* --------------
* This is a small tool to generate the "duma_config.h" configuration file.
* Its purpose is to allow fixed size memory allocation on stack, which can
* get protected calling page protection functions.
* Also its nice for the user to be able to see the page size.
*/

#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <stdarg.h>

#ifdef _MSC_VER
#include <direct.h>
#endif

#ifndef WIN32
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#else
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <winbase.h>

typedef LPVOID caddr_t;
typedef unsigned u_int;
#endif


/*
* retrieve page size
* size_t  Page_Size(void)
*/
static size_t
Page_Size(void)
{
#if defined(WIN32)
	SYSTEM_INFO SystemInfo;
	GetSystemInfo( &SystemInfo );
	return (size_t)SystemInfo.dwPageSize;
#elif defined(_SC_PAGESIZE)
	return (size_t)sysconf(_SC_PAGESIZE);
#elif defined(_SC_PAGE_SIZE)
	return (size_t)sysconf(_SC_PAGE_SIZE);
#else
	/* extern int	getpagesize(); */
	return getpagesize();
#endif
}

int initattr_ok = 0;

void
#ifdef __GNUC__
__attribute ((constructor))
#endif
init_function(void)
{
	initattr_ok = 1;
}



typedef struct
{
	int  id;
	int  size;
	char *type;
}
DataType_T;


DataType_T  sIntTypes[] =
{
	{ 0, sizeof(char)     , "char"      }
	, { 1, sizeof(short int), "short int" }
	, { 2, sizeof(int)      , "int"       }
	, { 3, sizeof(long int) , "long int"  }
	/* hier zusaetzliche compiler-spezifische datentypen hinzufuegen */
#ifdef _MSC_VER
	, { 4, sizeof(__int64), "__int64"     }
#endif
#ifdef __GNUC__
	, { 5, sizeof(long long int), "long long int" }
#endif
};


/* test access to each data type */
void testAlignment(int addrIdx, char * buffer, int alignment, int max_sizeof)
{
	int offset;

	switch ( sIntTypes[addrIdx].id )
	{
	case 0:
#define TYPE unsigned char
		for(offset=0; offset < max_sizeof; ++offset)
		{
			TYPE addr = (TYPE)(buffer) + offset;
			if ( addr % alignment == 0 )
			{
				*( (unsigned char *)     addr ) = 0;
				*( (unsigned short int*) addr ) = 0;
				*( (unsigned int *)      addr ) = 0;
				*( (unsigned long int *) addr ) = 0L;
				*( (float *)             addr ) = 0.0F;
				*( (double *)            addr ) = 0.0;
				*( (long double *)       addr ) = 0.0;
#ifdef _MSC_VER
				*( (unsigned __int64 *) addr ) = 0L;
#endif
#ifdef __GNUC__
				*( (unsigned long long int *) addr ) = 0L;
#endif
			}
		}
		break;
	case 1:
#undef TYPE
#define TYPE unsigned short int
		for(offset=0; offset < max_sizeof; ++offset)
		{
			TYPE addr = (TYPE)(buffer) + offset;
			if ( addr % alignment == 0 )
			{
				*( (unsigned char *)     addr ) = 0;
				*( (unsigned short int*) addr ) = 0;
				*( (unsigned int *)      addr ) = 0;
				*( (unsigned long int *) addr ) = 0L;
				*( (float *)             addr ) = 0.0F;
				*( (double *)            addr ) = 0.0;
				*( (long double *)       addr ) = 0.0;
#ifdef _MSC_VER
				*( (unsigned __int64 *) addr ) = 0L;
#endif
#ifdef __GNUC__
				*( (unsigned long long int *) addr ) = 0L;
#endif
			}
		}
		break;
	case 2:
#undef TYPE
#define TYPE unsigned int
		for(offset=0; offset < max_sizeof; ++offset)
		{
			TYPE addr = (TYPE)(buffer) + offset;
			if ( addr % alignment == 0 )
			{
				*( (unsigned char *)     addr ) = 0;
				*( (unsigned short int*) addr ) = 0;
				*( (unsigned int *)      addr ) = 0;
				*( (unsigned long int *) addr ) = 0L;
				*( (float *)             addr ) = 0.0F;
				*( (double *)            addr ) = 0.0;
				*( (long double *)       addr ) = 0.0;
#ifdef _MSC_VER
				*( (unsigned __int64 *) addr ) = 0L;
#endif
#ifdef __GNUC__
				*( (unsigned long long int *) addr ) = 0L;
#endif
			}
		}
		break;
	case 3:
#undef TYPE
#define TYPE unsigned long int
		for(offset=0; offset < max_sizeof; ++offset)
		{
			TYPE addr = (TYPE)(buffer) + offset;
			if ( addr % alignment == 0 )
			{
				*( (unsigned char *)     addr ) = 0;
				*( (unsigned short int*) addr ) = 0;
				*( (unsigned int *)      addr ) = 0;
				*( (unsigned long int *) addr ) = 0L;
				*( (float *)             addr ) = 0.0F;
				*( (double *)            addr ) = 0.0;
				*( (long double *)       addr ) = 0.0;
#ifdef _MSC_VER
				*( (unsigned __int64 *) addr ) = 0L;
#endif
#ifdef __GNUC__
				*( (unsigned long long int *) addr ) = 0L;
#endif
			}
		}
		break;
#ifdef _MSC_VER
	case 4:
#undef TYPE
#define TYPE unsigned __int64
		for(offset=0; offset < max_sizeof; ++offset)
		{
			TYPE addr = (TYPE)(buffer) + offset;
			if ( addr % alignment == 0 )
			{
				*( (unsigned char *)     addr ) = 0;
				*( (unsigned short int*) addr ) = 0;
				*( (unsigned int *)      addr ) = 0;
				*( (unsigned long int *) addr ) = 0L;
				*( (float *)             addr ) = 0.0F;
				*( (double *)            addr ) = 0.0;
				*( (long double *)       addr ) = 0.0;
#ifdef _MSC_VER
				*( (unsigned __int64 *) addr ) = 0L;
#endif
#ifdef __GNUC__
				*( (unsigned long long int *) addr ) = 0L;
#endif
			}
		}
		break;
#endif
#ifdef __GNUC__
	case 5:
#undef TYPE
#define TYPE unsigned long long int
		for(offset=0; offset < max_sizeof; ++offset)
		{
			TYPE addr = (TYPE)(buffer) + offset;
			if ( addr % alignment == 0 )
			{
				*( (unsigned char *)     addr ) = 0;
				*( (unsigned short int*) addr ) = 0;
				*( (unsigned int *)      addr ) = 0;
				*( (unsigned long int *) addr ) = 0L;
				*( (float *)             addr ) = 0.0F;
				*( (double *)            addr ) = 0.0;
				*( (long double *)       addr ) = 0.0;
#ifdef _MSC_VER
				*( (unsigned __int64 *) addr ) = 0L;
#endif
#ifdef __GNUC__
				*( (unsigned long long int *) addr ) = 0L;
#endif
			}
		}
		break;
#endif
		;
	}
}



void writeFile(const char * filename, unsigned long pagesize, int addrIdx, int sizeIdx, int alignment)
{
	FILE * f = fopen(filename, "w");
	if (!f)
		return;

	fprintf(f, "/*\n");
	fprintf(f, " * WARNING: DO NOT CHANGE THIS FILE!\n");
	fprintf(f, " * This file is automatically generated from createconf\n");

#if defined(WIN32)
#if defined(_MSC_VER)
	fprintf(f, " * using Microsoft Visual C++ under MS Windows(TM) the %s\n", __DATE__ );
#else
	printf(f, " * under MS Windows(TM) the %s\n", __DATE__ );
#endif
#elif ( defined(sgi) )
	fprintf(f, " * under sgi the %s\n", __DATE__ );
#elif ( defined(_AIX) )
	fprintf(f, " * under AIX the %s\n", __DATE__ );
#elif ( defined(__linux__) )
	fprintf(f, " * under Linux the %s\n", __DATE__ );
#else
	fprintf(f, " * the %s \n", __DATE__ );
#endif

	fprintf(f, " */\n");
	fprintf(f, "\n");
	fprintf(f, "#ifndef _DUMA_CONFIG_H_\n");
	fprintf(f, "#define _DUMA_CONFIG_H_\n");

	fprintf(f, "\n");

	fprintf(f, "\n");
	fprintf(f, "/* Variable: DUMA_PAGE_SIZE\n");
	fprintf(f, " *\n");
	fprintf(f, " * Number of bytes per virtual-memory page, as returned by Page_Size().\n");
	fprintf(f, " */\n");
	fprintf(f, "#define DUMA_PAGE_SIZE %lu\n", pagesize);

	fprintf(f, "\n");
	fprintf(f, "/* Variable: DUMA_MIN_ALIGNMENT\n");
	fprintf(f, " *\n");
	fprintf(f, " * Minimum required alignment by CPU.\n");
	fprintf(f, " */\n");
	fprintf(f, "#define DUMA_MIN_ALIGNMENT %d\n", alignment);

	fprintf(f, "\n");
	fprintf(f, "/* Variable: DUMA_GNU_INIT_ATTR\n");
	fprintf(f, " *\n");
	fprintf(f, " * Build environment supports the '__attribute ((constructor))'?\n");
	fprintf(f, " */\n");
	if (initattr_ok)
		fprintf(f, "#define DUMA_GNU_INIT_ATTR 1\n");
	else
		fprintf(f, "/* #define DUMA_GNU_INIT_ATTR 0 */\n");

	fprintf(f, "\n");
	fprintf(f, "/* Type: DUMA_ADDR\n");
	fprintf(f, " *\n");
	fprintf(f, " * An integer type with same size as 'void *'\n");
	fprintf(f, " */\n");
	if (addrIdx >= 0)
		fprintf(f, "typedef unsigned %s DUMA_ADDR;\n", sIntTypes[addrIdx].type);
	else
		fprintf(f, "/* Error: No datatype for DUMA_ADDR found! */\n");

	fprintf(f, "\n");
	fprintf(f, "/* Type: DUMA_SIZE\n");
	fprintf(f, " *\n");
	fprintf(f, " * An integer type with same size as 'size_t'\n");
	fprintf(f, " */\n");
	if (sizeIdx >= 0)
		fprintf(f, "typedef unsigned %s DUMA_SIZE;\n", sIntTypes[sizeIdx].type);
	else
		fprintf(f, "/* No datatype for DUMA_SIZE found! */\n");

	fprintf(f, "\n");
	fprintf(f, "#endif /* _DUMA_CONFIG_H_ */\n");

	fclose(f);
}


int main()
{
	int iNumIntTypes, iIt;
	int addrIdx, sizeIdx;
	int alignment, max_sizeof;
	char  buffer[1024];
	char filename[1024];

	unsigned long pagesize = Page_Size();

	iNumIntTypes = sizeof( sIntTypes ) / sizeof( sIntTypes[0] );

	// detect compatible type for ADDRESS
	addrIdx = -1;
	for (iIt = 0; iIt < iNumIntTypes; ++iIt)
	{
		if ( sizeof(void*) == sIntTypes[iIt].size )
		{
			addrIdx = iIt;
			break;
		}
	}

	// detect compatible type for SIZE_T
	sizeIdx = -1;
	for (iIt = 0; iIt < iNumIntTypes; ++iIt)
	{
		if ( sizeof(size_t) == sIntTypes[iIt].size )
		{
			sizeIdx = iIt;
			break;
		}
	}

	/* detect maximum data type, which should be maximum alignment */
	max_sizeof = 0;
	for (iIt = 0; iIt < iNumIntTypes; ++iIt)
	{
		if ( max_sizeof < sIntTypes[iIt].size )
			max_sizeof = sIntTypes[iIt].size;
	}
	if ( max_sizeof < sizeof(float) )
		max_sizeof = sizeof(float);
	if ( max_sizeof < sizeof(double) )
		max_sizeof = sizeof(double);
	if ( max_sizeof < sizeof(long double) )
		max_sizeof = sizeof(long double);

#ifdef _MSC_VER
	{
		/* fix output path when started form Visual C++ IDE */
		char  directory[1024];
		char  * start;

		_getcwd( directory, 1024 ); /* get current directory */
		if ( ( start = strstr( directory, "win32-msvc" ) ) )
		{
			*start = 0;
			strcpy(filename, directory);
			strcat(filename, "duma_config.h");
		}
		else
			strcpy( filename, "duma_config.h" );
	}
#else
	strcpy( filename, "duma_config.h" );
#endif

	/* detect minimum alignment */
	alignment = max_sizeof;
	do
	{
		/* do the alignment access tests */
		testAlignment(addrIdx, buffer, alignment, max_sizeof);
		/* write whole config file. next test may crash ! */
		writeFile(filename, pagesize, addrIdx, sizeIdx, alignment);
		/* try next lower alignment */
		alignment >>= 1;
	}
	while ( alignment > 0 );

	return 0;
}
