Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ add_executable(wibo
dll/kernel32/stringapiset.cpp
dll/kernel32/synchapi.cpp
dll/kernel32/sysinfoapi.cpp
dll/kernel32/msvc_compat_stubs.cpp
dll/kernel32/timezoneapi.cpp
dll/kernel32/winbase.cpp
dll/kernel32/wincon.cpp
Expand All @@ -219,6 +220,7 @@ add_executable(wibo
dll/lmgr.cpp
dll/mscoree.cpp
dll/ntdll.cpp
dll/msvc_compat_ntdll.cpp
dll/ole32.cpp
dll/rpcrt4.cpp
dll/shlwapi.cpp
Expand Down Expand Up @@ -383,7 +385,7 @@ wibo_codegen_module(NAME vcruntime HEADERS dll/vcruntime.h)
wibo_codegen_module(NAME lmgr HEADERS dll/lmgr.h)
wibo_codegen_module(NAME ole32 HEADERS dll/ole32.h)
wibo_codegen_module(NAME user32 HEADERS dll/user32.h)
wibo_codegen_module(NAME ntdll HEADERS dll/ntdll.h)
wibo_codegen_module(NAME ntdll HEADERS dll/ntdll.h dll/msvc_compat_ntdll.h)
wibo_codegen_module(NAME kernel32 HEADERS
dll/kernel32/debugapi.h
dll/kernel32/errhandlingapi.h
Expand All @@ -402,6 +404,7 @@ wibo_codegen_module(NAME kernel32 HEADERS
dll/kernel32/stringapiset.h
dll/kernel32/synchapi.h
dll/kernel32/sysinfoapi.h
dll/kernel32/msvc_compat_stubs.h
dll/kernel32/timezoneapi.h
dll/kernel32/winbase.h
dll/kernel32/wincon.h
Expand Down Expand Up @@ -486,6 +489,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS)
wibo_add_fixture_bin(NAME test_tls_reloc SOURCES test/test_tls_reloc.c)
wibo_add_fixture_bin(NAME test_handleapi SOURCES test/test_handleapi.c)
wibo_add_fixture_bin(NAME test_findfile SOURCES test/test_findfile.c)
wibo_add_fixture_bin(NAME test_lstr SOURCES test/test_lstr.c)
wibo_add_fixture_bin(NAME test_getfileattributesex SOURCES test/test_getfileattributesex.c)
wibo_add_fixture_bin(NAME test_locale SOURCES test/test_locale.c)
wibo_add_fixture_bin(NAME test_critical_section SOURCES test/test_critical_section.c)
Expand Down
20 changes: 16 additions & 4 deletions dll/kernel32/fibersapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@
#include "errors.h"
#include "internal.h"

#include <mutex>

namespace {

// FLS without fibers is THREAD-local storage on
// Windows (every thread is exactly one fiber); only the index allocation map
// is process-wide. The previous process-global value array made all threads
// share one cell per index, which clobbers msvcr120's per-thread data (_ptd) --
// including the _beginthreadex entry/argument that _threadstartex re-reads
// through FlsGetValue -- so concurrently-starting threads could duplicate or
// swap their start arguments.
constexpr DWORD kMaxFlsValues = 0x100;
std::mutex g_flsMutex;
bool g_flsValuesUsed[kMaxFlsValues] = {false};
LPVOID g_flsValues[kMaxFlsValues] = {nullptr};
thread_local LPVOID t_flsValues[kMaxFlsValues] = {nullptr};

} // namespace

Expand All @@ -19,10 +29,11 @@ DWORD WINAPI FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("FlsAlloc(%p)", lpCallback);
// If the function succeeds, the return value is an FLS index initialized to zero.
std::lock_guard lk(g_flsMutex);
for (DWORD i = 0; i < kMaxFlsValues; i++) {
if (g_flsValuesUsed[i] == false) {
g_flsValuesUsed[i] = true;
g_flsValues[i] = nullptr;
t_flsValues[i] = nullptr;
DEBUG_LOG(" -> %d\n", i);
return i;
}
Expand All @@ -35,6 +46,7 @@ DWORD WINAPI FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback) {
BOOL WINAPI FlsFree(DWORD dwFlsIndex) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("FlsFree(%u)\n", dwFlsIndex);
std::lock_guard lk(g_flsMutex);
if (dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) {
g_flsValuesUsed[dwFlsIndex] = false;
return TRUE;
Expand All @@ -49,7 +61,7 @@ PVOID WINAPI FlsGetValue(DWORD dwFlsIndex) {
VERBOSE_LOG("FlsGetValue(%u)\n", dwFlsIndex);
PVOID result = nullptr;
if (dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) {
result = g_flsValues[dwFlsIndex];
result = t_flsValues[dwFlsIndex];
// See https://learn.microsoft.com/en-us/windows/win32/api/fibersapi/nf-fibersapi-flsgetvalue
setLastError(ERROR_SUCCESS);
} else {
Expand All @@ -63,7 +75,7 @@ BOOL WINAPI FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData) {
HOST_CONTEXT_GUARD();
VERBOSE_LOG("FlsSetValue(%u, %p)\n", dwFlsIndex, lpFlsData);
if (dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) {
g_flsValues[dwFlsIndex] = lpFlsData;
t_flsValues[dwFlsIndex] = lpFlsData;
return TRUE;
} else {
setLastError(ERROR_INVALID_PARAMETER);
Expand Down
43 changes: 43 additions & 0 deletions dll/kernel32/fileapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,49 @@ HANDLE WINAPI FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelI
return findFirstFileCommon(std::string(lpFileName), findData);
}

HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("FindFirstFileExW(%p, %d, %p, %d, %p, 0x%x)", lpFileName, fInfoLevelId, lpFindFileData, fSearchOp,
lpSearchFilter, dwAdditionalFlags);
if (!lpFindFileData) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (!lpFileName) {
DEBUG_LOG(" -> ERROR_PATH_NOT_FOUND\n");
setLastError(ERROR_PATH_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
if (fInfoLevelId != FindExInfoStandard) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (fSearchOp != FindExSearchNameMatch) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (lpSearchFilter) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (dwAdditionalFlags != 0) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}

std::string narrowName = wideStringToString(lpFileName);
auto *findData = static_cast<LPWIN32_FIND_DATAW>(lpFindFileData);
HANDLE handle = findFirstFileCommon(narrowName, findData);
DEBUG_LOG(" -> %p\n", handle);
return handle;
}

BOOL WINAPI FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("FindNextFileA(%p, %p)\n", hFindFile, lpFindFileData);
Expand Down
2 changes: 2 additions & 0 deletions dll/kernel32/fileapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ HANDLE WINAPI FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileDat
HANDLE WINAPI FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData);
HANDLE WINAPI FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
BOOL WINAPI FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData);
BOOL WINAPI FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData);
BOOL WINAPI FindClose(HANDLE hFindFile);
Expand Down
45 changes: 45 additions & 0 deletions dll/kernel32/interlockedapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "context.h"

#include <cstring>
#include <mutex>

namespace kernel32 {

Expand Down Expand Up @@ -48,4 +49,48 @@ void WINAPI InitializeSListHead(PSLIST_HEADER ListHead) {
std::memset(ListHead, 0, sizeof(*ListHead));
}

// These are "Interlocked" ops on a shared list; mspdbcore's worker-thread pool
// pushes/pops the same SLIST concurrently, so they MUST be atomic. A global
// mutex serializes all SLIST operations (correct, if not strictly lock-free).
static std::mutex g_slistMutex;

PSLIST_ENTRY WINAPI InterlockedPushEntrySList(PSLIST_HEADER ListHead, PSLIST_ENTRY ListEntry) {
HOST_CONTEXT_GUARD();
if (!ListHead || !ListEntry) return nullptr;
std::lock_guard<std::mutex> lk(g_slistMutex);
GUEST_PTR prevHead = ListHead->Head;
ListEntry->Next = prevHead;
ListHead->Head = toGuestPtr(ListEntry);
ListHead->Depth++;
return fromGuestPtr<SLIST_ENTRY>(prevHead);
}

PSLIST_ENTRY WINAPI InterlockedPopEntrySList(PSLIST_HEADER ListHead) {
HOST_CONTEXT_GUARD();
if (!ListHead) return nullptr;
std::lock_guard<std::mutex> lk(g_slistMutex);
if (!ListHead->Head) return nullptr;
PSLIST_ENTRY entry = fromGuestPtr<SLIST_ENTRY>(ListHead->Head);
ListHead->Head = entry->Next;
if (ListHead->Depth) ListHead->Depth--;
return entry;
}

PSLIST_ENTRY WINAPI InterlockedFlushSList(PSLIST_HEADER ListHead) {
HOST_CONTEXT_GUARD();
if (!ListHead) return nullptr;
std::lock_guard<std::mutex> lk(g_slistMutex);
PSLIST_ENTRY first = fromGuestPtr<SLIST_ENTRY>(ListHead->Head);
ListHead->Head = GUEST_NULL;
ListHead->Depth = 0;
return first;
}

USHORT WINAPI QueryDepthSList(PSLIST_HEADER ListHead) {
HOST_CONTEXT_GUARD();
if (!ListHead) return 0;
std::lock_guard<std::mutex> lk(g_slistMutex);
return ListHead->Depth;
}

} // namespace kernel32
4 changes: 4 additions & 0 deletions dll/kernel32/interlockedapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@ LONG WINAPI InterlockedDecrement(LONG volatile *Addend);
LONG WINAPI InterlockedExchange(LONG volatile *Target, LONG Value);
LONG WINAPI InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand);
void WINAPI InitializeSListHead(PSLIST_HEADER ListHead);
PSLIST_ENTRY WINAPI InterlockedPushEntrySList(PSLIST_HEADER ListHead, PSLIST_ENTRY ListEntry);
PSLIST_ENTRY WINAPI InterlockedPopEntrySList(PSLIST_HEADER ListHead);
PSLIST_ENTRY WINAPI InterlockedFlushSList(PSLIST_HEADER ListHead);
USHORT WINAPI QueryDepthSList(PSLIST_HEADER ListHead);

} // namespace kernel32
33 changes: 33 additions & 0 deletions dll/kernel32/libloaderapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,39 @@ HMODULE WINAPI GetModuleHandleW(LPCWSTR lpModuleName) {
return GetModuleHandleA(nullptr);
}

static BOOL getModuleHandleExImpl(DWORD dwFlags, const void *nameOrAddr, bool wide, HMODULE *phModule) {
constexpr DWORD GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004;
wibo::ModuleInfo *info = nullptr;
if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) {
info = wibo::moduleInfoFromAddress(const_cast<void *>(nameOrAddr));
} else if (nameOrAddr) {
std::string name = wide ? wideStringToString(static_cast<LPCWSTR>(nameOrAddr))
: std::string(static_cast<LPCSTR>(nameOrAddr));
info = wibo::findLoadedModule(name.c_str());
} else {
info = wibo::mainModule;
}
if (!info) {
if (phModule) *phModule = NO_HANDLE;
kernel32::setLastError(ERROR_MOD_NOT_FOUND);
return FALSE;
}
if (phModule) *phModule = info->handle;
return TRUE;
}

BOOL WINAPI GetModuleHandleExA(DWORD dwFlags, LPCSTR lpModuleName, HMODULE *phModule) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("GetModuleHandleExA(0x%x, %p, %p)\n", dwFlags, (const void *)lpModuleName, (void *)phModule);
return getModuleHandleExImpl(dwFlags, lpModuleName, false, phModule);
}

