diff --git a/dll/ntdll/include/ntdllp.h b/dll/ntdll/include/ntdllp.h index bbef5444e86..ce47a64d134 100644 --- a/dll/ntdll/include/ntdllp.h +++ b/dll/ntdll/include/ntdllp.h @@ -29,6 +29,18 @@ typedef struct _LDRP_TLS_DATA IMAGE_TLS_DIRECTORY TlsDirectory; } LDRP_TLS_DATA, *PLDRP_TLS_DATA; +typedef struct _LDRP_OLD_TLS_VECTOR_ENTRY +{ + LIST_ENTRY TlsVectorLinks; + PVOID* OldTlsVector; +} LDRP_OLD_TLS_VECTOR_ENTRY, *PLDRP_OLD_TLS_VECTOR_ENTRY; + +typedef struct _LDRP_TEB_LIST_ENTRY +{ + LIST_ENTRY TebLinks; + PTEB Teb; +} LDRP_TEB_LIST_ENTRY, *PLDRP_TEB_LIST_ENTRY; + typedef NTSTATUS (NTAPI* PLDR_APP_COMPAT_DLL_REDIRECTION_CALLBACK_FUNCTION)( @@ -71,7 +83,7 @@ extern PVOID g_pfnSE_ProcessDying; NTSTATUS NTAPI LdrpRunInitializeRoutines(IN PCONTEXT Context OPTIONAL); VOID NTAPI LdrpInitializeThread(IN PCONTEXT Context); NTSTATUS NTAPI LdrpInitializeTls(VOID); -NTSTATUS NTAPI LdrpAllocateTls(VOID); +NTSTATUS NTAPI LdrpAllocateTls(_In_ PTEB Teb); VOID NTAPI LdrpFreeTls(VOID); VOID NTAPI LdrpCallTlsInitializers(IN PLDR_DATA_TABLE_ENTRY LdrEntry, IN ULONG Reason); BOOLEAN NTAPI LdrpCallInitRoutine(IN PDLL_INIT_ROUTINE EntryPoint, IN PVOID BaseAddress, IN ULONG Reason, IN PVOID Context); diff --git a/dll/ntdll/ldr/ldrinit.c b/dll/ntdll/ldr/ldrinit.c index 14f70b0cb1b..0e84ab7fc6d 100644 --- a/dll/ntdll/ldr/ldrinit.c +++ b/dll/ntdll/ldr/ldrinit.c @@ -51,6 +51,7 @@ RTL_BITMAP TlsExpansionBitMap; RTL_BITMAP FlsBitMap; BOOLEAN LdrpImageHasTls; LIST_ENTRY LdrpTlsList; +LIST_ENTRY LdrpActiveTebList; ULONG LdrpNumberOfTlsEntries; ULONG LdrpNumberOfProcessors; PVOID NtDllBase; @@ -509,6 +510,8 @@ NTAPI LdrpInitializeThread(IN PCONTEXT Context) { PPEB Peb = NtCurrentPeb(); + PTEB Teb = NtCurrentTeb(); + PLDRP_TEB_LIST_ENTRY TebEntry; PLDR_DATA_TABLE_ENTRY LdrEntry; PLIST_ENTRY NextEntry, ListHead; RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx; @@ -535,7 +538,13 @@ LdrpInitializeThread(IN PCONTEXT Context) if (LdrpShutdownInProgress) goto Exit; /* Allocate TLS */ - LdrpAllocateTls(); + LdrpAllocateTls(Teb); + + /* Add thread to active TEB list */ + ListHead = &LdrpActiveTebList; + TebEntry = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*TebEntry)); + TebEntry->Teb = NtCurrentTeb(); + InsertTailList(&LdrpActiveTebList, &TebEntry->TebLinks); /* Start at the beginning */ ListHead = &Peb->Ldr->InMemoryOrderModuleList; @@ -1094,6 +1103,7 @@ LdrShutdownThread(VOID) { PPEB Peb = NtCurrentPeb(); PTEB Teb = NtCurrentTeb(); + PLDRP_TEB_LIST_ENTRY TebEntry; PLDR_DATA_TABLE_ENTRY LdrEntry; PLIST_ENTRY NextEntry, ListHead; RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx; @@ -1112,6 +1122,20 @@ LdrShutdownThread(VOID) /* Get the Ldr Lock */ RtlEnterCriticalSection(&LdrpLoaderLock); + /* Remove the thread from the active TEB list */ + ListHead = &LdrpActiveTebList; + for (NextEntry = ListHead->Blink; + NextEntry != ListHead; + NextEntry = NextEntry->Blink) + { + TebEntry = CONTAINING_RECORD(NextEntry, LDRP_TEB_LIST_ENTRY, TebLinks); + if (TebEntry->Teb == Teb) + { + RemoveEntryList(NextEntry); + break; + } + } + /* Start at the end */ ListHead = &Peb->Ldr->InInitializationOrderModuleList; NextEntry = ListHead->Blink; @@ -1270,12 +1294,16 @@ LdrpInitializeTls(VOID) { PLIST_ENTRY NextEntry, ListHead; PLDR_DATA_TABLE_ENTRY LdrEntry; + PLDRP_TEB_LIST_ENTRY TebEntry; PIMAGE_TLS_DIRECTORY TlsDirectory; PLDRP_TLS_DATA TlsData; ULONG Size; + NTSTATUS Status; + BOOLEAN AllocateTls = FALSE; /* Initialize the TLS List */ - InitializeListHead(&LdrpTlsList); + if (!LdrpImageHasTls) + InitializeListHead(&LdrpTlsList); /* Loop all the modules */ ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; @@ -1295,8 +1323,12 @@ LdrpInitializeTls(VOID) /* Check if we have a directory */ if (!TlsDirectory) continue; + if (LdrEntry->Flags & LDRP_TLS_LOADED) + continue; + /* Check if the image has TLS */ if (!LdrpImageHasTls) LdrpImageHasTls = TRUE; + AllocateTls = TRUE; /* Show debug message */ if (ShowSnaps) @@ -1313,6 +1345,7 @@ LdrpInitializeTls(VOID) /* Lock the DLL and mark it for TLS Usage */ LdrEntry->LoadCount = -1; LdrEntry->TlsIndex = -1; + LdrEntry->Flags |= LDRP_TLS_LOADED; /* Save the cached TLS data */ TlsData->TlsDirectory = *TlsDirectory; @@ -1324,29 +1357,57 @@ LdrpInitializeTls(VOID) } /* Done setting up TLS, allocate entries */ - return LdrpAllocateTls(); + if (AllocateTls) + { + Status = LdrpAllocateTls(NtCurrentTeb()); + if (Status != STATUS_SUCCESS) + return Status; + + ListHead = &LdrpActiveTebList; + NextEntry = ListHead->Blink; + + for (NextEntry = ListHead->Blink; + NextEntry != ListHead; + NextEntry = NextEntry->Blink) + { + TebEntry = CONTAINING_RECORD(NextEntry, LDRP_TEB_LIST_ENTRY, TebLinks); + if (TebEntry->Teb != NtCurrentTeb()) + { + Status = LdrpAllocateTls(TebEntry->Teb); + if (Status != STATUS_SUCCESS) + return Status; + } + } + } + + return STATUS_SUCCESS; } NTSTATUS NTAPI -LdrpAllocateTls(VOID) +LdrpAllocateTls(_In_ PTEB Teb) { - PTEB Teb = NtCurrentTeb(); PLIST_ENTRY NextEntry, ListHead; PLDRP_TLS_DATA TlsData; SIZE_T TlsDataSize; PVOID *TlsVector; + PVOID *OldTlsVector; + PLDRP_OLD_TLS_VECTOR_ENTRY OldTlsVectorEntry; /* Check if we have any entries */ if (!LdrpNumberOfTlsEntries) return STATUS_SUCCESS; + /* Check if there are new entries to add to the vector */ + if (LdrpNumberOfTlsEntries == Teb->UserReserved.ThreadNumberOfTlsEntries) + return STATUS_SUCCESS; /* Allocate the vector array */ TlsVector = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - LdrpNumberOfTlsEntries * sizeof(PVOID)); + 0, + LdrpNumberOfTlsEntries * sizeof(PVOID)); if (!TlsVector) return STATUS_NO_MEMORY; - Teb->ThreadLocalStoragePointer = TlsVector; + /* Grab old TLS vector to retrieve existing values */ + OldTlsVector = Teb->ThreadLocalStoragePointer; /* Loop the TLS Array */ ListHead = &LdrpTlsList; @@ -1360,32 +1421,74 @@ LdrpAllocateTls(VOID) /* Allocate this vector */ TlsDataSize = TlsData->TlsDirectory.EndAddressOfRawData - TlsData->TlsDirectory.StartAddressOfRawData; - TlsVector[TlsData->TlsDirectory.Characteristics] = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - TlsDataSize); - if (!TlsVector[TlsData->TlsDirectory.Characteristics]) - { - /* Out of memory */ - return STATUS_NO_MEMORY; - } - /* Show debug message */ - if (ShowSnaps) + if (!OldTlsVector || ((TlsData->TlsDirectory.Characteristics + 1) > Teb->UserReserved.ThreadNumberOfTlsEntries)) { - DPRINT1("LDR: TlsVector %p Index %lu = %p copied from %x to %p\n", - TlsVector, - TlsData->TlsDirectory.Characteristics, - &TlsVector[TlsData->TlsDirectory.Characteristics], - TlsData->TlsDirectory.StartAddressOfRawData, - TlsVector[TlsData->TlsDirectory.Characteristics]); - } + TlsVector[TlsData->TlsDirectory.Characteristics] = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + TlsDataSize); + if (!TlsVector[TlsData->TlsDirectory.Characteristics]) + { + /* Out of memory */ + return STATUS_NO_MEMORY; + } - /* Copy the data */ - RtlCopyMemory(TlsVector[TlsData->TlsDirectory.Characteristics], - (PVOID)TlsData->TlsDirectory.StartAddressOfRawData, - TlsDataSize); + /* Show debug message */ + if (ShowSnaps) + { + DPRINT1("LDR: TlsVector %p Index %lu = %p copied from %x to %p\n", + TlsVector, + TlsData->TlsDirectory.Characteristics, + &TlsVector[TlsData->TlsDirectory.Characteristics], + TlsData->TlsDirectory.StartAddressOfRawData, + TlsVector[TlsData->TlsDirectory.Characteristics]); + } + + /* Copy the data */ + RtlCopyMemory(TlsVector[TlsData->TlsDirectory.Characteristics], + (PVOID)TlsData->TlsDirectory.StartAddressOfRawData, + TlsDataSize); + } + else + { + /* Reuse the previous thread-local copy of the TLS data in the new vector. */ + TlsVector[TlsData->TlsDirectory.Characteristics] = OldTlsVector[TlsData->TlsDirectory.Characteristics]; + /* Show debug message */ + if (ShowSnaps) + { + DPRINT1("LDR: TlsVector %p Index %lu = %p recycled from %x to %p\n", + TlsVector, + TlsData->TlsDirectory.Characteristics, + &OldTlsVector[TlsData->TlsDirectory.Characteristics], + OldTlsVector[TlsData->TlsDirectory.Characteristics], + TlsVector[TlsData->TlsDirectory.Characteristics]); + } + } } + if (OldTlsVector) + { + OldTlsVectorEntry = RtlAllocateHeap(RtlGetProcessHeap(), 0, + sizeof(*OldTlsVectorEntry)); + if (!OldTlsVectorEntry) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, TlsVector); + return STATUS_NO_MEMORY; + } + OldTlsVectorEntry->OldTlsVector = OldTlsVector; + if (Teb->SystemReserved1.OldTlsVectorList) + { + InsertTailList((PLIST_ENTRY)Teb->SystemReserved1.OldTlsVectorList, + &OldTlsVectorEntry->TlsVectorLinks); + } + else + { + InitializeListHead(&OldTlsVectorEntry->TlsVectorLinks); + Teb->SystemReserved1.OldTlsVectorList = OldTlsVectorEntry; + } + } + Teb->ThreadLocalStoragePointer = TlsVector; + Teb->UserReserved.ThreadNumberOfTlsEntries = LdrpNumberOfTlsEntries; /* Done */ return STATUS_SUCCESS; } @@ -1396,6 +1499,7 @@ LdrpFreeTls(VOID) { PLIST_ENTRY ListHead, NextEntry; PLDRP_TLS_DATA TlsData; + PLDRP_OLD_TLS_VECTOR_ENTRY OldTlsVectorDataEntry; PVOID *TlsVector; PTEB Teb = NtCurrentTeb(); @@ -1424,6 +1528,24 @@ LdrpFreeTls(VOID) RtlFreeHeap(RtlGetProcessHeap(), 0, TlsVector); + + if (Teb->SystemReserved1.OldTlsVectorList) + { + /* Loop through it */ + ListHead = (PLIST_ENTRY)Teb->SystemReserved1.OldTlsVectorList; + NextEntry = ListHead->Flink; + while (NextEntry != ListHead) + { + OldTlsVectorDataEntry = CONTAINING_RECORD(NextEntry, + LDRP_OLD_TLS_VECTOR_ENTRY, + TlsVectorLinks); + NextEntry = NextEntry->Flink; + + /* Free each old TLS vector and the entry itself */ + RtlFreeHeap(RtlGetProcessHeap(), 0, OldTlsVectorDataEntry->OldTlsVector); + RtlFreeHeap(RtlGetProcessHeap(), 0, OldTlsVectorDataEntry); + } + } } NTSTATUS @@ -2436,6 +2558,9 @@ LdrpInitializeProcess(IN PCONTEXT Context, RtlpInitializeKeyedEvent(); RtlpInitializeThreadPooling(); + /* Initialize Active TEB List */ + InitializeListHead(&LdrpActiveTebList); + /* Initialize TLS */ Status = LdrpInitializeTls(); if (!NT_SUCCESS(Status)) diff --git a/dll/ntdll/ldr/ldrutils.c b/dll/ntdll/ldr/ldrutils.c index 19c90865531..09a9fcdbb5f 100644 --- a/dll/ntdll/ldr/ldrutils.c +++ b/dll/ntdll/ldr/ldrutils.c @@ -2580,6 +2580,18 @@ LdrpLoadDll(IN BOOLEAN Redirected, } /* Run the init routine */ + Status = LdrpInitializeTls(); + if (!NT_SUCCESS(Status)) + { + /* Failed, unload the DLL */ + if (ShowSnaps) + { + DbgPrint("LDR: Unloading %wZ because dynamic TLS allocation failed; status = 0x%08lx\n", + DllName, + Status); + } + LdrUnloadDll(LdrEntry->DllBase); + } Status = LdrpRunInitializeRoutines(NULL); if (!NT_SUCCESS(Status)) { diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 71f8ec4af7e..db0e48b4f8c 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -1,7 +1,9 @@ +add_subdirectory(implicit_tls) add_subdirectory(load_notifications) add_subdirectory(empty_dll) +include_directories($) include_directories($) include_directories($) include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) @@ -38,6 +40,7 @@ add_dependencies(ntdll_crt_test_lib psdk) list(APPEND SOURCE DllLoadNotification.c + implicit_tls.c LdrEnumResources.c LdrFindResource_U.c LdrLoadDll.c @@ -161,6 +164,7 @@ endif() list(APPEND PCH_SKIP_SOURCE testlist.c) +add_rc_deps(testdata.rc ${CMAKE_CURRENT_BINARY_DIR}/implicit_tls/implicit_tls.dll) add_rc_deps(testdata.rc ${CMAKE_CURRENT_BINARY_DIR}/load_notifications/load_notifications.dll) add_rc_deps(testdata.rc ${CMAKE_CURRENT_BINARY_DIR}/empty_dll/empty_dll.dll) @@ -180,7 +184,7 @@ target_link_libraries(ntdll_apitest ntdll_crt_test_lib rtl_test_lib wine uuid ${ set_module_type(ntdll_apitest win32cui) add_importlibs(ntdll_apitest msvcrt advapi32 kernel32 ntdll) add_pch(ntdll_apitest precomp.h "${PCH_SKIP_SOURCE}") -add_dependencies(ntdll_apitest load_notifications empty_dll) +add_dependencies(ntdll_apitest implicit_tls load_notifications empty_dll) if(NOT MSVC) set_source_files_properties( diff --git a/modules/rostests/apitests/ntdll/implicit_tls.c b/modules/rostests/apitests/ntdll/implicit_tls.c new file mode 100644 index 00000000000..d9758a5353a --- /dev/null +++ b/modules/rostests/apitests/ntdll/implicit_tls.c @@ -0,0 +1,139 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: Tests for Implicit Thread Local Storage (TLS) support + * COPYRIGHT: Copyright 2025 Shane Fournier + */ + +#include "precomp.h" + +#include + +#define TLS_VECTOR_MAX_SIZE 512 + +WCHAR dllpath_implicit_tls[MAX_PATH]; + +HANDLE ThreadEvent; + +ULONG ExtantTlsEntryCount = 0; + +DWORD WINAPI +AuxThread0Proc( + _In_ LPVOID pParameter +) +{ + PVOID* TlsVector; + PTEB Teb = NtCurrentTeb(); + int i = 0; + WaitForSingleObject(ThreadEvent, INFINITE); + TlsVector = Teb->ThreadLocalStoragePointer; + while (TlsVector[i] && !((ULONG_PTR)TlsVector[i] % 4)) + { + ++i; + } + WaitForSingleObject(ThreadEvent, INFINITE); + ok(i == TLS_VECTOR_MAX_SIZE + ExtantTlsEntryCount, "ThreadLocalStoragePointer length is %d, expected length %lu\n", i, TLS_VECTOR_MAX_SIZE + ExtantTlsEntryCount); + return 0; +} + +DWORD WINAPI +AuxThread1Proc( + _In_ LPVOID pParameter +) +{ + PVOID* TlsVector; + PTEB Teb = NtCurrentTeb(); + TlsVector = Teb->ThreadLocalStoragePointer; + ok(TlsVector[1] != NULL, "ThreadLocalStoragePointer index 1 is NULL; failed to initialize other thread with TLS entries present\n"); + return 0; +} + +START_TEST(implicit_tls) +{ + PVOID* TlsVector; + WCHAR workdir[MAX_PATH]; + WCHAR duplicatepath[MAX_PATH]; + WCHAR basedllname[MAX_PATH]; + HMODULE DllAddr[TLS_VECTOR_MAX_SIZE]; + BOOL IsSuccess; + DWORD Length; + HANDLE AuxThread0, AuxThread1; + int i; + PTEB Teb = NtCurrentTeb(); + ULONG TlsIdx0Value; + ULONG TlsIdx0ValueAfter; + + IsSuccess = GetTempPathW(_countof(workdir), workdir); + ok(IsSuccess, "GetTempPathW error: %lu\n", GetLastError()); + + Length = GetTempFileNameW(workdir, L"implicit_tls", 0, dllpath_implicit_tls); + ok(Length != 0, "GetTempFileNameW failed with %lu\n", GetLastError()); + + IsSuccess = CopyFileW(L"implicit_tls.dll", dllpath_implicit_tls, FALSE); + ok(IsSuccess, "CopyFileW failed with %lu\n", GetLastError()); + + ThreadEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + ok(ThreadEvent != INVALID_HANDLE_VALUE, "CreateEventA failed with %lu\n", GetLastError()); + + AuxThread0 = CreateThread(NULL, 0, AuxThread0Proc, NULL, 0, NULL); + ok(AuxThread0 != NULL, "CreateThread failed with %lu\n", GetLastError()); + + TlsVector = Teb->ThreadLocalStoragePointer; + PULONG_PTR ModuleHandle = TlsVector[0]; + #if defined(_MSC_VER) + #pragma warning( disable : 4311) + #endif + *ModuleHandle = (ULONG)GetModuleHandleA(NULL) + 3; + + TlsIdx0Value = *(PULONG)TlsVector[0]; + while (TlsVector[ExtantTlsEntryCount] && !((ULONG_PTR)TlsVector[ExtantTlsEntryCount] % 4)) + { + ++ExtantTlsEntryCount; + } + for (i = 0; i < TLS_VECTOR_MAX_SIZE; i++) + { + StringCchPrintfW(basedllname, MAX_PATH, L"basedllname_%d", i); + Length = GetTempFileNameW(workdir, basedllname, 0, duplicatepath); + ok(Length != 0, "GetTempFileNameW failed with %lu\n", GetLastError()); + IsSuccess = CopyFileW(dllpath_implicit_tls, duplicatepath, FALSE); + ok(IsSuccess, "CopyFileW failed with %lu\n", GetLastError()); + DllAddr[i] = LoadLibraryW(duplicatepath); + ok(DllAddr[i] != NULL, "LoadLibraryW failed with %lu\n", GetLastError()); + + if (i == TLS_VECTOR_MAX_SIZE / 2) + { + AuxThread1 = CreateThread(NULL, 0, AuxThread1Proc, NULL, 0, NULL); + ok(AuxThread1 != NULL, "CreateThread failed with %lu\n", GetLastError()); + } + } + + TlsVector = Teb->ThreadLocalStoragePointer; + ok(TlsVector[1] != NULL, "ThreadLocalStoragePointer index 1 is NULL; Implicit TLS unavailable\n"); + + SetEvent(ThreadEvent); + + TlsIdx0ValueAfter = *(PULONG)TlsVector[0]; + + ok(TlsIdx0Value == TlsIdx0ValueAfter, "Value in TLS index 0 corrupted by DLL loads; expected %lu and got %lu\n", TlsIdx0Value, TlsIdx0ValueAfter); + + i = 0; + while (TlsVector[i] && !((ULONG_PTR)TlsVector[i] % 4)) + { + ++i; + } + ok(i == TLS_VECTOR_MAX_SIZE + ExtantTlsEntryCount, "ThreadLocalStoragePointer length is %d, expected length %lu\n", i, TLS_VECTOR_MAX_SIZE + ExtantTlsEntryCount); + + SetEvent(ThreadEvent); + + for (i = 0; i < TLS_VECTOR_MAX_SIZE; i++) + { + StringCchPrintfW(basedllname, MAX_PATH, L"basedllname_%d", i); + Length = GetTempFileNameW(workdir, basedllname, 0, duplicatepath); + ok(Length != 0, "GetTempFileNameW failed with %lu\n", GetLastError()); + IsSuccess = DeleteFileW(duplicatepath); + ok(IsSuccess, "DeleteFileW failed with %lu\n", GetLastError()); + FreeLibrary(DllAddr[i]); + } + + DeleteFileW(dllpath_implicit_tls); +} diff --git a/modules/rostests/apitests/ntdll/implicit_tls/CMakeLists.txt b/modules/rostests/apitests/ntdll/implicit_tls/CMakeLists.txt new file mode 100644 index 00000000000..91917c8704b --- /dev/null +++ b/modules/rostests/apitests/ntdll/implicit_tls/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_library(implicit_tls MODULE implicit_tls.c) +set_module_type(implicit_tls win32dll ENTRYPOINT DllMain 12) +add_importlibs(implicit_tls kernel32 ntdll) +add_dependencies(implicit_tls psdk) +add_rostests_file(TARGET implicit_tls) diff --git a/modules/rostests/apitests/ntdll/implicit_tls/implicit_tls.c b/modules/rostests/apitests/ntdll/implicit_tls/implicit_tls.c new file mode 100644 index 00000000000..1bf21254514 --- /dev/null +++ b/modules/rostests/apitests/ntdll/implicit_tls/implicit_tls.c @@ -0,0 +1,65 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: Tests for Implicit Thread Local Storage (TLS) support + * COPYRIGHT: Copyright 2025 Shane Fournier + */ + +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#include +#include + +#if defined(_MSC_VER) +#define _CRTALLOC(x) __declspec(allocate(x)) +#elif defined(__GNUC__) +#define _CRTALLOC(x) __attribute__ ((section (x) )) +#else +#error Your compiler is not supported. +#endif + +/* + * Putting this note here as this is more directly related to TLS: + * https://web.archive.org/web/20250204223245/http://www.nynaeve.net/?tag=tls + */ +/* Tls magic stolen from sdk/lib/crt/startup/tlssup.c */ +/* ROS is built with the flag /Zc:threadSafeInit- which prevents the compilation + * of proper TLS code. Instead, hack up a TLS directory to increment the TLS index + * and allow the loader to allocate a sufficient buffer in the TLS vector. */ + +#if defined(_MSC_VER) +#pragma section(".rdata$T",long,read) +#pragma section(".tls",long,read,write) +#pragma section(".tls$ZZZ",long,read,write) +#endif + +_CRTALLOC(".tls") char _tls_start = 0; +_CRTALLOC(".tls$ZZZ") char _tls_end = 0; + +ULONG _tls_index = 0; + +_CRTALLOC(".rdata$T") const IMAGE_TLS_DIRECTORY _tls_used = { + (ULONG_PTR) &_tls_start, (ULONG_PTR) &_tls_end + 3, + (ULONG_PTR) &_tls_index, (ULONG_PTR) 0, + (ULONG) 0, (ULONG) 0 +}; + +BOOL WINAPI +DllMain(IN HINSTANCE hDllHandle, + IN DWORD dwReason, + IN LPVOID lpvReserved) +{ + PTEB Teb = NtCurrentTeb(); + PVOID* TlsVector = Teb->ThreadLocalStoragePointer; + PULONG_PTR ModuleHandle = (PULONG_PTR)TlsVector[_tls_index]; + if (dwReason == DLL_PROCESS_ATTACH) + { + #if defined(_MSC_VER) + #pragma warning( disable : 4311) + #endif + *ModuleHandle = (ULONG_PTR)GetModuleHandleA(NULL) + 1; + } + return TRUE; +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 6584eaf0f9b..d4048d88854 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -25,6 +25,7 @@ extern void func_wcstoul(void); extern void func_wcstombs(void); extern void func_DllLoadNotification(void); +extern void func_implicit_tls(void); extern void func_LdrEnumResources(void); extern void func_LdrFindResource_U(void); extern void func_LdrLoadDll(void); @@ -159,6 +160,7 @@ const struct test winetest_testlist[] = { "wcstombs", func_wcstombs }, { "DllLoadNotification", func_DllLoadNotification }, + { "implicit_tls", func_implicit_tls }, { "LdrEnumResources", func_LdrEnumResources }, { "LdrFindResource_U", func_LdrFindResource_U }, { "LdrLoadDll", func_LdrLoadDll }, diff --git a/sdk/include/ndk/ldrtypes.h b/sdk/include/ndk/ldrtypes.h index 4c16bd524c1..8145573d851 100644 --- a/sdk/include/ndk/ldrtypes.h +++ b/sdk/include/ndk/ldrtypes.h @@ -43,6 +43,7 @@ Author: #define LDRP_LOAD_NOTIFICATIONS_SENT 0x00000008 #endif #define LDRP_IMAGE_INTEGRITY_FORCED 0x00000020 +#define LDRP_TLS_LOADED 0x00000040 #define LDRP_LOAD_IN_PROGRESS 0x00001000 #define LDRP_UNLOAD_IN_PROGRESS 0x00002000 #define LDRP_ENTRY_PROCESSED 0x00004000 diff --git a/sdk/include/ndk/peb_teb.h b/sdk/include/ndk/peb_teb.h index 85347d9180c..cf9d50433ec 100644 --- a/sdk/include/ndk/peb_teb.h +++ b/sdk/include/ndk/peb_teb.h @@ -240,16 +240,43 @@ typedef struct STRUCT(_TEB) PTR(PVOID) CsrClientThread; PTR(PVOID) Win32ThreadInfo; ULONG User32Reserved[26]; - ULONG UserReserved[5]; + union + { + ULONG UserReserved[5]; + struct + { + /* ReactOS specific */ + ULONG ThreadNumberOfTlsEntries; + ULONG Spare[4]; + }; + } UserReserved; PTR(PVOID) WOW32Reserved; LCID CurrentLocale; ULONG FpSoftwareStatusRegister; #if (NTDDI_VERSION >= NTDDI_WIN10) // since 10.0.10240.16384 PTR(PVOID) ReservedForDebuggerInstrumentation[16]; - PTR(PVOID) SystemReserved1[38]; + union + { + PTR(PVOID) SystemReserved1[38]; + struct + { + /* ReactOS specific */ + PTR(PVOID) OldTlsVectorList; + PTR(PVOID) Spare[37]; + }; + } SystemReserved1; #else - PTR(PVOID) SystemReserved1[54]; + union + { + PTR(PVOID) SystemReserved1[54]; + struct + { + /* ReactOS specific */ + PTR(PVOID) OldTlsVectorList; + PTR(PVOID) Spare[53]; + }; + } SystemReserved1; #endif LONG ExceptionCode; #ifdef _STRUCT64