2004-10-02 Casper S. Hornstrup <chorns@users.sourceforge.net>

* lib/gdiplus/tests/.cvsignore: Ignore _hooks.c and _stubs.S.
	* lib/gdiplus/tests/Makefile (TARGET_OBJECTS): Remove passthrough.o and
	add _hooks.o and _stubs.o.
	* lib/gdiplus/tests/tests/test-1.c: Test API hooking.
	* regtests/shared/regtests.h: Add support for API hooking.
	* tools/helper.mk: Generate stubs.
	* tools/regtests.c: Add support for generating stubs and hooks.
	* lib/gdiplus/tests/passthrough.c: Remove.
	* lib/gdiplus/tests/stubs.tst: New file.

svn path=/trunk/; revision=11147
This commit is contained in:
Casper Hornstrup 2004-10-02 08:44:54 +00:00
parent d12bb0393c
commit 9495493fde
9 changed files with 371 additions and 62 deletions

View file

@ -1,3 +1,15 @@
2004-10-02 Casper S. Hornstrup <chorns@users.sourceforge.net>
* lib/gdiplus/tests/.cvsignore: Ignore _hooks.c and _stubs.S.
* lib/gdiplus/tests/Makefile (TARGET_OBJECTS): Remove passthrough.o and
add _hooks.o and _stubs.o.
* lib/gdiplus/tests/tests/test-1.c: Test API hooking.
* regtests/shared/regtests.h: Add support for API hooking.
* tools/helper.mk: Generate stubs.
* tools/regtests.c: Add support for generating stubs and hooks.
* lib/gdiplus/tests/passthrough.c: Remove.
* lib/gdiplus/tests/stubs.tst: New file.
2004-09-23 Casper S. Hornstrup <chorns@users.sourceforge.net>
* lib/msafd/makefile (TARGET_CFLAGS): Don't define DBG.

View file

@ -1,5 +1,7 @@
_hooks.c
_regtests.c
_rtstub.c
_stubs.S
Makefile.tests
*.d
*.o

View file

@ -21,14 +21,17 @@ TARGET_CFLAGS = \
TARGET_OBJECTS = \
_regtests.o \
passthrough.o \
_hooks.o \
_stubs.o \
$(addprefix tests/, $(TESTS))
include $(PATH_TO_TOP)/rules.mak
include $(TOOLS_PATH)/helper.mk
LIBS = ../gdiplus.a
run: all
@$(CC) -o _runtest.exe _rtstub.o regtests.a $(SDK_PATH_LIB)/rtshared.a ../gdiplus.a
@$(CC) -o _runtest.exe _rtstub.o regtests.a $(SDK_PATH_LIB)/rtshared.a $(LIBS)
@_runtest.exe
@$(RM) _runtest.exe

View file

@ -1,49 +0,0 @@
#include <windows.h>
#define NTOS_MODE_USER
#include <ntos.h>
#include "regtests.h"
static PVOID
GetFunction(LPSTR FileName,
LPSTR FunctionName)
{
HMODULE hModule;
PVOID Function;
hModule = GetModuleHandleA(FileName);
if (hModule != NULL)
{
Function = GetProcAddress(hModule, FunctionName);
}
else
{
hModule = LoadLibraryA(FileName);
if (hModule != NULL)
{
Function = GetProcAddress(hModule, FunctionName);
//FreeLibrary(hModule);
}
}
return Function;
}
typedef PVOID STDCALL (*RTL_ALLOCATE_HEAP)(PVOID a1, ULONG a2, ULONG a3);
PVOID STDCALL
RtlAllocateHeap(PVOID a1,
ULONG a2,
ULONG a3)
{
RTL_ALLOCATE_HEAP p;
p = GetFunction("ntdll.dll", "RtlAllocateHeap");
return p(a1, a2, a3);
}
BOOLEAN STDCALL
RtlFreeHeap(
HANDLE heap,
ULONG flags,
PVOID ptr)
{
return TRUE;
}

View file

@ -0,0 +1,3 @@
ntdll.dll RtlAllocateHeap@12
ntdll.dll RtlFreeHeap@12
msvcrt.dll printf

View file

@ -3,9 +3,36 @@
#include "regtests.h"
BOOL
ReturnTrue()
{
return TRUE;
}
static BOOL MyRtlFreeHeapCalled = FALSE;
VOID STDCALL
MyRtlFreeHeap(ULONG a1, ULONG a2, ULONG a3)
{
MyRtlFreeHeapCalled = TRUE;
}
extern VOID STDCALL
RtlFreeHeap(ULONG a1, ULONG a2, ULONG a3);
HOOK Hooks[] =
{
{"RtlFreeHeap", MyRtlFreeHeap}
};
static int
RunTest(char *Buffer)
{
_SetHooks(Hooks);
RtlFreeHeap(0,0,0);
FAIL_IF_FALSE(MyRtlFreeHeapCalled, "RtlFreeHeap() must be called.");
FAIL_IF_FALSE(ReturnTrue(), "ReturnTrue() must always return TRUE.");
return TS_OK;
}