BOOL WINAPI GetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE *phModule) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("GetModuleHandleExW(0x%x, %p, %p)\n", dwFlags, (const void *)lpModuleName, (void *)phModule);
return getModuleHandleExImpl(dwFlags, lpModuleName, true, phModule);
}

DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("GetModuleFileNameA(%p, %p, %u)\n", hModule, lpFilename, nSize);
Expand Down
2 changes: 2 additions & 0 deletions dll/kernel32/libloaderapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace kernel32 {
BOOL WINAPI DisableThreadLibraryCalls(HMODULE hLibModule);
HMODULE WINAPI GetModuleHandleA(LPCSTR lpModuleName);
HMODULE WINAPI GetModuleHandleW(LPCWSTR lpModuleName);
BOOL WINAPI GetModuleHandleExA(DWORD dwFlags, LPCSTR lpModuleName, HMODULE *phModule);
BOOL WINAPI GetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE *phModule);
DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
DWORD WINAPI GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
HRSRC WINAPI FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType);
Expand Down
64 changes: 64 additions & 0 deletions dll/kernel32/msvc_compat_stubs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "msvc_compat_stubs.h"
#include "common.h"
#include "context.h"
#include "winnls.h"
#include "synchapi.h"

namespace kernel32 {

ULONGLONG WINAPI GetEnabledXStateFeatures() {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: GetEnabledXStateFeatures()\n");
return 0;
}

BOOL WINAPI SetThreadPreferredUILanguages(DWORD dwFlags, void *langs, unsigned int *pnum) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: SetThreadPreferredUILanguages()\n");
(void)dwFlags; (void)langs; if (pnum) *pnum = 0; return TRUE;
}

