Split kernel32 into separate files (part 2)

This commit is contained in:
2025-10-02 00:20:28 -06:00
parent 9ea5b24b67
commit fa3ed4893a
53 changed files with 7352 additions and 6134 deletions

View File

@@ -1,21 +1,76 @@
#include "processthreadsapi.h"
#include "common.h"
#include "errors.h"
#include "files.h"
#include "handles.h"
#include "kernel32.h"
#include "internal.h"
#include "processes.h"
#include "strutil.h"
#include "timeutil.h"
#include <cerrno>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <functional>
#include <limits>
#include <pthread.h>
#include <string>
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <vector>
namespace {
using kernel32::ThreadObject;
constexpr DWORD kMaxTlsValues = 100;
bool g_tlsSlotsUsed[kMaxTlsValues] = {false};
LPVOID g_tlsSlots[kMaxTlsValues] = {nullptr};
DWORD_PTR g_processAffinityMask = 0;
bool g_processAffinityMaskInitialized = false;
const FILETIME kDefaultThreadFileTime = {static_cast<DWORD>(UNIX_TIME_ZERO & 0xFFFFFFFFULL),
static_cast<DWORD>(UNIX_TIME_ZERO >> 32)};
constexpr DWORD STARTF_USESHOWWINDOW = 0x00000001;
constexpr DWORD STARTF_USESTDHANDLES = 0x00000100;
constexpr WORD SW_SHOWNORMAL = 1;
struct ThreadStartData {
LPTHREAD_START_ROUTINE startRoutine;
void *parameter;
ThreadObject *threadObject;
};
FILETIME fileTimeFromTimeval(const struct timeval &value) {
uint64_t total = 0;
if (value.tv_sec > 0 || value.tv_usec > 0) {
total = static_cast<uint64_t>(value.tv_sec) * 10000000ULL + static_cast<uint64_t>(value.tv_usec) * 10ULL;
}
return fileTimeFromDuration(total);
}
FILETIME fileTimeFromTimespec(const struct timespec &value) {
uint64_t total = 0;
if (value.tv_sec > 0 || value.tv_nsec > 0) {
total = static_cast<uint64_t>(value.tv_sec) * 10000000ULL + static_cast<uint64_t>(value.tv_nsec) / 100ULL;
}
return fileTimeFromDuration(total);
}
void destroyThreadObject(ThreadObject *obj) {
if (!obj) {
return;
}
pthread_cond_destroy(&obj->cond);
pthread_mutex_destroy(&obj->mutex);
delete obj;
}
DWORD_PTR computeSystemAffinityMask() {
long reported = sysconf(_SC_NPROCESSORS_ONLN);
if (reported <= 0) {
@@ -29,12 +84,153 @@ DWORD_PTR computeSystemAffinityMask() {
return (static_cast<DWORD_PTR>(1) << usable) - 1;
}
DWORD_PTR g_processAffinityMask = 0;
bool g_processAffinityMaskInitialized = false;
template <typename StartupInfo> void populateStartupInfo(StartupInfo *info) {
if (!info) {
return;
}
std::memset(info, 0, sizeof(StartupInfo));
info->cb = sizeof(StartupInfo);
info->dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
info->wShowWindow = SW_SHOWNORMAL;
info->cbReserved2 = 0;
info->lpReserved2 = nullptr;
info->hStdInput = files::getStdHandle(STD_INPUT_HANDLE);
info->hStdOutput = files::getStdHandle(STD_OUTPUT_HANDLE);
info->hStdError = files::getStdHandle(STD_ERROR_HANDLE);
}
} // namespace
namespace kernel32 {
thread_local ThreadObject *g_currentThreadObject = nullptr;
ThreadObject *ensureCurrentThreadObject() {
ThreadObject *obj = g_currentThreadObject;
if (obj) {
return obj;
}
obj = new ThreadObject();
obj->thread = pthread_self();
obj->finished = false;
obj->joined = false;
obj->detached = true;
obj->synthetic = false;
obj->exitCode = STILL_ACTIVE;
obj->refCount = 0;
obj->suspendCount = 0;
pthread_mutex_init(&obj->mutex, nullptr);
pthread_cond_init(&obj->cond, nullptr);
g_currentThreadObject = obj;
return obj;
}
ThreadObject *threadObjectFromHandle(HANDLE hThread) {
auto raw = reinterpret_cast<uintptr_t>(hThread);
if (raw == kPseudoCurrentThreadHandleValue) {
return ensureCurrentThreadObject();
}
if (raw == static_cast<uintptr_t>(-1) || raw == 0) {
return nullptr;
}
auto data = handles::dataFromHandle(hThread, false);
if (data.type != handles::TYPE_THREAD || data.ptr == nullptr) {
return nullptr;
}
return reinterpret_cast<ThreadObject *>(data.ptr);
}
ThreadObject *retainThreadObject(ThreadObject *obj) {
if (!obj) {
return nullptr;
}
pthread_mutex_lock(&obj->mutex);
obj->refCount++;
pthread_mutex_unlock(&obj->mutex);
return obj;
}
void releaseThreadObject(ThreadObject *obj) {
if (!obj) {
return;
}
pthread_t thread = 0;
pthread_mutex_lock(&obj->mutex);
obj->refCount--;
bool shouldDelete = false;
bool shouldDetach = false;
bool finished = obj->finished;
bool joined = obj->joined;
bool detached = obj->detached;
bool synthetic = obj->synthetic;
thread = obj->thread;
if (obj->refCount == 0) {
if (finished || synthetic) {
shouldDelete = true;
} else if (!detached) {
obj->detached = true;
shouldDetach = true;
detached = true;
}
}
pthread_mutex_unlock(&obj->mutex);
if (shouldDetach && !synthetic) {
pthread_detach(thread);
}
if (shouldDelete) {
if (!synthetic) {
if (!joined && !detached) {
pthread_join(thread, nullptr);
}
}
destroyThreadObject(obj);
}
}
static void *threadTrampoline(void *param) {
ThreadStartData *data = static_cast<ThreadStartData *>(param);
ThreadObject *obj = data->threadObject;
LPTHREAD_START_ROUTINE startRoutine = data->startRoutine;
void *userParam = data->parameter;
delete data;
uint16_t previousSegment = 0;
bool tibInstalled = false;
if (wibo::tibSelector) {
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
tibInstalled = true;
}
g_currentThreadObject = obj;
pthread_mutex_lock(&obj->mutex);
while (obj->suspendCount > 0) {
pthread_cond_wait(&obj->cond, &obj->mutex);
}
pthread_mutex_unlock(&obj->mutex);
DWORD result = startRoutine ? startRoutine(userParam) : 0;
pthread_mutex_lock(&obj->mutex);
obj->finished = true;
obj->exitCode = result;
pthread_cond_broadcast(&obj->cond);
bool shouldDelete = (obj->refCount == 0);
bool detached = obj->detached;
pthread_mutex_unlock(&obj->mutex);
g_currentThreadObject = nullptr;
if (shouldDelete) {
assert(detached && "ThreadObject must be detached when refCount reaches zero before completion");
destroyThreadObject(obj);
}
if (tibInstalled) {
asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory");
}
return nullptr;
}
HANDLE WIN_FUNC GetCurrentProcess() {
DEBUG_LOG("GetCurrentProcess() -> %p\n", reinterpret_cast<void *>(static_cast<uintptr_t>(-1)));
return reinterpret_cast<HANDLE>(static_cast<uintptr_t>(-1));
@@ -53,6 +249,14 @@ DWORD WIN_FUNC GetCurrentThreadId() {
return threadId;
}
HANDLE WIN_FUNC GetCurrentThread() {
ThreadObject *obj = ensureCurrentThreadObject();
(void)obj;
HANDLE pseudoHandle = reinterpret_cast<HANDLE>(kPseudoCurrentThreadHandleValue);
DEBUG_LOG("GetCurrentThread() -> %p\n", pseudoHandle);
return pseudoHandle;
}
BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask,
PDWORD_PTR lpSystemAffinityMask) {
DEBUG_LOG("GetProcessAffinityMask(%p, %p, %p)\n", hProcess, lpProcessAffinityMask, lpSystemAffinityMask);
@@ -116,6 +320,38 @@ BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinit
return TRUE;
}
DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask) {
DEBUG_LOG("SetThreadAffinityMask(%p, 0x%lx)\n", hThread, static_cast<unsigned long>(dwThreadAffinityMask));
if (dwThreadAffinityMask == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
uintptr_t rawThreadHandle = reinterpret_cast<uintptr_t>(hThread);
bool isPseudoHandle = rawThreadHandle == kPseudoCurrentThreadHandleValue || rawThreadHandle == 0 ||
rawThreadHandle == static_cast<uintptr_t>(-1);
if (!isPseudoHandle) {
auto data = handles::dataFromHandle(hThread, false);
if (data.type != handles::TYPE_THREAD) {
wibo::lastError = ERROR_INVALID_HANDLE;
return 0;
}
}
DWORD_PTR processMask = 0;
DWORD_PTR systemMask = 0;
if (!GetProcessAffinityMask(GetCurrentProcess(), &processMask, &systemMask)) {
return 0;
}
if ((dwThreadAffinityMask & ~systemMask) != 0 || (dwThreadAffinityMask & processMask) == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
wibo::lastError = ERROR_SUCCESS;
return processMask;
}
void WIN_FUNC ExitProcess(UINT uExitCode) {
DEBUG_LOG("ExitProcess(%u)\n", uExitCode);
std::exit(static_cast<int>(uExitCode));
@@ -169,6 +405,258 @@ BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) {
return TRUE;
}
DWORD WIN_FUNC TlsAlloc() {
VERBOSE_LOG("TlsAlloc()\n");
for (DWORD i = 0; i < kMaxTlsValues; ++i) {
if (!g_tlsSlotsUsed[i]) {
g_tlsSlotsUsed[i] = true;
g_tlsSlots[i] = nullptr;
wibo::lastError = ERROR_SUCCESS;
return i;
}
}
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return TLS_OUT_OF_INDEXES;
}
BOOL WIN_FUNC TlsFree(DWORD dwTlsIndex) {
VERBOSE_LOG("TlsFree(%u)\n", dwTlsIndex);
if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
g_tlsSlotsUsed[dwTlsIndex] = false;
g_tlsSlots[dwTlsIndex] = nullptr;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
LPVOID WIN_FUNC TlsGetValue(DWORD dwTlsIndex) {
VERBOSE_LOG("TlsGetValue(%u)\n", dwTlsIndex);
if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return g_tlsSlots[dwTlsIndex];
}
BOOL WIN_FUNC TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) {
VERBOSE_LOG("TlsSetValue(%u, %p)\n", dwTlsIndex, lpTlsValue);
if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
g_tlsSlots[dwTlsIndex] = lpTlsValue;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
DWORD WIN_FUNC ResumeThread(HANDLE hThread) {
DEBUG_LOG("ResumeThread(%p)\n", hThread);
ThreadObject *obj = threadObjectFromHandle(hThread);
if (!obj) {
wibo::lastError = ERROR_INVALID_HANDLE;
return static_cast<DWORD>(-1);
}
pthread_mutex_lock(&obj->mutex);
DWORD previous = obj->suspendCount;
if (obj->suspendCount > 0) {
obj->suspendCount--;
if (obj->suspendCount == 0) {
pthread_cond_broadcast(&obj->cond);
}
}
pthread_mutex_unlock(&obj->mutex);
wibo::lastError = ERROR_SUCCESS;
return previous;
}
HRESULT WIN_FUNC SetThreadDescription(HANDLE hThread, LPCWSTR lpThreadDescription) {
DEBUG_LOG("STUB: SetThreadDescription(%p, %p)\n", hThread, lpThreadDescription);
(void)hThread;
(void)lpThreadDescription;
return S_OK;
}
HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags,
LPDWORD lpThreadId) {
DEBUG_LOG("CreateThread(%p, %zu, %p, %p, %u, %p)\n", lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter,
dwCreationFlags, lpThreadId);
(void)lpThreadAttributes;
constexpr DWORD CREATE_SUSPENDED = 0x00000004;
constexpr DWORD STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000;
constexpr DWORD SUPPORTED_FLAGS = CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION;
if ((dwCreationFlags & ~SUPPORTED_FLAGS) != 0) {
DEBUG_LOG("CreateThread: unsupported creation flags 0x%x\n", dwCreationFlags);
wibo::lastError = ERROR_NOT_SUPPORTED;
return nullptr;
}
bool startSuspended = (dwCreationFlags & CREATE_SUSPENDED) != 0;
ThreadObject *obj = new ThreadObject();
pthread_mutex_init(&obj->mutex, nullptr);
pthread_cond_init(&obj->cond, nullptr);
obj->finished = false;
obj->joined = false;
obj->detached = false;
obj->exitCode = 0;
obj->refCount = 1;
obj->suspendCount = startSuspended ? 1u : 0u;
obj->synthetic = false;
ThreadStartData *startData = new ThreadStartData{lpStartAddress, lpParameter, obj};
pthread_attr_t attr;
pthread_attr_t *attrPtr = nullptr;
if (dwStackSize != 0) {
pthread_attr_init(&attr);
size_t stackSize = dwStackSize;
#ifdef PTHREAD_STACK_MIN
if (stackSize < static_cast<size_t>(PTHREAD_STACK_MIN)) {
stackSize = PTHREAD_STACK_MIN;
}
#endif
if (pthread_attr_setstacksize(&attr, stackSize) == 0) {
attrPtr = &attr;
} else {
pthread_attr_destroy(&attr);
}
}
int rc = pthread_create(&obj->thread, attrPtr, threadTrampoline, startData);
if (attrPtr) {
pthread_attr_destroy(attrPtr);
}
if (rc != 0) {
delete startData;
destroyThreadObject(obj);
errno = rc;
setLastErrorFromErrno();
return nullptr;
}
if (lpThreadId) {
std::size_t hashed = std::hash<pthread_t>{}(obj->thread);
*lpThreadId = static_cast<DWORD>(hashed & 0xffffffffu);
}
wibo::lastError = ERROR_SUCCESS;
return handles::allocDataHandle({handles::TYPE_THREAD, obj, 0});
}
void WIN_FUNC ExitThread(DWORD dwExitCode) {
DEBUG_LOG("ExitThread(%u)\n", dwExitCode);
ThreadObject *obj = g_currentThreadObject;
uint16_t previousSegment = 0;
bool tibInstalled = false;
if (wibo::tibSelector) {
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
tibInstalled = true;
}
if (obj) {
pthread_mutex_lock(&obj->mutex);
obj->finished = true;
obj->exitCode = dwExitCode;
pthread_cond_broadcast(&obj->cond);
bool shouldDelete = (obj->refCount == 0);
bool detached = obj->detached;
pthread_mutex_unlock(&obj->mutex);
g_currentThreadObject = nullptr;
if (shouldDelete) {
assert(detached && "ThreadObject must be detached when refCount reaches zero before completion");
destroyThreadObject(obj);
}
}
if (tibInstalled) {
asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory");
}
pthread_exit(nullptr);
}
BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) {
DEBUG_LOG("GetExitCodeThread(%p, %p)\n", hThread, lpExitCode);
if (!lpExitCode) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
ThreadObject *obj = threadObjectFromHandle(hThread);
if (!obj) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
pthread_mutex_lock(&obj->mutex);
DWORD code = obj->finished ? obj->exitCode : STILL_ACTIVE;
pthread_mutex_unlock(&obj->mutex);
*lpExitCode = code;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC SetThreadPriority(HANDLE hThread, int nPriority) {
DEBUG_LOG("STUB: SetThreadPriority(%p, %d)\n", hThread, nPriority);
(void)hThread;
(void)nPriority;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
int WIN_FUNC GetThreadPriority(HANDLE hThread) {
DEBUG_LOG("STUB: GetThreadPriority(%p)\n", hThread);
(void)hThread;
wibo::lastError = ERROR_SUCCESS;
return 0;
}
BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME *lpExitTime, FILETIME *lpKernelTime,
FILETIME *lpUserTime) {
DEBUG_LOG("GetThreadTimes(%p, %p, %p, %p, %p)\n", hThread, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime);
if (!lpKernelTime || !lpUserTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
bool isPseudoCurrentThread = reinterpret_cast<uintptr_t>(hThread) == kPseudoCurrentThreadHandleValue ||
hThread == nullptr || hThread == reinterpret_cast<HANDLE>(static_cast<uintptr_t>(-1));
if (!isPseudoCurrentThread) {
DEBUG_LOG("GetThreadTimes: unsupported handle %p\n", hThread);
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
if (lpCreationTime) {
*lpCreationTime = kDefaultThreadFileTime;
}
if (lpExitTime) {
lpExitTime->dwLowDateTime = 0;
lpExitTime->dwHighDateTime = 0;
}
struct rusage usage;
if (getrusage(RUSAGE_THREAD, &usage) == 0) {
*lpKernelTime = fileTimeFromTimeval(usage.ru_stime);
*lpUserTime = fileTimeFromTimeval(usage.ru_utime);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
struct timespec cpuTime;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) {
*lpKernelTime = fileTimeFromDuration(0);
*lpUserTime = fileTimeFromTimespec(cpuTime);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
setLastErrorFromErrno();
*lpKernelTime = fileTimeFromDuration(0);
*lpUserTime = fileTimeFromDuration(0);
return FALSE;
}
BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags,
LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
@@ -256,4 +744,20 @@ BOOL WIN_FUNC CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LP
lpProcessInformation);
}
void WIN_FUNC GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo) {
DEBUG_LOG("GetStartupInfoA(%p)\n", lpStartupInfo);
populateStartupInfo(lpStartupInfo);
}
void WIN_FUNC GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo) {
DEBUG_LOG("GetStartupInfoW(%p)\n", lpStartupInfo);
populateStartupInfo(lpStartupInfo);
}
BOOL WIN_FUNC SetThreadStackGuarantee(PULONG StackSizeInBytes) {
DEBUG_LOG("STUB: SetThreadStackGuarantee(%p)\n", StackSizeInBytes);
(void)StackSizeInBytes;
return TRUE;
}
} // namespace kernel32