diff --git a/modules/rostests/apitests/CMakeLists.txt b/modules/rostests/apitests/CMakeLists.txt index 752755a453f..abb0fb8209b 100644 --- a/modules/rostests/apitests/CMakeLists.txt +++ b/modules/rostests/apitests/CMakeLists.txt @@ -22,6 +22,11 @@ add_subdirectory(gdi32) add_subdirectory(gditools) add_subdirectory(iphlpapi) add_subdirectory(kernel32) +if(NOT GCC) + # gcc / binutils are unable to add an IMAGE_LOAD_CONFIG_DIRECTORY + # So maybe we should resuscitate sdk/tools/pefixup.c + add_subdirectory(loadconfig) +endif() add_subdirectory(localspl) add_subdirectory(mountmgr) add_subdirectory(msgina) diff --git a/modules/rostests/apitests/loadconfig/CMakeLists.txt b/modules/rostests/apitests/loadconfig/CMakeLists.txt new file mode 100644 index 00000000000..a09e73e90b1 --- /dev/null +++ b/modules/rostests/apitests/loadconfig/CMakeLists.txt @@ -0,0 +1,12 @@ + +list(APPEND SOURCE + common.c + stacktrace.c + loadconfig.h) + +add_executable(loadconfig_apitest ${SOURCE} testlist.c) +target_link_libraries(loadconfig_apitest wine ${PSEH_LIB}) +set_module_type(loadconfig_apitest win32cui) +add_importlibs(loadconfig_apitest msvcrt kernel32 ntdll) +add_pch(loadconfig_apitest loadconfig.h SOURCE) +add_rostests_file(TARGET loadconfig_apitest) diff --git a/modules/rostests/apitests/loadconfig/common.c b/modules/rostests/apitests/loadconfig/common.c new file mode 100644 index 00000000000..ab1a26a6250 --- /dev/null +++ b/modules/rostests/apitests/loadconfig/common.c @@ -0,0 +1,70 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: LOAD_CONFIG validation / registration + * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "loadconfig.h" + + +// Tell the linker we want a LOAD_CONFIG +const IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = +{ + sizeof(IMAGE_LOAD_CONFIG_DIRECTORY), + 0, // TimeDateStamp + 0, // MajorVersion + 0, // MinorVersion + 0, // GlobalFlagsClear + FLG_USER_STACK_TRACE_DB, // GlobalFlagsSet + 0, // CriticalSectionDefaultTimeout + 0, // DeCommitFreeBlockThreshold + 0, // DeCommitTotalFreeThreshold + 0, // LockPrefixTable + 0, // MaximumAllocationSize + 0, // VirtualMemoryThreshold + 0, // ProcessHeapFlags + 0, // ProcessAffinityMask + 0, // CSDVersion + 0, // Reserved1 + 0, // EditList + 0, // SecurityCookie +}; + + +BOOL check_loadconfig() +{ + BOOL Result; + PPEB Peb = NtCurrentPeb(); + ULONG ConfigSize = 0; + ULONG MinimalSize; + PIMAGE_LOAD_CONFIG_DIRECTORY LoadConfig; + + // Validate the required flag for the 'stacktrace' test + ok(Peb->NtGlobalFlag & FLG_USER_STACK_TRACE_DB, "NtGlobalFlag: 0x%lx\n", Peb->NtGlobalFlag); + Result = (Peb->NtGlobalFlag & FLG_USER_STACK_TRACE_DB) != 0; + + // Now validate our LOAD_CONFIG entry + LoadConfig = (PIMAGE_LOAD_CONFIG_DIRECTORY)RtlImageDirectoryEntryToData(Peb->ImageBaseAddress, + TRUE, + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, + &ConfigSize); + + MinimalSize = FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY, SecurityCookie) + sizeof(LoadConfig->SecurityCookie); + if (!LoadConfig || ConfigSize < MinimalSize) + { + ok(0, "Invalid IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: %p, %lu (%lu)\n", + LoadConfig, ConfigSize, MinimalSize); + } + else + { + ok(LoadConfig->GlobalFlagsSet & FLG_USER_STACK_TRACE_DB, + "Invalid GlobalFlagsSet: %lx\n", LoadConfig->GlobalFlagsSet); + ok(!(LoadConfig->GlobalFlagsClear & FLG_USER_STACK_TRACE_DB), + "Invalid GlobalFlagsClear: %lx\n", LoadConfig->GlobalFlagsClear); + ok(LoadConfig->Size == sizeof(IMAGE_LOAD_CONFIG_DIRECTORY), + "Unexpected size difference: %lu vs %u\n", LoadConfig->Size, sizeof(IMAGE_LOAD_CONFIG_DIRECTORY)); + } + + return Result; +} diff --git a/modules/rostests/apitests/loadconfig/loadconfig.h b/modules/rostests/apitests/loadconfig/loadconfig.h new file mode 100644 index 00000000000..ce2d2910230 --- /dev/null +++ b/modules/rostests/apitests/loadconfig/loadconfig.h @@ -0,0 +1,14 @@ +#pragma once + +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#include +#include +#include +#include + + +/* common.c */ +BOOL check_loadconfig(); diff --git a/modules/rostests/apitests/loadconfig/stacktrace.c b/modules/rostests/apitests/loadconfig/stacktrace.c new file mode 100644 index 00000000000..f233563f61b --- /dev/null +++ b/modules/rostests/apitests/loadconfig/stacktrace.c @@ -0,0 +1,166 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Test for RtlQueryProcessBackTraceInformation & RtlLogStackBackTrace + * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "loadconfig.h" + + +// This test serves 2 purposes: +// 1. It tests RtlQueryProcessBackTraceInformation & RtlLogStackBackTrace +// 2. It tests the correct activation of IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (see also common.c) + +NTSTATUS NTAPI RtlQueryProcessBackTraceInformation(IN OUT PRTL_DEBUG_INFORMATION Buffer); + + +// Seems that this struct is wrong in our sdk? +typedef struct _RTL_PROCESS_BACKTRACE_INFORMATION_32 +{ + PVOID SymbolicBackTrace; + ULONG TraceCount; + USHORT Index; + USHORT Depth; + PVOID BackTrace[32]; +} RTL_PROCESS_BACKTRACE_INFORMATION_32, *PRTL_PROCESS_BACKTRACE_INFORMATION_32; + + +static PVOID g_Call_Address; +static PVOID g_PreviousReturnAddress; + +__declspec(noinline) +PVOID GetFunctionAddress() +{ + return _ReturnAddress(); +} + +__declspec(noinline) +USHORT Call_Backtrace_2() +{ + USHORT Index; + Index = RtlLogStackBackTrace(); + ok(Index != 0, "RtlLogStackBackTrace failed\n"); + return Index; +} + + +__declspec(noinline) +USHORT Call_Backtrace_1() +{ + USHORT Index; + // It seems that the function calling RtlLogStackBackTrace is skipped, + // so we wrap it in a deeper nested function to be able to check it + g_Call_Address = GetFunctionAddress(); + g_PreviousReturnAddress = _ReturnAddress(); + Index = Call_Backtrace_2(); + return Index; +} + +static void Check_Stacktrace(PVOID* BackTrace, USHORT Depth) +{ + ok(Depth > 2, "Unexpected Depth: %lu\n", (ULONG)Depth); + if (Depth > 2) + { + ULONG_PTR EndAddress = ((ULONG_PTR)g_Call_Address + 0x20); + ok(BackTrace[0] >= g_Call_Address && (ULONG_PTR)BackTrace[0] < EndAddress, + "Unexpected return: %p, expected between %p and %p\n", BackTrace[0], g_Call_Address, (PVOID)EndAddress); + ok(BackTrace[1] == g_PreviousReturnAddress, "Unexpected return: %p, expected: %p\n", + BackTrace[1], g_PreviousReturnAddress); + } +} + +static void test_QueryBacktrace(PRTL_DEBUG_INFORMATION Buffer1, PRTL_DEBUG_INFORMATION Buffer2) +{ + NTSTATUS Status; + USHORT StackTrace; + PRTL_PROCESS_BACKTRACES Backtraces; + ULONG OldNumberOfBackTraces, n; + PRTL_PROCESS_BACKTRACE_INFORMATION_32 FirstBacktrace; + int found = 0; + + Status = RtlQueryProcessBackTraceInformation(Buffer1); + ok_hex(Status, STATUS_SUCCESS); + if (Status != STATUS_SUCCESS) + return; + + Backtraces = Buffer1->BackTraces; + ok(Backtraces != NULL, "No BackTraces\n"); + if (!Backtraces) + return; + + OldNumberOfBackTraces = Backtraces->NumberOfBackTraces; + // Capture a stacktrace + StackTrace = Call_Backtrace_1(); + + // Show that the old debugbuffer is not changed + ok(OldNumberOfBackTraces == Backtraces->NumberOfBackTraces, "Debug buffer changed! (%lu => %lu)\n", + OldNumberOfBackTraces, Backtraces->NumberOfBackTraces); + + // Ask for a new snapshot + Status = RtlQueryProcessBackTraceInformation(Buffer2); + ok_hex(Status, STATUS_SUCCESS); + if (Status != STATUS_SUCCESS) + return; + + Backtraces = Buffer2->BackTraces; + ok(Backtraces != NULL, "No BackTraces\n"); + if (!Backtraces) + return; + + ok(OldNumberOfBackTraces+1 == Backtraces->NumberOfBackTraces, "Stacktrace not added! (%lu => %lu)\n", + OldNumberOfBackTraces, Backtraces->NumberOfBackTraces); + + FirstBacktrace = (PRTL_PROCESS_BACKTRACE_INFORMATION_32)&Backtraces->BackTraces[0]; + trace("NumberOfBackTraces=%lu\n", Backtraces->NumberOfBackTraces); + for (n = 0; n < Backtraces->NumberOfBackTraces; ++n) + { + PRTL_PROCESS_BACKTRACE_INFORMATION_32 Info = FirstBacktrace + n; +#if 0 + USHORT j; + + trace("BackTraces[%02lu]->SymbolicBackTrace = %p\n", n, Info->SymbolicBackTrace); + trace("BackTraces[%02lu]->TraceCount = %lu\n", n, Info->TraceCount); + trace("BackTraces[%02lu]->Index = %lu\n", n, (ULONG)Info->Index); + trace("BackTraces[%02lu]->Depth = %lu\n", n, (ULONG)Info->Depth); + for (j = 0; j < Info->Depth; ++j) + { + trace("BackTraces[%02lu]->BackTrace[%02u] = %p\n", n, j, Info->BackTrace[j]); + } + trace("\n"); +#endif + if (Info->Index == StackTrace) + { + found = 1; + Check_Stacktrace(Info->BackTrace, Info->Depth); + } + } + ok(found, "Stacktrace not found :(\n"); +} + + +START_TEST(stacktrace) +{ + PRTL_DEBUG_INFORMATION Buffer1, Buffer2; + + if (!check_loadconfig()) + return; + + skip("QueryBacktrace not implemented yet\n"); + return; + + Buffer1 = RtlCreateQueryDebugBuffer(0, FALSE); + ok(Buffer1 != NULL, "Failed!\n"); + if (Buffer1) + { + Buffer2 = RtlCreateQueryDebugBuffer(0, FALSE); + ok(Buffer2 != NULL, "Failed!\n"); + if (Buffer2) + { + test_QueryBacktrace(Buffer1, Buffer2); + RtlDestroyQueryDebugBuffer(Buffer2); + } + + RtlDestroyQueryDebugBuffer(Buffer1); + } +} diff --git a/modules/rostests/apitests/loadconfig/testlist.c b/modules/rostests/apitests/loadconfig/testlist.c new file mode 100644 index 00000000000..7f585152fa6 --- /dev/null +++ b/modules/rostests/apitests/loadconfig/testlist.c @@ -0,0 +1,12 @@ + +#define STANDALONE +#include + +extern void func_stacktrace(void); + +const struct test winetest_testlist[] = +{ + { "stacktrace", func_stacktrace }, + + { 0, 0 } +};