From: Casper Hornstrup Date: Sat, 2 Oct 2004 08:44:54 +0000 (+0000) Subject: 2004-10-02 Casper S. Hornstrup X-Git-Tag: backups/alex_2gb+hdrtests@12432~47 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=9495493fde1d3823ff6555f562a17876b1e7fc54 2004-10-02 Casper S. Hornstrup * 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 --- diff --git a/reactos/ChangeLog b/reactos/ChangeLog index 342bc546976..b15df4fc1a4 100644 --- a/reactos/ChangeLog +++ b/reactos/ChangeLog @@ -1,3 +1,15 @@ +2004-10-02 Casper S. Hornstrup + + * 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 * lib/msafd/makefile (TARGET_CFLAGS): Don't define DBG. diff --git a/reactos/lib/gdiplus/tests/.cvsignore b/reactos/lib/gdiplus/tests/.cvsignore index 576b35ee7b0..f234ac5d0ba 100644 --- a/reactos/lib/gdiplus/tests/.cvsignore +++ b/reactos/lib/gdiplus/tests/.cvsignore @@ -1,5 +1,7 @@ +_hooks.c _regtests.c _rtstub.c +_stubs.S Makefile.tests *.d *.o diff --git a/reactos/lib/gdiplus/tests/Makefile b/reactos/lib/gdiplus/tests/Makefile index 9149f8a0f54..b82159efb8c 100644 --- a/reactos/lib/gdiplus/tests/Makefile +++ b/reactos/lib/gdiplus/tests/Makefile @@ -15,20 +15,23 @@ TARGET_CFLAGS = \ -D__USE_W32API \ -DWINVER=0x0600 \ -D_WIN32_WINNT=0x0501 \ - -I$(REGTESTS_PATH_INC) + -I$(REGTESTS_PATH_INC) -include Makefile.tests 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 diff --git a/reactos/lib/gdiplus/tests/passthrough.c b/reactos/lib/gdiplus/tests/passthrough.c deleted file mode 100644 index be7ccab5b5a..00000000000 --- a/reactos/lib/gdiplus/tests/passthrough.c +++ /dev/null @@ -1,49 +0,0 @@ -#include -#define NTOS_MODE_USER -#include -#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; -} diff --git a/reactos/lib/gdiplus/tests/stubs.tst b/reactos/lib/gdiplus/tests/stubs.tst new file mode 100644 index 00000000000..512ef0874b1 --- /dev/null +++ b/reactos/lib/gdiplus/tests/stubs.tst @@ -0,0 +1,3 @@ +ntdll.dll RtlAllocateHeap@12 +ntdll.dll RtlFreeHeap@12 +msvcrt.dll printf diff --git a/reactos/lib/gdiplus/tests/tests/test-1.c b/reactos/lib/gdiplus/tests/tests/test-1.c index 60a12d4b01e..ec3e11e5272 100644 --- a/reactos/lib/gdiplus/tests/tests/test-1.c +++ b/reactos/lib/gdiplus/tests/tests/test-1.c @@ -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; } diff --git a/reactos/regtests/shared/regtests.h b/reactos/regtests/shared/regtests.h index 631c84ecb81..03620c04b58 100755 --- a/reactos/regtests/shared/regtests.h +++ b/reactos/regtests/shared/regtests.h @@ -7,6 +7,7 @@ * 06-07-2003 CSH Created */ #include +#include #include /* 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); +} diff --git a/reactos/tools/helper.mk b/reactos/tools/helper.mk index 1f92c993dc5..c1ea98c4e97 100644 --- a/reactos/tools/helper.mk +++ b/reactos/tools/helper.mk @@ -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 diff --git a/reactos/tools/regtests.c b/reactos/tools/regtests.c index 14921ea4d55..1a56c1dfbe2 100755 --- a/reactos/tools/regtests.c +++ b/reactos/tools/regtests.c @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef WIN32 #include @@ -20,7 +21,6 @@ #include #include #endif -#include #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 \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"; + " 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" + " stublistfile File with descriptions of stubs\n" + " stubsfile File with stubs to create\n" + " hooksfile File with hooks to create\n"; + +#define INPUT_BUFFER_SIZE 255 + +void +write_stubs_header(FILE * out) +{ + fputs(STUBS_HEADER, out); +} -int main(int argc, +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); + } +}