View file

@ -7,6 +7,7 @@
* 06-07-2003 CSH Created
*/
#include <stdio.h>
#include <string.h>
#include <windows.h>
/* Valid values for Command parameter of TestRoutine */
@ -84,3 +85,103 @@ extern VOID PerformTests(TestOutputRoutine OutputRoutine, LPSTR TestName);
/* Routines provided by the driver */
extern PVOID AllocateMemory(ULONG Size);
extern VOID FreeMemory(PVOID Base);
typedef struct _API_DESCRIPTION
{
PCHAR FileName;
PCHAR FunctionName;
PVOID FunctionAddress;
PVOID MockFunctionAddress;
} API_DESCRIPTION, *PAPI_DESCRIPTION;
extern API_DESCRIPTION ExternalDependencies[];
extern ULONG MaxExternalDependency;
static inline PVOID
FrameworkGetFunction(PAPI_DESCRIPTION ApiDescription)
{
HMODULE hModule;
PVOID Function;
hModule = GetModuleHandleA(ApiDescription->FileName);
if (hModule != NULL)
{
Function = GetProcAddress(hModule, ApiDescription->FunctionName);
}
else
{
hModule = LoadLibraryA(ApiDescription->FileName);
if (hModule != NULL)
{
Function = GetProcAddress(hModule, ApiDescription->FunctionName);
//FreeLibrary(hModule);
}
}
return Function;
}
static inline PVOID STDCALL
FrameworkGetHookInternal(ULONG index)
{
PVOID address;
if (index > MaxExternalDependency)
return NULL;
if (ExternalDependencies[index].MockFunctionAddress != NULL)
return ExternalDependencies[index].MockFunctionAddress;
if (ExternalDependencies[index].FunctionAddress != NULL)
return ExternalDependencies[index].FunctionAddress;
address = FrameworkGetFunction(&ExternalDependencies[index]);
ExternalDependencies[index].FunctionAddress = address;
return address;
}
static inline VOID
_SetHook(PCHAR name,
PVOID address)
{
PAPI_DESCRIPTION api;
ULONG index;
for (index = 0; index <= MaxExternalDependency; index++)
{
api = &ExternalDependencies[index];
if (strcmp(api->FunctionName, name) == 0)
{
api->FunctionAddress = address;
return;
}
}
}
typedef struct _HOOK
{
PCHAR FunctionName;
PVOID FunctionAddress;
} HOOK, *PHOOK;
static inline VOID
_SetHooks(PHOOK hookTable)
{
PHOOK hook;
hook = &hookTable[0];
_SetHook(hook->FunctionName,
hook->FunctionAddress);
}
static inline VOID
_UnsetHooks(PHOOK hookTable)
{
PHOOK hook;
hook = &hookTable[0];
_SetHook(hook->FunctionName,
NULL);
}

View file

@ -1,4 +1,4 @@
# $Id: helper.mk,v 1.80 2004/09/16 10:25:17 gvg Exp $
# $Id: helper.mk,v 1.81 2004/10/02 08:44:54 chorns Exp $
#
# Helper makefile for ReactOS modules
# Variables this makefile accepts:
@ -977,6 +977,7 @@ $(REGTEST_TARGETS): $(REGTEST_TESTS)
ifeq ($(MK_MODE),user)
ifeq ($(TARGET_BUILDENV_TEST),yes)
$(REGTESTS) ./tests/tests ./tests/_regtests.c ./tests/Makefile.tests -e ./tests/_rtstub.c
$(REGTESTS) -s ./tests/stubs.tst ./tests/_stubs.S ./tests/_hooks.c
else
$(REGTESTS) ./tests/tests ./tests/_regtests.c ./tests/Makefile.tests -u ./tests/_rtstub.c
endif

View file

