implement user mode catchable exceptions, refactor system status codes to use NTLSTATUS_ prefix, update build scripts to use shared literal macros for linker arguments, update named queue lifetime cleanup to include KeNamedQueueUnpublishHandle.

This commit is contained in:
maksw committed 2026-06-27 20:38:43 +02:00
1 parent 1f5477cf66
commit b7fcfce6f7
140 files changed
+7649 -3943

No files matched your search

+531 -48
View File
@@ -23,12 +23,14 @@
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
#include <ntlite/exception.h>
extern size_t _malloc_usable_size(const void* ptr);
// Types.
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef unsigned long long DWORD64;
typedef unsigned short WORD;
typedef int BOOL;
typedef unsigned char BYTE;
@@ -252,13 +254,13 @@ static uint32_t K32SetCwdNative(const char* path) {
// Implements the k32 Get Cwd Native operation.
static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
SYS_STRING_VIEW cwd = SysGetCurrentDirectoryView();
if ((cwd.Length && !cwd.Data) || cwd.Length > SYS_CWD_MAX) return STATUS_INVALID_PARAMETER;
if ((cwd.Length && !cwd.Data) || cwd.Length > SYS_CWD_MAX) return NTLSTATUS_INVALID_PARAMETER;
if (lengthOut) *lengthOut = cwd.Length;
if (!buffer || cwd.Length + 1 > capacity) return STATUS_BUFFER_TOO_SMALL;
if (!buffer || cwd.Length + 1 > capacity) return NTLSTATUS_BUFFER_TOO_SMALL;
if (cwd.Length) memcpy(buffer, cwd.Data, cwd.Length);
buffer[cwd.Length] = '\0';
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
// Implements the k32 Create Process Native operation.
@@ -289,6 +291,7 @@ typedef struct WIN32_THREAD_ENV {
DWORD Reserved0;
void* TlsSlots[WIN32_TLS_SLOTS];
void* FlsSlots[FLS_MAX_SLOTS];
void* ExceptionFrameHead;
} WIN32_THREAD_ENV;
static WIN32_THREAD_ENV gFallbackThreadEnv;
@@ -429,7 +432,7 @@ __declspec(dllexport) void OutputDebugStringA(const char* str) {
// Queries the native monotonic tick frequency.
static uint64_t K32TickFrequency(void) {
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS) return 0;
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS) return 0;
return frequency;
}
@@ -525,9 +528,17 @@ __declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize,
size_t pages = (dwSize + 4095u) / 4096u;
void* base = NULL;
uint32_t flags = lpAddress ? SYS_VM_FLAG_FIXED : SYS_VM_FLAG_NONE;
uint32_t s = SysAllocVm(lpAddress, pages, WinProtectToSysVm(flProtect), flags, &base);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_FOUND || s == STATUS_INVALID_PARAMETER) ? 87 : 8;
SYS_ALLOC_VM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.PreferredBase = lpAddress;
req.PageCount = pages;
req.Protect = WinProtectToSysVm(flProtect);
req.Flags = flags;
req.BaseOut = &base;
KSTATUS s = SysAllocVm(&req);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_FOUND || s == NTLSTATUS_INVALID_PARAMETER) ? 87 : 8;
return NULL;
}
return base;
@@ -538,7 +549,7 @@ __declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwF
(void)dwSize; (void)dwFreeType;
if (!lpAddress) return TRUE;
uint32_t s = SysFreeVm(lpAddress);
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
@@ -549,7 +560,7 @@ __declspec(dllexport) BOOL VirtualProtect(void* lpAddress, size_t dwSize,
uint32_t oldSysProtect = 0;
uint32_t s = SysProtectVm(lpAddress, pages, WinProtectToSysVm(flNewProtect),
lpflOldProtect ? &oldSysProtect : NULL);
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (lpflOldProtect) *lpflOldProtect = SysProtectVmToWin(oldSysProtect);
return TRUE;
}
@@ -564,7 +575,7 @@ __declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
SYS_VM_REGION_INFO info;
uint32_t s = SysQueryVm((void*)lpAddress, &info);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
gLastError = 87;
return 0;
}
@@ -600,8 +611,8 @@ __declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
SYS_MAP_FLAG_NONE,
maximumSize,
&kh);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_SUPPORTED) ? 120 : ((s == STATUS_NO_MEMORY) ? 8 : 87);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return KH_TO_HANDLE(kh);
@@ -636,8 +647,8 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
WinFileMapAccessToSys(dwDesiredAccess),
SYS_MAP_FLAG_NONE,
&base);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_SUPPORTED) ? 120 : ((s == STATUS_NO_MEMORY) ? 8 : 87);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return base;
@@ -646,7 +657,7 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
__declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) {
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
uint32_t s = SysFreeVm((void*)lpBaseAddress);
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
@@ -663,7 +674,7 @@ __declspec(dllexport) BOOL FlushViewOfFile(const void* lpBaseAddress, size_t dwN
// Returns whether one input key is currently held for Win32 adapters.
static bool Win32IsInputKeyDown(INPUT_KEY key) {
bool down = false;
return SysIsKeyDown(key, &down) == STATUS_SUCCESS && down;
return SysIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
}
// GetAsyncKeyState implementation over the canonical input key namespace.
@@ -731,7 +742,7 @@ __declspec(dllexport) BOOL FlushConsoleInputBuffer(HANDLE hConsoleInput) {
(void)hConsoleInput;
// Drain the keyboard buffer by reading until empty.
INPUT_KEY key;
while (SysPollKeyboard(&key) == STATUS_SUCCESS) {
while (SysPollKeyboard(&key) == NTLSTATUS_SUCCESS) {
char ignored;
(void)SysReadKeyboardChar(&ignored);
}
@@ -832,8 +843,8 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
uint32_t kh = 0;
uint32_t s = K32OpenFileNative(translatedName, sysAccess, sysDisposition, &kh);
if (s != 0) {
gLastError = (s == STATUS_OBJECT_NAME_COLLISION) ? 80 :
(s == STATUS_NOT_FOUND) ? 2 : 5;
gLastError = (s == NTLSTATUS_OBJECT_NAME_COLLISION) ? 80 :
(s == NTLSTATUS_NOT_FOUND) ? 2 : 5;
return INVALID_HANDLE_VALUE;
}
@@ -1115,7 +1126,7 @@ __declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
req.ModuleHandleOut = &handle;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
gLastError = 126;
return NULL;
}
@@ -1160,7 +1171,7 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
req.Name = SysSvFromCstr(lpProcName);
req.AddressOut = &address;
uint32_t status = SysGetModuleExport(&req);
if (status != STATUS_SUCCESS || !address) { gLastError = 127; return NULL; }
if (status != NTLSTATUS_SUCCESS || !address) { gLastError = 127; return NULL; }
return (void*)address;
}
@@ -1184,7 +1195,7 @@ __declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
if (!hLibModule || hLibModule == WIN32_HMODULE_WIN32 || hLibModule == WIN32_HMODULE_EXE)
return TRUE;
uint32_t status = SysUnloadModule((uint32_t)(uintptr_t)hLibModule);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
gLastError = 126;
return FALSE;
}
@@ -1223,7 +1234,7 @@ __declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitC
uint32_t s = SysKillProcess(HANDLE_TO_KH(hProcess), (int32_t)uExitCode);
if (s != 0) {
gLastError = (s == STATUS_INVALID_HANDLE) ? 6 : 5;
gLastError = (s == NTLSTATUS_INVALID_HANDLE) ? 6 : 5;
return FALSE;
}
return TRUE;
@@ -1491,13 +1502,13 @@ static BOOL K32IsConsoleOutputHandle(HANDLE h) {
// Implements the k32 Set Console Last Error operation.
static void K32SetConsoleLastError(uint32_t status) {
switch (status) {
case STATUS_SUCCESS:
case NTLSTATUS_SUCCESS:
gLastError = 0;
break;
case STATUS_INVALID_PARAMETER:
case NTLSTATUS_INVALID_PARAMETER:
gLastError = 87;
break;
case STATUS_NO_MEMORY:
case NTLSTATUS_NO_MEMORY:
gLastError = 8;
break;
default:
@@ -1527,7 +1538,7 @@ __declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
while (i < nNumberOfCharsToRead) {
char c = 0;
if (SysReadKeyboardChar(&c) != STATUS_SUCCESS) continue;
if (SysReadKeyboardChar(&c) != NTLSTATUS_SUCCESS) continue;
// Ignore unmapped keys (Shift, Ctrl, etc.).
if (c == 0) continue;
@@ -1579,7 +1590,7 @@ __declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput,
SYS_CONSOLE_INFO info;
uint32_t s = SysGetConsoleInfo(&info);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1610,7 +1621,7 @@ __declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput,
}
uint32_t s = SysSetConsoleCursor((int)dwCursorPosition.X, (int)dwCursorPosition.Y);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1639,7 +1650,7 @@ __declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = (DWORD)written;
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1668,7 +1679,7 @@ __declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = (DWORD)written;
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -2220,7 +2231,7 @@ __declspec(dllexport) void GetSystemTime(SYSTEMTIME* lpSystemTime) {
if (!lpSystemTime) return;
SYS_SYSTEM_TIME st;
if (SysGetSystemTime(&st) != STATUS_SUCCESS) {
if (SysGetSystemTime(&st) != NTLSTATUS_SUCCESS) {
memset(lpSystemTime, 0, sizeof(*lpSystemTime));
return;
}
@@ -2258,7 +2269,7 @@ __declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime)
SYS_SYSTEM_TIME st;
uint64_t ft;
if (SysGetSystemTime(&st) == STATUS_SUCCESS) {
if (SysGetSystemTime(&st) == NTLSTATUS_SUCCESS) {
ft = K32EpochToFileTime(st.EpochSeconds, st.Nanosecond);
} else {
uint64_t frequency = K32TickFrequency();
@@ -2436,32 +2447,504 @@ __declspec(dllexport) DWORD GetFileType(HANDLE hFile) {
// Exception Handling & Debugging.
#define EXCEPTION_CONTINUE_EXECUTION ((LONG)-1)
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_EXECUTE_HANDLER 1
#define EXCEPTION_NONCONTINUABLE 0x1UL
#define EXCEPTION_MAXIMUM_PARAMETERS SYS_EXCEPTION_MAXIMUM_PARAMETERS
#define WIN_VECTORED_MAX_HANDLERS 16u
#define WIN_SEH_MAX_FRAMES 16u
// Implements the add Vectored Exception Handler operation.
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
(void)First; (void)Handler;
// Return a non-NULL sentinel so callers can pass it to RemoveVectoredExceptionHandler.
return (void*)(uintptr_t)0x56455843UL; // 'VEXC'
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD* ExceptionRecord;
void* ExceptionAddress;
DWORD NumberParameters;
uintptr_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
typedef struct _CONTEXT {
DWORD64 P1Home;
DWORD64 P2Home;
DWORD64 P3Home;
DWORD64 P4Home;
DWORD64 P5Home;
DWORD64 P6Home;
DWORD ContextFlags;
DWORD MxCsr;
WORD SegCs;
WORD SegDs;
WORD SegEs;
WORD SegFs;
WORD SegGs;
WORD SegSs;
DWORD EFlags;
DWORD64 Dr0;
DWORD64 Dr1;
DWORD64 Dr2;
DWORD64 Dr3;
DWORD64 Dr6;
DWORD64 Dr7;
DWORD64 Rax;
DWORD64 Rcx;
DWORD64 Rdx;
DWORD64 Rbx;
DWORD64 Rsp;
DWORD64 Rbp;
DWORD64 Rsi;
DWORD64 Rdi;
DWORD64 R8;
DWORD64 R9;
DWORD64 R10;
DWORD64 R11;
DWORD64 R12;
DWORD64 R13;
DWORD64 R14;
DWORD64 R15;
DWORD64 Rip;
} CONTEXT;
typedef struct _EXCEPTION_POINTERS {
EXCEPTION_RECORD* ExceptionRecord;
CONTEXT* ContextRecord;
} EXCEPTION_POINTERS;
typedef LONG (*PVECTORED_EXCEPTION_HANDLER)(EXCEPTION_POINTERS* ExceptionInfo);
typedef LONG (*PRTL_EXCEPTION_FRAME_HANDLER)(EXCEPTION_POINTERS* ExceptionInfo, void* UserContext);
typedef struct _RTL_EXCEPTION_FRAME {
DWORD Size;
DWORD Flags;
struct _RTL_EXCEPTION_FRAME* Previous;
PRTL_EXCEPTION_FRAME_HANDLER Handler;
void* UserContext;
} RTL_EXCEPTION_FRAME;
typedef struct WIN_VECTORED_HANDLER_ENTRY {
uint32_t Active;
uint32_t IsContinue;
uint32_t Previous;
uint32_t Next;
PVECTORED_EXCEPTION_HANDLER Handler;
} WIN_VECTORED_HANDLER_ENTRY;
typedef struct WIN_VECTORED_HANDLER_SNAPSHOT {
PVECTORED_EXCEPTION_HANDLER Handler;
} WIN_VECTORED_HANDLER_SNAPSHOT;
typedef struct WIN_SEH_FRAME_SNAPSHOT {
PRTL_EXCEPTION_FRAME_HANDLER Handler;
void* UserContext;
} WIN_SEH_FRAME_SNAPSHOT;
static volatile long gWinVectoredLock;
static uint32_t gWinVectoredExceptionHead = UINT32_MAX;
static uint32_t gWinVectoredExceptionTail = UINT32_MAX;
static uint32_t gWinVectoredContinueHead = UINT32_MAX;
static uint32_t gWinVectoredContinueTail = UINT32_MAX;
static WIN_VECTORED_HANDLER_ENTRY gWinVectoredHandlers[WIN_VECTORED_MAX_HANDLERS];
static void* gWinExceptionNativeHandle;
// Acquires the Win32 vectored-handler list lock.
static void WinpVectoredLock(void) {
while (_InterlockedCompareExchange(&gWinVectoredLock, 1, 0) != 0)
_mm_pause();
_ReadWriteBarrier();
}
// Releases vectored Exception Handler.
// Releases the Win32 vectored-handler list lock.
static void WinpVectoredUnlock(void) {
_ReadWriteBarrier();
_InterlockedExchange(&gWinVectoredLock, 0);
}
// Returns the list head slot for one vectored-handler kind.
static uint32_t* WinpVectoredHeadSlot(uint32_t isContinue) {
return isContinue ? &gWinVectoredContinueHead : &gWinVectoredExceptionHead;
}
// Returns the list tail slot for one vectored-handler kind.
static uint32_t* WinpVectoredTailSlot(uint32_t isContinue) {
return isContinue ? &gWinVectoredContinueTail : &gWinVectoredExceptionTail;
}
// Copies native exception state into the Win32-facing exception record.
static void WinpBuildExceptionRecord(const SYS_EXCEPTION_RECORD* source, EXCEPTION_RECORD* target) {
memset(target, 0, sizeof(*target));
target->ExceptionCode = source->Code;
if (source->Flags & SYS_EXCEPTION_FLAG_NONCONTINUABLE)
target->ExceptionFlags |= EXCEPTION_NONCONTINUABLE;
target->ExceptionAddress = (void*)(uintptr_t)source->ExceptionAddress;
target->NumberParameters = source->ParameterCount;
if (target->NumberParameters > EXCEPTION_MAXIMUM_PARAMETERS)
target->NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS;
for (DWORD i = 0; i < target->NumberParameters; i++)
target->ExceptionInformation[i] = (uintptr_t)source->Parameters[i];
}
// Copies native user context into the subset of x64 CONTEXT currently exposed by Win32.
static void WinpBuildContext(const SYS_USER_CONTEXT* source, CONTEXT* target) {
memset(target, 0, sizeof(*target));
target->SegCs = (WORD)source->SegCs;
target->SegSs = (WORD)source->SegSs;
target->EFlags = (DWORD)source->Rflags;
target->Rax = source->Rax;
target->Rcx = source->Rcx;
target->Rdx = source->Rdx;
target->Rbx = source->Rbx;
target->Rsp = source->Rsp;
target->Rbp = source->Rbp;
target->Rsi = source->Rsi;
target->Rdi = source->Rdi;
target->R8 = source->R8;
target->R9 = source->R9;
target->R10 = source->R10;
target->R11 = source->R11;
target->R12 = source->R12;
target->R13 = source->R13;
target->R14 = source->R14;
target->R15 = source->R15;
target->Rip = source->Rip;
}
// Copies handler-edited Win32 context back to the native resume context.
static void WinpStoreContext(SYS_USER_CONTEXT* target, const CONTEXT* source) {
target->Rflags = source->EFlags;
target->Rax = source->Rax;
target->Rcx = source->Rcx;
target->Rdx = source->Rdx;
target->Rbx = source->Rbx;
target->Rsp = source->Rsp;
target->Rbp = source->Rbp;
target->Rsi = source->Rsi;
target->Rdi = source->Rdi;
target->R8 = source->R8;
target->R9 = source->R9;
target->R10 = source->R10;
target->R11 = source->R11;
target->R12 = source->R12;
target->R13 = source->R13;
target->R14 = source->R14;
target->R15 = source->R15;
target->Rip = source->Rip;
}
// Copies one vectored-handler list so callbacks can run without the list lock.
static size_t WinpSnapshotVectoredHandlers(uint32_t isContinue,
WIN_VECTORED_HANDLER_SNAPSHOT* snapshot,
size_t capacity) {
size_t count = 0;
WinpVectoredLock();
uint32_t head = *WinpVectoredHeadSlot(isContinue);
for (uint32_t index = head;
index != UINT32_MAX && count < capacity;
index = gWinVectoredHandlers[index].Next) {
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[index];
if (!entry->Active || entry->IsContinue != isContinue || !entry->Handler)
continue;
snapshot[count++].Handler = entry->Handler;
}
WinpVectoredUnlock();
return count;
}
// Runs one Win32 vectored-handler list against the current exception state.
static int WinpRunVectoredList(uint32_t isContinue,
EXCEPTION_POINTERS* pointers,
SYS_USER_CONTEXT* nativeContext) {
WIN_VECTORED_HANDLER_SNAPSHOT snapshot[WIN_VECTORED_MAX_HANDLERS];
size_t count = WinpSnapshotVectoredHandlers(isContinue, snapshot, WIN_VECTORED_MAX_HANDLERS);
for (size_t i = 0; i < count; i++) {
LONG disposition = snapshot[i].Handler(pointers);
if (disposition == EXCEPTION_CONTINUE_SEARCH)
continue;
if (disposition == EXCEPTION_CONTINUE_EXECUTION) {
if (pointers->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
return NTL_EXCEPTION_TERMINATE_PROCESS;
WinpStoreContext(nativeContext, pointers->ContextRecord);
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
}
return NTL_EXCEPTION_CONTINUE_SEARCH;
}
// Returns the current thread's top runtime-registered SEH frame.
static RTL_EXCEPTION_FRAME* WinpSehFrameHead(void) {
return (RTL_EXCEPTION_FRAME*)K32ThreadEnv()->ExceptionFrameHead;
}
// Updates the current thread's top runtime-registered SEH frame.
static void WinpSetSehFrameHead(RTL_EXCEPTION_FRAME* frame) {
K32ThreadEnv()->ExceptionFrameHead = frame;
}
// Copies the current SEH frame chain so callbacks can mutate registration safely.
static size_t WinpSnapshotSehFrames(WIN_SEH_FRAME_SNAPSHOT* snapshot, size_t capacity) {
size_t count = 0;
for (RTL_EXCEPTION_FRAME* frame = WinpSehFrameHead();
frame && count < capacity;
frame = frame->Previous) {
if (frame->Size != sizeof(*frame) || !frame->Handler)
break;
snapshot[count].Handler = frame->Handler;
snapshot[count].UserContext = frame->UserContext;
count++;
}
return count;
}
// Runs runtime-registered SEH frames after VEH has declined the exception.
static int WinpRunSehFrames(EXCEPTION_POINTERS* pointers, SYS_USER_CONTEXT* nativeContext) {
WIN_SEH_FRAME_SNAPSHOT snapshot[WIN_SEH_MAX_FRAMES];
size_t count = WinpSnapshotSehFrames(snapshot, WIN_SEH_MAX_FRAMES);
for (size_t i = 0; i < count; i++) {
LONG disposition = snapshot[i].Handler(pointers, snapshot[i].UserContext);
if (disposition == EXCEPTION_CONTINUE_SEARCH)
continue;
if (disposition == EXCEPTION_CONTINUE_EXECUTION) {
if (pointers->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
return NTL_EXCEPTION_TERMINATE_PROCESS;
WinpStoreContext(nativeContext, pointers->ContextRecord);
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
if (disposition == EXCEPTION_EXECUTE_HANDLER) {
// Runtime frames do not have a compiler-generated landing pad yet.
return NTL_EXCEPTION_TERMINATE_PROCESS;
}
return NTL_EXCEPTION_TERMINATE_PROCESS;
}
return NTL_EXCEPTION_CONTINUE_SEARCH;
}
// Bridges the native exception runtime into Win32 exception policy.
static int WinpExceptionNativeDispatch(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) {
(void)userContext;
EXCEPTION_RECORD winRecord;
CONTEXT winContext;
EXCEPTION_POINTERS pointers;
WinpBuildExceptionRecord(record, &winRecord);
WinpBuildContext(context, &winContext);
pointers.ExceptionRecord = &winRecord;
pointers.ContextRecord = &winContext;
int disposition = WinpRunVectoredList(0, &pointers, context);
if (disposition != NTL_EXCEPTION_CONTINUE_SEARCH)
return disposition;
disposition = WinpRunSehFrames(&pointers, context);
if (disposition != NTL_EXCEPTION_CONTINUE_SEARCH)
return disposition;
return WinpRunVectoredList(1, &pointers, context);
}
// Ensures the Win32 exception bridge is registered with the native exception runtime.
static BOOL WinpEnsureExceptionBridge(void) {
if (gWinExceptionNativeHandle)
return TRUE;
WinpVectoredLock();
if (gWinExceptionNativeHandle) {
WinpVectoredUnlock();
return TRUE;
}
void* handle = NtlAddExceptionHandler(1, WinpExceptionNativeDispatch, NULL);
if (!handle) {
WinpVectoredUnlock();
gLastError = 8;
return FALSE;
}
gWinExceptionNativeHandle = handle;
WinpVectoredUnlock();
return TRUE;
}
// Adds one Win32 vectored exception or continue handler.
static void* WinpAddVectoredHandler(uint32_t isContinue, unsigned long first, void* handler) {
if (!handler) {
gLastError = 87;
return NULL;
}
if (!WinpEnsureExceptionBridge())
return NULL;
WinpVectoredLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < WIN_VECTORED_MAX_HANDLERS; i++) {
if (!gWinVectoredHandlers[i].Active) {
slot = i;
break;
}
}
if (slot == UINT32_MAX) {
WinpVectoredUnlock();
gLastError = 8;
return NULL;
}
uint32_t* head = WinpVectoredHeadSlot(isContinue);
uint32_t* tail = WinpVectoredTailSlot(isContinue);
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[slot];
entry->Active = 1;
entry->IsContinue = isContinue;
entry->Handler = (PVECTORED_EXCEPTION_HANDLER)handler;
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
if (*head == UINT32_MAX) {
*head = slot;
*tail = slot;
} else if (first) {
entry->Next = *head;
gWinVectoredHandlers[*head].Previous = slot;
*head = slot;
} else {
entry->Previous = *tail;
gWinVectoredHandlers[*tail].Next = slot;
*tail = slot;
}
WinpVectoredUnlock();
return entry;
}
// Removes one Win32 vectored exception or continue handler.
static unsigned long WinpRemoveVectoredHandler(uint32_t isContinue, void* handle) {
if (!handle) {
gLastError = 6;
return 0;
}
WinpVectoredLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < WIN_VECTORED_MAX_HANDLERS; i++) {
if (handle == &gWinVectoredHandlers[i]) {
slot = i;
break;
}
}
if (slot == UINT32_MAX || !gWinVectoredHandlers[slot].Active ||
gWinVectoredHandlers[slot].IsContinue != isContinue) {
WinpVectoredUnlock();
gLastError = 6;
return 0;
}
uint32_t* head = WinpVectoredHeadSlot(isContinue);
uint32_t* tail = WinpVectoredTailSlot(isContinue);
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[slot];
if (entry->Previous != UINT32_MAX)
gWinVectoredHandlers[entry->Previous].Next = entry->Next;
else
*head = entry->Next;
if (entry->Next != UINT32_MAX)
gWinVectoredHandlers[entry->Next].Previous = entry->Previous;
else
*tail = entry->Previous;
memset(entry, 0, sizeof(*entry));
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
WinpVectoredUnlock();
return 1;
}
// Registers one Win32 vectored exception handler.
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
return WinpAddVectoredHandler(0, First, Handler);
}
// Removes one Win32 vectored exception handler.
__declspec(dllexport) unsigned long RemoveVectoredExceptionHandler(void* Handle) {
(void)Handle;
return 1; // success
return WinpRemoveVectoredHandler(0, Handle);
}
// Registers one Win32 vectored continue handler.
__declspec(dllexport) void* AddVectoredContinueHandler(unsigned long First, void* Handler) {
return WinpAddVectoredHandler(1, First, Handler);
}
// Removes one Win32 vectored continue handler.
__declspec(dllexport) unsigned long RemoveVectoredContinueHandler(void* Handle) {
return WinpRemoveVectoredHandler(1, Handle);
}
// Pushes one explicit runtime SEH frame for tests and hand-written personality code.
__declspec(dllexport) void* RtlPushExceptionFrame(RTL_EXCEPTION_FRAME* Frame,
PRTL_EXCEPTION_FRAME_HANDLER Handler,
void* UserContext) {
if (!Frame || !Handler) {
gLastError = 87;
return NULL;
}
if (!WinpEnsureExceptionBridge())
return NULL;
memset(Frame, 0, sizeof(*Frame));
Frame->Size = sizeof(*Frame);
Frame->Previous = WinpSehFrameHead();
Frame->Handler = Handler;
Frame->UserContext = UserContext;
WinpSetSehFrameHead(Frame);
return Frame;
}
// Pops the current explicit runtime SEH frame and enforces stack discipline.
__declspec(dllexport) BOOL RtlPopExceptionFrame(RTL_EXCEPTION_FRAME* Frame) {
if (!Frame || WinpSehFrameHead() != Frame) {
gLastError = 87;
return FALSE;
}
WinpSetSehFrameHead(Frame->Previous);
memset(Frame, 0, sizeof(*Frame));
return TRUE;
}
__declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
const uintptr_t* lpArguments) {
(void)dwExceptionCode; (void)dwExceptionFlags;
(void)nNumberOfArguments; (void)lpArguments;
// Best-effort: nothing to unwind; just halt.
while (true) ;
}
uint64_t arguments[EXCEPTION_MAXIMUM_PARAMETERS];
DWORD count = nNumberOfArguments;
if (count > EXCEPTION_MAXIMUM_PARAMETERS)
count = EXCEPTION_MAXIMUM_PARAMETERS;
typedef struct _EXCEPTION_POINTERS { void* ExceptionRecord; void* ContextRecord; } EXCEPTION_POINTERS;
for (DWORD i = 0; i < count; i++)
arguments[i] = lpArguments ? (uint64_t)lpArguments[i] : 0;
SYS_RAISE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Code = dwExceptionCode;
request.Flags = (dwExceptionFlags & EXCEPTION_NONCONTINUABLE) ?
SYS_RAISE_EXCEPTION_FLAG_NONCONTINUABLE :
SYS_RAISE_EXCEPTION_FLAG_NONE;
request.ParameterCount = count;
request.Parameters = count ? arguments : NULL;
uint32_t status = SysRaiseException(&request);
if (status != NTLSTATUS_SUCCESS)
SysExitProcess((int32_t)status);
}
// Implements the unhandled Exception Filter operation.
__declspec(dllexport) long UnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
@@ -2483,7 +2966,7 @@ __declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}
// RTL Exception / Unwind (SEH infrastructure - all stubs).
// RTL Exception / Unwind metadata helpers.
typedef struct _RUNTIME_FUNCTION {
unsigned int BeginAddress;
+1 -1
View File
@@ -50,7 +50,7 @@ typedef struct {
__declspec(dllexport) unsigned long timeGetTime(void) {
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS || !frequency) return 0;
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS || !frequency) return 0;
return (unsigned long)SysTicksToMilliseconds(SysGetTick(), frequency);
}