BOOL WINAPI GetThreadPreferredUILanguages(DWORD dwFlags, unsigned int *pulNumLanguages, unsigned short *pwszBuf, unsigned int *pcchBuf) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: GetThreadPreferredUILanguages()\n");
(void)dwFlags;
if (pulNumLanguages) *pulNumLanguages = 0;
if (pcchBuf) {
if (pwszBuf && *pcchBuf >= 2) { pwszBuf[0] = 0; pwszBuf[1] = 0; }
*pcchBuf = 2;
}
return TRUE;
}

BOOL WINAPI IsValidLocaleName(void *lpLocaleName) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: IsValidLocaleName()\n");
(void)lpLocaleName; return TRUE;
}

VOID WINAPI InitializeSRWLock(void **SRWLock) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: InitializeSRWLock()\n");
if (SRWLock) *SRWLock = nullptr;
}

DWORD WINAPI WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, int bAlertable) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: WaitForSingleObjectEx()\n");
(void)bAlertable; return WaitForSingleObject(hHandle, dwMilliseconds);
}

DWORD WINAPI WaitForMultipleObjectsEx(DWORD nCount, const HANDLE *lpHandles, int bWaitAll, DWORD dwMilliseconds, int bAlertable) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: WaitForMultipleObjectsEx()\n");
(void)bAlertable; return WaitForMultipleObjects(nCount, lpHandles, bWaitAll, dwMilliseconds);
}