@ -9,6 +9,7 @@
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef WIN32
#include <io.h>
@ -20,7 +21,6 @@
#include <dirent.h>
#include <unistd.h>
#endif
#include <ctype.h>
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
@ -556,17 +556,208 @@ static char EXESTUB[] =
" return 0;\n"
"}\n";
static char STUBS_HEADER[] =
"/* This file is autogenerated. */\n"
"passthrough:\n"
" call _FrameworkGetHook@4\n"
" test %eax, %eax\n"
" je .return\n"
" jmp *%eax\n"
".return:\n"
" /* This will most likely corrupt the stack */\n"
" ret\n"
"\n";
static char HOOKS_HEADER[] =
"/* This file is autogenerated. */\n"
"#include <windows.h>\n"
"#include \"regtests.h\"\n"
"\n"
"API_DESCRIPTION ExternalDependencies[] =\n"
"{\n";
static char HOOKS_FOOTER[] =
"};\n"
"\n"
"#define ExternalDependencyCount %d\n"
"ULONG MaxExternalDependency = ExternalDependencyCount - 1;\n"
"\n"
"PVOID STDCALL\n"
"FrameworkGetHook(ULONG index)\n"
"{\n"
" return FrameworkGetHookInternal(index);\n"
"}\n";
static char HELP[] =
"REGTESTS path file makefile [-u umstubfile] [-k kmstubfile] [-e exestubfile]\n"
"REGTESTS -s stublistfile stubsfile hooksfile\n"
"\n"
" path Path to files\n"
" file Registration file to create\n"
" makefile Makefile to create\n"
" umstubfile Optional stub for running tests internal to a user-mode module\n"
" kmstubfile Optional stub for running tests internal to a kernel-mode module\n"
" exestubfile Optional stub for running tests internal to a module in the build environment\n";
" exestubfile Optional stub for running tests internal to a module in the build environment\n"
" stublistfile File with descriptions of stubs\n"
" stubsfile File with stubs to create\n"
" hooksfile File with hooks to create\n";
int main(int argc,
#define INPUT_BUFFER_SIZE 255
void
write_stubs_header(FILE * out)
{
fputs(STUBS_HEADER, out);
}
void
write_hooks_header(FILE * out)
{
fputs(HOOKS_HEADER, out);
}
void
write_hooks_footer(FILE *hooks_out, unsigned long nr_stubs)
{
fprintf(hooks_out, HOOKS_FOOTER, nr_stubs);
}
char *
get_undecorate_name(char *buf,
char *decoratedname)
{
int start = 0;
int end = 0;
while (start < strlen(decoratedname) && decoratedname[start] == '@')
{
start++;
}
strcpy(buf, &decoratedname[start]);
end = strlen(buf) - 1;
while (end > 0 && isdigit(buf[end]))
{
end--;
}
if (buf[end] == '@')
{
buf[end] = 0;
}
return buf;
}
void
write_stub(FILE *stubs_out, FILE *hooks_out, char *dllname,
char *decoratedname, unsigned int stub_index)
{
char buf[300];
fprintf(stubs_out, ".globl _%s\n", decoratedname);
fprintf(stubs_out, "_%s:\n", decoratedname);
fprintf(stubs_out, " pushl $%d\n", stub_index);
fprintf(stubs_out, " jmp passthrough\n");
fprintf(stubs_out, "\n");
fprintf(hooks_out, " {\"%s\", \"%s\", NULL, NULL},\n",
dllname, get_undecorate_name(buf, decoratedname));
}
void
create_stubs_and_hooks(
FILE *in,
FILE *stubs_out,
FILE *hooks_out)
{
char line[INPUT_BUFFER_SIZE];
char *s;
char *dllname;
char *decoratedname;
int stub_index;
write_stubs_header(stubs_out);
write_hooks_header(hooks_out);
/*
* Scan the database. The database is a text file; each
* line is a record, which contains data for one stub.
* Each record has two columns:
*
* DLLNAME (e.g. ntdll.dll)
* DECORATED NAME (e.g. NtCreateProcess@32, @InterlockedIncrement@4 or printf)
*/
for (
/* First stub has index zero */
stub_index = 0;
/* Go on until EOF or read zero bytes */
((!feof(in)) && (fgets(line, sizeof line, in) != NULL));
/* Next stub index */
stub_index++)
{
/*
* Remove, if present, the trailing CR.
* (os specific?)
*/
if ((s = (char *) strchr(line,'\r')) != NULL)
{
*s = '\0';
}
/*
* Skip comments (#) and empty lines.
*/
s = & line[0];
if ((*s) != '#' && (*s) != '\0')
{
/* Extract the DLL name */
dllname = (char *) strtok(s," \t");
/* Extract the decorated function name */
decoratedname = (char *) strtok(NULL," \t");
/* Extract the argument count */
write_stub(stubs_out, hooks_out, dllname, decoratedname, stub_index);
}
}
write_hooks_footer(hooks_out, stub_index + 1);
}
int run_stubs(int argc,
char **argv)
{
FILE *in;
FILE *stubs_out;
FILE *hooks_out;
in = fopen(argv[2], "rb");
if (in == NULL)
{
perror("Failed to open stub description input file");
return 1;
}
stubs_out = fopen(argv[3], "wb");
if (stubs_out == NULL)
{
perror("Failed to open stubs output file");
return 1;
}
hooks_out = fopen(argv[4], "wb");
if (hooks_out == NULL)
{
perror("Failed to open hooks output file");
return 1;
}
create_stubs_and_hooks(in, stubs_out, hooks_out);
fclose(stubs_out);
fclose(hooks_out);
return 0;
}
int run_registrations(int argc,
char **argv)
{
char buf[MAX_PATH];
@ -578,7 +769,6 @@ int main(int argc,
return 1;
}
strcpy(buf, convert_path(argv[1]));
if (buf[strlen(buf)] != DIR_SEPARATOR_CHAR)
{
@ -728,3 +918,22 @@ int main(int argc,
return 0;
}
int main(int argc,
char **argv)
{
if (argc < 2)
{
puts(HELP);
return 1;
}
if (strlen(argv[1]) > 1 && argv[1][0] == '-' && argv[1][1] == 's')
{
return run_stubs(argc, argv);
}
else
{
return run_registrations(argc, argv);
}
}