add page cache with read-ahead and dirty writeback, file-backed and anonymous mmap with CoW, VAD-based virtual memory replacing the old user region model, PFN database with eviction and reclaim, file identity for cross-open section sharing, FAT32 path cache and per-file cluster chain cache, serial.sys for COM1 keyboard input, xHCI MSI with INTx and polled fallback, PciEnableMsiAuto, waitable input queue, KeWaitForAnyEvent, per-process thread info syscall, expanded kernel stats (PFN/PTE/VAD/file-cache/disk), pool allocation tags, async I/O user buffer locking, file flush on close, add docs.md.

This commit is contained in:
maksw committed 2026-06-20 13:17:39 +02:00
1 parent 61ec2442eb
commit 2c4a68a75f
114 files changed
+18069 -2620

No files matched your search

+16 -1
View File
@@ -72,12 +72,27 @@ extern int errno; // defined in posix_fd.c; single global in v1
#define SEEK_CUR 1
#define SEEK_END 2
// mmap() protection and mapping flags.
#define PROT_NONE 0x0
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_FAILED ((void*)(intptr_t)-1)
#define MS_SYNC 0x04
#define MS_ASYNC 0x01
#define MS_INVALIDATE 0x02
// Scalar types.
typedef int32_t pid_t;
typedef int32_t off_t;
typedef uint32_t mode_t;
typedef int64_t ssize_t;
__declspec(dllexport) void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
__declspec(dllexport) int munmap(void* addr, size_t length);
__declspec(dllexport) int msync(void* addr, size_t length, int flags);
// Sockets.
typedef uint16_t sa_family_t;
@@ -188,7 +203,7 @@ __declspec(dllexport) int getaddrinfo(const char* node, const char* service, con
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res);
// pthread types.
typedef struct { uint32_t Tid; } pthread_t;
typedef struct { uint32_t Handle; uint32_t Tid; } pthread_t;
typedef struct { uint32_t Handle; } pthread_mutex_t;
typedef void* pthread_attr_t; // unused in v1
typedef void* pthread_mutexattr_t;
+4 -2
View File
@@ -81,7 +81,8 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) {
return (ssize_t)count;
case FD_FILE: {
size_t got = 0;
uint32_t s = SysReadFile(e->KHandle, buf, count, &got);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(e->KHandle, buf, count, &got);
uint32_t s = SysReadFile(&req);
if (s != 0 && got == 0) { errno = PosixStatusToErrno(s); return -1; }
return (ssize_t)got;
}
@@ -115,7 +116,8 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) {
return (ssize_t)count;
case FD_FILE: {
size_t written = 0;
uint32_t s = SysWriteFile(e->KHandle, buf, count, &written);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(e->KHandle, buf, count, &written);
uint32_t s = SysWriteFile(&req);
if (s != 0 && written == 0) { errno = PosixStatusToErrno(s); return -1; }
return (ssize_t)written;
}
@@ -0,0 +1,85 @@
// POSIX compatibility support for mmap-style file mappings.
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
#include "posix.h"
#include "posix_fd.h"
static uint32_t PosixProtToSysMapAccess(int prot, int flags) {
uint32_t access = 0;
if (prot & PROT_EXEC) access |= SYS_MAP_ACCESS_EXECUTE;
if (prot & PROT_WRITE) {
access |= (flags & MAP_SHARED) ? SYS_MAP_ACCESS_WRITE : SYS_MAP_ACCESS_COPY;
} else if (flags & MAP_PRIVATE) {
access |= SYS_MAP_ACCESS_COPY;
}
if (prot & PROT_READ) access |= SYS_MAP_ACCESS_READ;
if (!access) access = SYS_MAP_ACCESS_READ;
return access;
}
__declspec(dllexport) void* mmap(void* addr, size_t length, int prot,
int flags, int fd, off_t offset) {
if (addr || length == 0 || offset < 0 || ((uint64_t)offset & 0xFFFu)) {
errno = EINVAL;
return MAP_FAILED;
}
if (!(flags & MAP_PRIVATE) && !(flags & MAP_SHARED)) {
errno = EINVAL;
return MAP_FAILED;
}
uint32_t pid = SysGetCurrentPid();
FD_ENTRY* e = FdGet(pid, fd);
if (!e) return MAP_FAILED;
if (e->Type != FD_FILE) {
errno = EBADF;
return MAP_FAILED;
}
uint64_t maxSize = (uint64_t)offset + (uint64_t)length;
if (maxSize < (uint64_t)offset) {
errno = EINVAL;
return MAP_FAILED;
}
uint32_t protect = (prot & PROT_WRITE) ? SYS_VM_READWRITE : SYS_VM_READ;
if (prot & PROT_EXEC)
protect = (prot & PROT_WRITE) ? SYS_VM_EXECUTE_READWRITE : SYS_VM_EXECUTE_READ;
uint32_t mapping = 0;
uint32_t s = SysCreateFileMapping(e->KHandle, protect, SYS_MAP_FLAG_NONE,
maxSize, &mapping);
if (s != SYS_STATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
void* base = (void*)0;
s = SysMapFile(mapping, (uint64_t)offset, length,
PosixProtToSysMapAccess(prot, flags),
SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
return base;
}
__declspec(dllexport) int munmap(void* addr, size_t length) {
(void)length;
if (!addr) { errno = EINVAL; return -1; }
uint32_t s = SysFreeVm(addr);
if (s != SYS_STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
return 0;
}
__declspec(dllexport) int msync(void* addr, size_t length, int flags) {
(void)addr;
(void)length;
(void)flags;
return 0;
}
+1 -1
View File
@@ -126,7 +126,7 @@ __declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
uint32_t handle = ProcTableFind(pid);
if (!handle) { errno = ESRCH; return -1; }
SysWaitForProcess(handle);
SysWait(handle, SYS_WAIT_FLAG_NONE);
int exitCode = 0;
SysGetProcessExitCode(handle, &exitCode);
+20 -7
View File
@@ -84,28 +84,41 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
tr->Pid = pid;
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
(uint64_t)tr, 1, &tid);
uint32_t handle = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)PthreadTrampoline,
(uintptr_t)tr,
1,
0,
&handle,
&tid
};
uint32_t s = SysCreateThread(&req);
if (s != 0) {
free(tr);
return EAGAIN;
}
// The trampoline allocates the retval slot itself after it knows its real
// TID, avoiding the creator/new-thread race.
if (t) t->Tid = tid;
if (t) {
t->Handle = handle;
t->Tid = tid;
} else {
SysCloseHandle(handle);
}
return 0;
}
// Implements the pthread join operation.
__declspec(dllexport) int pthread_join(pthread_t t, void** retval) {
uint32_t pid = SysGetCurrentPid();
uint32_t s = SysWaitThread(t.Tid);
// STATUS_NOT_FOUND (0x22) means already dead - treat as success.
if (s != 0 && s != 0x0022) return ESRCH;
uint32_t s = SysWait(t.Handle, SYS_WAIT_FLAG_NONE);
if (s != 0) return ESRCH;
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
if (retval) *retval = slot ? slot->RetVal : (void*)0;
if (slot) slot->Used = false;
SysCloseHandle(t.Handle);
return 0;
}
@@ -138,7 +151,7 @@ __declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) {
// Implements the pthread mutex lock operation.
__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) {
uint32_t s = SysAcquireMutex(m->Handle);
uint32_t s = SysWait(m->Handle, SYS_WAIT_FLAG_NONE);
return s == 0 ? 0 : EINVAL;
}
+7 -92
View File
@@ -63,101 +63,16 @@ static int* srcX_map = NULL;
static int currentDestCapacity = 0;
static int currentXMapCapacity = 0;
__declspec(dllexport) int StretchDIBits(
HDC hdc,
int xDest, int yDest, int DestWidth, int DestHeight,
__declspec(dllexport) int StretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight,
int xSrc, int ySrc, int SrcWidth, int SrcHeight,
const void *lpBits, const BITMAPINFO *lpbmi,
UINT iUsage, DWORD rop)
{
(void)hdc; (void)lpbmi; (void)iUsage; (void)rop;
(void)hdc; (void)xDest; (void)yDest; (void)DestWidth; (void)DestHeight;
(void)xSrc; (void)ySrc; (void)SrcWidth; (void)SrcHeight;
(void)lpBits; (void)lpbmi; (void)iUsage; (void)rop;
// Safety check to prevent division by zero.
if (DestWidth <= 0 || DestHeight <= 0 || SrcWidth <= 0 || SrcHeight <= 0) {
return 0;
}
// Handle trivial case (1:1 copy) to bypass allocation and scaling overhead.
if (DestWidth == SrcWidth && DestHeight == SrcHeight && xSrc == 0 && ySrc == 0) {
SysBlitScreen(xDest, yDest, DestWidth, DestHeight, (const uint32_t*)lpBits);
return DestHeight;
}
const uint32_t* srcPixels = (const uint32_t*)lpBits;
// Only reallocate if the new size is LARGER than what we already have.
int requiredPixels = DestWidth * DestHeight;
if (requiredPixels > currentDestCapacity) {
free(destBuffer);
destBuffer = (uint32_t*)malloc(requiredPixels * sizeof(uint32_t));
currentDestCapacity = requiredPixels;
}
if (DestWidth > currentXMapCapacity) {
free(srcX_map);
srcX_map = (int*)malloc(DestWidth * sizeof(int));
currentXMapCapacity = DestWidth;
}
for (int dx = 0; dx < DestWidth; ++dx) {
// Nearest neighbor coordinate mapping.
srcX_map[dx] = xSrc + (dx * SrcWidth) / DestWidth;
// Clamp to safe bounds.
if (srcX_map[dx] < 0) srcX_map[dx] = 0;
if (srcX_map[dx] >= SrcWidth) srcX_map[dx] = SrcWidth - 1;
}
// Process the image row by row.
for (int dy = 0; dy < DestHeight; ++dy) {
// Map destination Y to source Y.
int sy = ySrc + (dy * SrcHeight) / DestHeight;
if (sy < 0) sy = 0;
if (sy >= SrcHeight) sy = SrcHeight - 1;
// Assumes stride == SrcWidth (tightly packed 32bpp).
const uint32_t* srcRow = srcPixels + (sy * SrcWidth);
uint32_t* dstRow = destBuffer + (dy * DestWidth);
int dx = 0;
#if defined(__SSE2__) || defined(_M_X64)
// Process 4 pixels (16 bytes) per loop iteration using SSE2.
for (; dx <= DestWidth - 4; dx += 4) {
// Gather 4 source pixels using our precomputed X map.
// Note: _mm_set_epi32 takes arguments in reverse order (3, 2, 1, 0).
__m128i pixels = _mm_set_epi32(
srcRow[srcX_map[dx + 3]],
srcRow[srcX_map[dx + 2]],
srcRow[srcX_map[dx + 1]],
srcRow[srcX_map[dx + 0]]
);
// Issue a single 128-bit unaligned store.
_mm_storeu_si128((__m128i*)&dstRow[dx], pixels);
}
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
// Process 4 pixels per loop iteration using ARM NEON.
for (; dx <= DestWidth - 4; dx += 4) {
// Initialize a vector and populate lanes (avoids stack-memory hop).
uint32x4_t pixels = vdupq_n_u32(0);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 0]], pixels, 0);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 1]], pixels, 1);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 2]], pixels, 2);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 3]], pixels, 3);
// Issue a single 128-bit store.
vst1q_u32(&dstRow[dx], pixels);
}
#endif
// Epilogue: Handle remaining pixels if DestWidth isn't a multiple of 4.
for (; dx < DestWidth; ++dx) {
dstRow[dx] = srcRow[srcX_map[dx]];
}
}
// Direct mapping to kernel screen blit using the stretched buffer.
SysBlitScreen(xDest, yDest, DestWidth, DestHeight, destBuffer);
return DestHeight;
// There is no implicit display blit ABI. Native graphics clients own a
// surface and present it explicitly.
return 0;
}
+321 -97
View File
@@ -27,6 +27,8 @@
#include <intrin.h>
#include <ntlite/syscall.h>
extern size_t _malloc_usable_size(const void* ptr);
// Types.
typedef void* HANDLE;
typedef unsigned long DWORD;
@@ -58,6 +60,7 @@ typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
#define MEM_RELEASE 0x00008000UL
#define MEM_FREE 0x00010000UL
#define MEM_PRIVATE 0x00020000UL
#define MEM_MAPPED 0x00040000UL
#define PAGE_NOACCESS 0x01UL
#define PAGE_READONLY 0x02UL
#define PAGE_READWRITE 0x04UL
@@ -65,6 +68,13 @@ typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
#define PAGE_EXECUTE 0x10UL
#define PAGE_EXECUTE_READ 0x20UL
#define PAGE_EXECUTE_READWRITE 0x40UL
#define SEC_COMMIT 0x08000000UL
#define FILE_MAP_COPY 0x00000001UL
#define FILE_MAP_WRITE 0x00000002UL
#define FILE_MAP_READ 0x00000004UL
#define FILE_MAP_EXECUTE 0x00000020UL
#define FILE_MAP_ALL_ACCESS 0x000F001FUL
typedef struct _MEMORY_BASIC_INFORMATION {
void* BaseAddress;
@@ -90,6 +100,26 @@ static uint32_t WinProtectToSysVm(DWORD protect) {
}
}
static uint32_t WinFileMappingProtectToSysVm(DWORD protect) {
// Strip section attributes; the low byte carries PAGE_* protection.
DWORD pageProtect = protect & 0xFFu;
if (pageProtect == PAGE_WRITECOPY)
return SYS_VM_READ; // MapViewOfFile(FILE_MAP_COPY) asks for the private writable view.
return WinProtectToSysVm(pageProtect ? pageProtect : PAGE_READONLY);
}
static uint32_t WinFileMapAccessToSys(DWORD desiredAccess) {
uint32_t access = 0;
if (desiredAccess & FILE_MAP_EXECUTE) access |= SYS_MAP_ACCESS_EXECUTE;
if (desiredAccess & FILE_MAP_COPY) access |= SYS_MAP_ACCESS_COPY;
if (desiredAccess & FILE_MAP_WRITE) access |= SYS_MAP_ACCESS_WRITE;
if (desiredAccess & FILE_MAP_READ) access |= SYS_MAP_ACCESS_READ;
if ((desiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS)
access |= SYS_MAP_ACCESS_READ | SYS_MAP_ACCESS_COPY;
if (!access) access = SYS_MAP_ACCESS_READ;
return access;
}
// Implements the protect Vm To Win operation.
static DWORD SysProtectVmToWin(uint32_t protect) {
switch (protect) {
@@ -153,24 +183,9 @@ typedef struct _WIN32_FIND_DATAA {
static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; }
static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; }
// Thread IDs are not kernel handles. Keep Win32 thread pseudo-handles out of
// the low numeric handle namespace so CloseHandle/WaitForSingleObject cannot
// accidentally operate on an unrelated file/process handle with the same value.
#define WIN32_THREAD_HANDLE_TAG ((uintptr_t)1ULL << 63)
#define WIN32_MUTEX_SENTINEL ((uintptr_t)0x4D555458UL) // 'MUTX'
#define WIN32_SEMA_SENTINEL ((uintptr_t)0x53454D41UL) // 'SEMA'
static inline HANDLE TID_TO_THREAD_HANDLE(uint32_t tid) {
return (HANDLE)(WIN32_THREAD_HANDLE_TAG | (uintptr_t)tid);
}
static inline BOOL IS_THREAD_HANDLE(HANDLE h) {
return h && h != INVALID_HANDLE_VALUE && (((uintptr_t)h & WIN32_THREAD_HANDLE_TAG) != 0);
}
static inline uint32_t THREAD_HANDLE_TO_TID(HANDLE h) {
return (uint32_t)((uintptr_t)h & 0xFFFFFFFFu);
}
static inline BOOL IS_NOOP_SYNC_HANDLE(HANDLE h) {
static inline BOOL IS_VALID_KERNEL_HANDLE(HANDLE h) {
uintptr_t v = (uintptr_t)h;
return v == WIN32_MUTEX_SENTINEL || v == WIN32_SEMA_SENTINEL;
return v != 0 && v != (uintptr_t)INVALID_HANDLE_VALUE;
}
// Path translator.
@@ -268,11 +283,14 @@ static void K32DebugPrintA(const char* s) {
}
#define WIN32_TLS_SLOTS 64
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
typedef struct WIN32_THREAD_ENV {
DWORD LastError;
DWORD Reserved0;
void* TlsSlots[WIN32_TLS_SLOTS];
void* FlsSlots[FLS_MAX_SLOTS];
} WIN32_THREAD_ENV;
static WIN32_THREAD_ENV gFallbackThreadEnv;
@@ -418,11 +436,27 @@ __declspec(dllexport) uint64_t GetTickCount64(void) {
}
// Memory (heap argument is ignored - single user-mode heap).
#define HEAP_ZERO_MEMORY 0x00000008UL
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010UL
__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
(void)heap; (void)flags;
(void)heap;
if (!size) return NULL;
void* ptr = malloc(size);
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
if (!ptr) {
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return NULL;
}
// Bink and the Microsoft CRT routinely allocate internal state with
// HEAP_ZERO_MEMORY. Ignoring this flag only works accidentally while the
// heap keeps receiving fresh demand-zero pages; under low memory / swap the
// allocator reuses dirty freed blocks and stale pitch/pointer fields can
// corrupt the decoder.
if (flags & HEAP_ZERO_MEMORY)
memset(ptr, 0, size);
return ptr;
}
@@ -436,9 +470,28 @@ __declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) {
__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags,
void* ptr, size_t size) {
(void)heap; (void)flags;
(void)heap;
if (flags & HEAP_REALLOC_IN_PLACE_ONLY) {
// The user heap does not expose an in-place grow primitive. Preserve
// Windows semantics by failing instead of silently moving the block.
size_t oldSize = _malloc_usable_size(ptr);
if (oldSize != (size_t)-1 && oldSize >= size)
return ptr;
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return NULL;
}
size_t oldSize = ptr ? _malloc_usable_size(ptr) : 0;
void* newptr = realloc(ptr, size);
if (!newptr && size) gLastError = 8;
if (!newptr && size) {
gLastError = 8;
return NULL;
}
if (newptr && (flags & HEAP_ZERO_MEMORY) && oldSize != (size_t)-1 && size > oldSize)
memset((unsigned char*)newptr + oldSize, 0, size - oldSize);
return newptr;
}
@@ -508,10 +561,88 @@ __declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
lpBuffer->RegionSize = info.PageCount * 4096u;
lpBuffer->State = MEM_COMMIT;
lpBuffer->Protect = protect;
lpBuffer->Type = MEM_PRIVATE;
lpBuffer->Type = (info.Type == 7u) ? MEM_MAPPED : MEM_PRIVATE; // 7 == MmVadMappedFile
return sizeof(MEMORY_BASIC_INFORMATION);
}
__declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
void* lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
const char* lpName) {
(void)lpFileMappingAttributes;
(void)lpName;
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
gLastError = 6; // ERROR_INVALID_HANDLE
return NULL;
}
uint64_t maximumSize = ((uint64_t)dwMaximumSizeHigh << 32) | (uint64_t)dwMaximumSizeLow;
uint32_t kh = 0;
uint32_t s = SysCreateFileMapping(HANDLE_TO_KH(hFile),
WinFileMappingProtectToSysVm(flProtect),
SYS_MAP_FLAG_NONE,
maximumSize,
&kh);
if (s != SYS_STATUS_SUCCESS) {
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return KH_TO_HANDLE(kh);
}
__declspec(dllexport) HANDLE CreateFileMappingW(HANDLE hFile,
void* lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
const unsigned short* lpName) {
(void)lpName;
return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect,
dwMaximumSizeHigh, dwMaximumSizeLow, NULL);
}
__declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
size_t dwNumberOfBytesToMap) {
if (!hFileMappingObject || hFileMappingObject == INVALID_HANDLE_VALUE) {
gLastError = 6;
return NULL;
}
uint64_t offset = ((uint64_t)dwFileOffsetHigh << 32) | (uint64_t)dwFileOffsetLow;
void* base = NULL;
uint32_t s = SysMapFile(HANDLE_TO_KH(hFileMappingObject),
offset,
dwNumberOfBytesToMap,
WinFileMapAccessToSys(dwDesiredAccess),
SYS_MAP_FLAG_NONE,
&base);
if (s != SYS_STATUS_SUCCESS) {
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return base;
}
__declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) {
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
uint32_t s = SysFreeVm((void*)lpBaseAddress);
if (s != SYS_STATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
__declspec(dllexport) BOOL FlushViewOfFile(const void* lpBaseAddress, size_t dwNumberOfBytesToFlush) {
(void)dwNumberOfBytesToFlush;
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
// Native flush is file-handle based for now; unmap/close also harvests dirty
// PTEs, so FlushViewOfFile remains a successful cache-coherency hint.
return TRUE;
}
// --- Win32 API ---.
// GetAsyncKeyState implementation over the canonical input key namespace.
@@ -720,7 +851,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return 0xFFFFFFFFUL; }
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
@@ -731,25 +862,27 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) {
(void)overlapped;
if (bytesRead) *bytesRead = 0;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) {
gLastError = 6;
return FALSE;
}
size_t got = 0;
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
uint32_t s = SysReadFile(&req);
if (bytesRead) *bytesRead = (DWORD)got;
// Harden callers that accidentally look past *bytesRead. EOF/short reads
// are legal for files, but consuming stale stack/heap bytes after them makes
// the failure mode look like random game-data corruption.
if (got < (size_t)toRead)
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
if (s != 0) {
gLastError = 1;
return FALSE;
}
// Harden callers that accidentally look past *bytesRead. EOF/short reads
// are legal for files, but do not touch the caller's remaining buffer after
// a failed read because that can fault in megabytes under low memory.
if (got < (size_t)toRead)
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
return TRUE;
}
@@ -758,9 +891,10 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)overlapped;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) { gLastError = 6; return FALSE; }
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; }
size_t written = 0;
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
uint32_t s = SysWriteFile(&req);
if (bytesWritten) *bytesWritten = (DWORD)written;
if (s != 0) { gLastError = 29; return FALSE; } // ERROR_WRITE_FAULT
return TRUE;
@@ -770,7 +904,7 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit
// Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
@@ -813,7 +947,7 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
// SetEndOfFile - truncate or extend the file to the current seek position.
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return FALSE; }
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
@@ -834,14 +968,10 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
return TRUE;
}
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
// CloseHandle - unified close for files, threads, mutexes, semaphores, events, queues, and processes.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject || hObject == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
// Our thread/sentinel sync handles are not entries in the kernel handle table.
// There is nothing to close; the thread object is reaped by the scheduler.
if (IS_THREAD_HANDLE(hObject) || IS_NOOP_SYNC_HANDLE(hObject)) return TRUE;
SysCloseHandle(HANDLE_TO_KH(hObject));
return TRUE;
}
@@ -870,8 +1000,11 @@ __declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
// Implements the heap Size operation.
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
(void)heap; (void)flags; (void)ptr;
return (size_t)-1; // Win32 returns (SIZE_T)-1 on error / unsupported
(void)heap; (void)flags;
size_t size = _malloc_usable_size(ptr);
if (size == (size_t)-1)
gLastError = 87; // ERROR_INVALID_PARAMETER
return size;
}
__declspec(dllexport) BOOL HeapSetInformation(HANDLE HeapHandle,
@@ -1050,9 +1183,29 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
// Implements the terminate Process operation.
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
(void)hProcess;
SysExitProcess((int)uExitCode);
while (true) ;
if (hProcess == GetCurrentProcess()) {
SysExitProcess((int)uExitCode);
while (true) ;
}
if (!hProcess) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
uint32_t pid = 0;
uint32_t s = SysGetProcessPid(HANDLE_TO_KH(hProcess), &pid);
if (s != 0 || pid == 0) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
s = SysKillProcess(pid, (int)uExitCode);
if (s != 0) {
gLastError = 5; // ERROR_ACCESS_DENIED / generic failure for now
return FALSE;
}
return TRUE;
}
// STARTUPINFOA - only cb matters for our stub.
@@ -1099,35 +1252,42 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
DWORD* lpThreadId) {
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)(uintptr_t)lpStartAddress,
(uint64_t)(uintptr_t)lpParameter,
0, &tid);
uint32_t kh = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)lpStartAddress,
(uintptr_t)lpParameter,
0,
0,
&kh,
&tid
};
uint32_t s = SysCreateThread(&req);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid;
return TID_TO_THREAD_HANDLE(tid);
return KH_TO_HANDLE(kh);
}
// WaitForSingleObject - blocks on process handles via SysWaitForProcess;
// thread and other handle types are not yet waitable so return immediately.
// WaitForSingleObject - blocks on native waitable handles. A zero timeout is
// only a probe for mutexes/semaphores until native timeout waits exist.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
(void)dwMilliseconds;
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
if (IS_THREAD_HANDLE(hHandle)) {
uint32_t tid = THREAD_HANDLE_TO_TID(hHandle);
uint32_t s = SysWaitThread(tid);
if (s != 0 && s != 0x0022) { gLastError = 6; return 0xFFFFFFFFUL; }
return WAIT_OBJECT_0;
}
if (IS_NOOP_SYNC_HANDLE(hHandle)) return WAIT_OBJECT_0;
if (!IS_VALID_KERNEL_HANDLE(hHandle)) { gLastError = 6; return 0xFFFFFFFFUL; }
uint32_t kh = HANDLE_TO_KH(hHandle);
if (kh) {
uint32_t s = SysWaitForProcess(kh);
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
if (dwMilliseconds == 0) {
uint32_t s = SysTryAcquireMutex(kh);
if (s == 0) return WAIT_OBJECT_0;
s = SysTryWaitSem(kh);
if (s == 0) return WAIT_OBJECT_0;
return WAIT_TIMEOUT;
}
return WAIT_OBJECT_0;
uint32_t s = SysWait(kh, SYS_WAIT_FLAG_NONE);
if (s == 0) return WAIT_OBJECT_0;
gLastError = 6;
return 0xFFFFFFFFUL;
}
@@ -1250,7 +1410,7 @@ __declspec(dllexport) BOOL CreateProcessA(
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
if (!lpExitCode) { gLastError = 87; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hProcess);
if (!kh || IS_THREAD_HANDLE(hProcess)) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
int code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
@@ -1791,57 +1951,123 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
return (HANDLE)WIN32_MUTEX_SENTINEL;
(void)lpMutexAttributes; (void)lpName;
uint32_t kh = 0;
uint32_t s = SysCreateMutex(&kh);
if (s != 0 || !kh) { gLastError = 8; return NULL; }
if (bInitialOwner) {
s = SysWait(kh, SYS_WAIT_FLAG_NONE);
if (s != 0) {
SysCloseHandle(kh);
gLastError = 1;
return NULL;
}
}
return KH_TO_HANDLE(kh);
}
// Releases mutex.
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
(void)hMutex;
if (!IS_VALID_KERNEL_HANDLE(hMutex)) { gLastError = 6; return FALSE; }
uint32_t s = SysReleaseMutex(HANDLE_TO_KH(hMutex));
if (s != 0) { gLastError = 6; return FALSE; }
return TRUE;
}
__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
long lInitialCount, long lMaximumCount,
const char* lpName) {
(void)lpSemaphoreAttributes; (void)lInitialCount;
(void)lMaximumCount; (void)lpName;
return (HANDLE)WIN32_SEMA_SENTINEL;
(void)lpSemaphoreAttributes; (void)lpName;
uint32_t kh = 0;
uint32_t s = SysCreateSem((int32_t)lInitialCount, (int32_t)lMaximumCount, &kh);
if (s != 0 || !kh) { gLastError = 8; return NULL; }
return KH_TO_HANDLE(kh);
}
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
long* lpPreviousCount) {
(void)hSemaphore; (void)lReleaseCount;
if (!IS_VALID_KERNEL_HANDLE(hSemaphore) || lReleaseCount < 0) { gLastError = 6; return FALSE; }
if (lpPreviousCount) *lpPreviousCount = 0;
for (long i = 0; i < lReleaseCount; ++i) {
uint32_t s = SysSignalSem(HANDLE_TO_KH(hSemaphore));
if (s != 0) { gLastError = 6; return FALSE; }
}
return TRUE;
}
// Critical Sections & Condition Variables
// Single-threaded: all locks are no-ops.
// Minimal opaque types; callers allocate these from their Windows headers -
// we just need the pointer to be accepted by our stubs.
// Critical Sections & Condition Variables.
// Match the size of Win64 RTL_CRITICAL_SECTION (5 pointer-sized fields) while
// using the storage as a small recursive spin lock. Bink uses these from its
// worker/copy paths, so the old no-op implementation allowed internal state
// races as soon as CreateThread started working.
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
#define K32_CS_LOCK 0
#define K32_CS_OWNER 1
#define K32_CS_RECURSION 2
static volatile long* K32CsLockWord(CRITICAL_SECTION* cs) {
return (volatile long*)&cs->_opaque[K32_CS_LOCK];
}
static DWORD K32CurrentTidOrOne(void) {
DWORD tid = (DWORD)SysGetCurrentTid();
return tid ? tid : 1;
}
// Initializes critical Section.
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS;
if (!lpCS) return;
memset(lpCS, 0, sizeof(*lpCS));
}
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS,
DWORD dwSpinCount) {
(void)lpCS; (void)dwSpinCount;
(void)dwSpinCount;
InitializeCriticalSection(lpCS);
return TRUE;
}
// Releases critical Section.
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
// Implements the enter Critical Section operation.
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
lpCS->_opaque[K32_CS_RECURSION]++;
return;
}
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
_mm_pause();
lpCS->_opaque[K32_CS_OWNER] = tid;
lpCS->_opaque[K32_CS_RECURSION] = 1;
}
// Implements the leave Critical Section operation.
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] != tid || lpCS->_opaque[K32_CS_RECURSION] == 0)
return;
lpCS->_opaque[K32_CS_RECURSION]--;
if (lpCS->_opaque[K32_CS_RECURSION] == 0) {
lpCS->_opaque[K32_CS_OWNER] = 0;
_ReadWriteBarrier();
_InterlockedExchange(K32CsLockWord(lpCS), 0);
}
}
// Implements the try Enter Critical Section operation.
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS; return TRUE;
if (!lpCS) return FALSE;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
lpCS->_opaque[K32_CS_RECURSION]++;
return TRUE;
}
if (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
return FALSE;
lpCS->_opaque[K32_CS_OWNER] = tid;
lpCS->_opaque[K32_CS_RECURSION] = 1;
return TRUE;
}
// Initializes condition Variable.
@@ -2357,14 +2583,12 @@ __declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType,
return 0;
}
// Fiber Local Storage (FLS)
// We implement a minimal 64-slot static table so callers that use FLS for
// thread-local-ish state don't immediately fail.
// Fiber Local Storage (FLS).
// Allocation state is process-global, but the values themselves are per-thread,
// matching the contract that Bink and the CRT expect when worker threads are
// enabled. The old single global value array let unrelated threads overwrite
// each other's decoder-local state.
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
static void* gFlsSlots[FLS_MAX_SLOTS];
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
// Implements the fls Alloc operation.
@@ -2372,8 +2596,8 @@ __declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
(void)lpCallback;
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
if (!gFlsUsed[i]) {
gFlsUsed[i] = 1;
gFlsSlots[i] = NULL;
gFlsUsed[i] = 1;
K32ThreadEnv()->FlsSlots[i] = NULL;
return (DWORD)i;
}
}
@@ -2387,7 +2611,7 @@ __declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return NULL;
}
return gFlsSlots[dwFlsIndex];
return K32ThreadEnv()->FlsSlots[dwFlsIndex];
}
// Implements the fls Set Value operation.
@@ -2396,7 +2620,7 @@ __declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
gLastError = 87;
return FALSE;
}
gFlsSlots[dwFlsIndex] = lpFlsData;
K32ThreadEnv()->FlsSlots[dwFlsIndex] = lpFlsData;
return TRUE;
}
@@ -2406,8 +2630,8 @@ __declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
gLastError = 87;
return FALSE;
}
gFlsUsed[dwFlsIndex] = 0;
gFlsSlots[dwFlsIndex] = NULL;
gFlsUsed[dwFlsIndex] = 0;
K32ThreadEnv()->FlsSlots[dwFlsIndex] = NULL;
return TRUE;
}