int WINAPI CompareStringEx(void *lpLocaleName, DWORD dwCmpFlags, LPCWCH lpString1, int cchCount1, LPCWCH lpString2, int cchCount2, void *lpVersionInformation, void *lpReserved, void *lParam) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: CompareStringEx()\n");
(void)lpLocaleName; (void)lpVersionInformation; (void)lpReserved; (void)lParam;
return CompareStringW(0x0409, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);
}

} // namespace kernel32
15 changes: 15 additions & 0 deletions dll/kernel32/msvc_compat_stubs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once
#include "types.h"

namespace kernel32 {

ULONGLONG WINAPI GetEnabledXStateFeatures();
BOOL WINAPI SetThreadPreferredUILanguages(DWORD dwFlags, void *langs, unsigned int *pnum);
BOOL WINAPI GetThreadPreferredUILanguages(DWORD dwFlags, unsigned int *pulNumLanguages, unsigned short *pwszBuf, unsigned int *pcchBuf);
BOOL WINAPI IsValidLocaleName(void *lpLocaleName);
VOID WINAPI InitializeSRWLock(void **SRWLock);
DWORD WINAPI WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, int bAlertable);
DWORD WINAPI WaitForMultipleObjectsEx(DWORD nCount, const HANDLE *lpHandles, int bWaitAll, DWORD dwMilliseconds, int bAlertable);
int WINAPI CompareStringEx(void *lpLocaleName, DWORD dwCmpFlags, LPCWCH lpString1, int cchCount1, LPCWCH lpString2, int cchCount2, void *lpVersionInformation, void *lpReserved, void *lParam);

} // namespace kernel32
Loading