doom is now using the native api, add a PEB + TEB to replace the dummy and the awkward argument passing to processes, all allocations are zeroed because i had enough, small readme changes and dedication.

This commit is contained in:
maksw committed 2026-05-18 21:30:07 +02:00
1 parent ddb5d5f5d8
commit f5fd7bdd75
28 files changed
+947 -335

No files matched your search

Binary file not shown.
+1 -1
View File
@@ -167,7 +167,7 @@ for %%f in (%SRC%\doom\*.c) do (
:: ---- Link doom.exe ---- :: ---- Link doom.exe ----
echo Linking doom.exe... echo Linking doom.exe...
link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
/OUT:"bin\doom.exe" /PDB:"bin\doom.pdb" bin\win32.lib obj\libc.obj obj\doom\*.obj || exit /b 1 /OUT:"bin\doom.exe" /PDB:"bin\doom.pdb" bin\syscall.lib obj\libc.obj obj\doom\*.obj || exit /b 1
:: ---- Compile video player ---- :: ---- Compile video player ----
echo Compiling video... echo Compiling video...
+185 -153
View File
@@ -1,43 +1,51 @@
// doom.c — NT-Lite port of PureDOOM // doom.c — PureDOOM port
// Win32 personality carrying all the weight, just like the real ntoskrnl does. // Native personality carrying all the weight.
// Drop doom1.wad (or doom.wad) next to this binary on your FAT32 volume. // Drop doom1.wad (or doom.wad) next to this binary on your FAT32 volume.
#include <windows.h>
#define DOOM_IMPLEMENTATION
#include "PureDOOM.h"
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <intrin.h> #include <intrin.h>
int _fltused; #include <syscall.h>
// --------------------------------------------------------------------------- #define DOOM_IMPLEMENTATION
// Globals #include "PureDOOM.h"
// ---------------------------------------------------------------------------
static HANDLE g_stdout; int _fltused;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// doom_print_fn // doom_print_fn
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void doom_print_wrapper(const char* msg) static void doom_print_wrapper(const char* msg)
{ {
UINT len = (UINT)strlen(msg); SysWriteConsole(msg, strlen(msg));
WriteConsoleA(g_stdout, msg, len, 0, 0);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// doom_malloc_fn / doom_free_fn // doom_malloc_fn / doom_free_fn
// Prepend size tracking metadata to satisfy SysFree's size requirement.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void* doom_malloc_wrapper(int size) static void* doom_malloc_wrapper(int size)
{ {
void* p = HeapAlloc(0, HEAP_ZERO_MEMORY, size); size_t total_size = size + sizeof(size_t);
if (!p) ExitProcess(GetLastError()); void* p = SysAlloc(total_size);
return p; if (!p) SysExitProcess(1);
// Store total allocated size at the start of the block
*(size_t*)p = total_size;
// Return pointer past the size prefix
return (void*)((size_t*)p + 1);
} }
static void doom_free_wrapper(void* ptr) static void doom_free_wrapper(void* ptr)
{ {
HeapFree(0, 0, ptr); if (!ptr) return;
size_t* real_p = (size_t*)ptr - 1;
size_t total_size = *real_p;
SysFree(real_p, total_size);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -45,61 +53,71 @@ static void doom_free_wrapper(void* ptr)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void* doom_open_wrapper(const char* path, const char* mode) static void* doom_open_wrapper(const char* path, const char* mode)
{ {
DWORD access = GENERIC_READ; uint32_t handle = 0;
DWORD disp = OPEN_EXISTING; uint32_t result;
if (mode[0] == 'w') { if (mode[0] == 'w') {
access = GENERIC_WRITE; result = SysCreateFile(path, &handle);
disp = CREATE_ALWAYS; } else {
} else if (mode[0] == 'r' && mode[1] == '+') { result = SysOpenFile(path, &handle);
access = GENERIC_READ | GENERIC_WRITE;
} }
HANDLE h = CreateFileA(path, access, FILE_SHARE_READ, 0, disp, 0, 0); return (result == 0) ? (void*)(uintptr_t)handle : 0;
return (h == INVALID_HANDLE_VALUE) ? 0 : h;
} }
static void doom_close_wrapper(void* handle) static void doom_close_wrapper(void* handle)
{ {
CloseHandle((HANDLE)handle); SysCloseHandle((uint32_t)(uintptr_t)handle);
} }
static int doom_read_wrapper(void* handle, void* buf, int count) static int doom_read_wrapper(void* handle, void* buf, int count)
{ {
DWORD read = 0; size_t read = 0;
ReadFile((HANDLE)handle, buf, (DWORD)count, &read, 0); SysReadFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &read);
return (int)read; return (int)read;
} }
static int doom_write_wrapper(void* handle, const void* buf, int count) static int doom_write_wrapper(void* handle, const void* buf, int count)
{ {
DWORD written = 0; size_t written = 0;
WriteFile((HANDLE)handle, buf, (DWORD)count, &written, 0); SysWriteFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &written);
return (int)written; return (int)written;
} }
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin) static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
{ {
DWORD method; uint32_t h = (uint32_t)(uintptr_t)handle;
uint64_t absolute_offset = 0;
switch (origin) { switch (origin) {
case DOOM_SEEK_SET: method = FILE_BEGIN; break; case DOOM_SEEK_SET:
case DOOM_SEEK_CUR: method = FILE_CURRENT; break; absolute_offset = (uint64_t)offset;
case DOOM_SEEK_END: method = FILE_END; break; break;
default: method = FILE_BEGIN; break; case DOOM_SEEK_CUR:
absolute_offset = SysGetFilePos(h) + offset;
break;
case DOOM_SEEK_END:
absolute_offset = SysGetFileSize(h) + offset;
break;
default:
absolute_offset = (uint64_t)offset;
break;
} }
SetFilePointer((HANDLE)handle, (LONG)offset, 0, method);
SysSeekFile(h, absolute_offset);
return 0; return 0;
} }
static int doom_tell_wrapper(void* handle) static int doom_tell_wrapper(void* handle)
{ {
return (int)SetFilePointer((HANDLE)handle, 0, 0, FILE_CURRENT); return (int)SysGetFilePos((uint32_t)(uintptr_t)handle);
} }
static int doom_eof_wrapper(void* handle) static int doom_eof_wrapper(void* handle)
{ {
DWORD size = GetFileSize((HANDLE)handle, 0); uint32_t h = (uint32_t)(uintptr_t)handle;
DWORD pos = (DWORD)doom_tell_wrapper(handle); uint64_t size = SysGetFileSize(h);
uint64_t pos = SysGetFilePos(h);
return (pos >= size) ? 1 : 0; return (pos >= size) ? 1 : 0;
} }
@@ -108,9 +126,9 @@ static int doom_eof_wrapper(void* handle)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void doom_gettime_wrapper(int* sec, int* usec) static void doom_gettime_wrapper(int* sec, int* usec)
{ {
DWORD t = GetTickCount(); // milliseconds uint64_t t = SysGetTick();
*sec = (int)(t / 1000); *sec = (int)(t / 1000000); // 1,000,000 us = 1 second
*usec = (int)((t % 1000) * 1000); // ms remainder → us *usec = (int)(t % 1000000); // Remainder in microseconds
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -118,16 +136,16 @@ static void doom_gettime_wrapper(int* sec, int* usec)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void doom_exit_wrapper(int code) static void doom_exit_wrapper(int code)
{ {
ExitProcess((UINT)code); SysExitProcess(code);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// doom_getenv_fn — we don't have environment variables // doom_getenv_fn
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static char* doom_getenv_wrapper(const char* name) static char* doom_getenv_wrapper(const char* name)
{ {
if (strcmp(name, "HOME") == 0) { if (strcmp(name, "HOME") == 0) {
static char home[] = "c:"; static char home[] = ".";
doom_print_wrapper("getenv: Returning HOME="); doom_print_wrapper(home); doom_print_wrapper("\n"); doom_print_wrapper("getenv: Returning HOME="); doom_print_wrapper(home); doom_print_wrapper("\n");
return home; return home;
} }
@@ -136,102 +154,88 @@ static char* doom_getenv_wrapper(const char* name)
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Input — poll GetAsyncKeyState every frame // Input — Maps Scancode Set 1 values to PureDOOM
// Generates key_down / key_up edge events, not held-key spam.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
typedef struct { int vk; doom_key_t key; } KeyMap; typedef struct { uint8_t scancode; bool extended; doom_key_t key; } KeyMap;
// NOTE: Win32 VK codes for letters are uppercase ASCII (e.g. 'W' = 0x57).
// DOOM_KEY_* for letters are lowercase ASCII (e.g. DOOM_KEY_W = 'w' = 0x77).
// These are different values — the VK column is what GetAsyncKeyState takes,
// the doom_key column is what PureDOOM wants. Don't collapse them.
static const KeyMap g_keymap[] = { static const KeyMap g_keymap[] = {
// Letters — VK is uppercase, DOOM key is lowercase // Letters
{ 'W', DOOM_KEY_W }, { 0x11, false, DOOM_KEY_W },
{ 'A', DOOM_KEY_A }, { 0x1E, false, DOOM_KEY_A },
{ 'S', DOOM_KEY_S }, { 0x1F, false, DOOM_KEY_S },
{ 'D', DOOM_KEY_D }, { 0x20, false, DOOM_KEY_D },
{ 'E', DOOM_KEY_E }, { 0x12, false, DOOM_KEY_E },
{ 'Q', DOOM_KEY_Q }, { 0x10, false, DOOM_KEY_Q },
{ 'Y', DOOM_KEY_Y }, // yes/no menus { 0x15, false, DOOM_KEY_Y },
{ 'N', DOOM_KEY_N }, { 0x31, false, DOOM_KEY_N },
// Arrow keys // Arrow keys
{ VK_UP, DOOM_KEY_UP_ARROW }, { 0x48, true, DOOM_KEY_UP_ARROW },
{ VK_DOWN, DOOM_KEY_DOWN_ARROW }, { 0x50, true, DOOM_KEY_DOWN_ARROW },
{ VK_LEFT, DOOM_KEY_LEFT_ARROW }, { 0x4B, true, DOOM_KEY_LEFT_ARROW },
{ VK_RIGHT, DOOM_KEY_RIGHT_ARROW }, { 0x4D, true, DOOM_KEY_RIGHT_ARROW },
// Action keys — Ctrl=fire, Shift=run, Alt=strafe // Action keys
{ VK_CONTROL, DOOM_KEY_CTRL }, { 0x1D, false, DOOM_KEY_CTRL },
{ VK_SPACE, DOOM_KEY_SPACE }, { 0x39, false, DOOM_KEY_SPACE },
{ VK_SHIFT, DOOM_KEY_SHIFT }, { 0x2A, false, DOOM_KEY_SHIFT },
{ VK_MENU, DOOM_KEY_ALT }, { 0x38, false, DOOM_KEY_ALT },
// Navigation // Navigation
{ VK_ESCAPE, DOOM_KEY_ESCAPE }, { 0x01, false, DOOM_KEY_ESCAPE },
{ VK_RETURN, DOOM_KEY_ENTER }, { 0x1C, false, DOOM_KEY_ENTER },
{ VK_TAB, DOOM_KEY_TAB }, { 0x0F, false, DOOM_KEY_TAB },
{ VK_BACK, DOOM_KEY_BACKSPACE }, { 0x0E, false, DOOM_KEY_BACKSPACE },
{ VK_PAUSE, DOOM_KEY_PAUSE },
// Weapon slots // Weapon slots
{ '1', DOOM_KEY_1 }, { 0x02, false, DOOM_KEY_1 },
{ '2', DOOM_KEY_2 }, { 0x03, false, DOOM_KEY_2 },
{ '3', DOOM_KEY_3 }, { 0x04, false, DOOM_KEY_3 },
{ '4', DOOM_KEY_4 }, { 0x05, false, DOOM_KEY_4 },
{ '5', DOOM_KEY_5 }, { 0x06, false, DOOM_KEY_5 },
{ '6', DOOM_KEY_6 }, { 0x07, false, DOOM_KEY_6 },
{ '7', DOOM_KEY_7 }, { 0x08, false, DOOM_KEY_7 },
// Misc // Misc
{ '-', DOOM_KEY_MINUS }, { 0x0C, false, DOOM_KEY_MINUS },
{ '=', DOOM_KEY_EQUALS }, { 0x0D, false, DOOM_KEY_EQUALS },
{ ',', DOOM_KEY_COMMA }, // default strafe left { 0x33, false, DOOM_KEY_COMMA },
{ '.', DOOM_KEY_PERIOD }, // default strafe right { 0x34, false, DOOM_KEY_PERIOD },
// Function keys — save/load/volume/etc // Function keys
{ VK_F1, DOOM_KEY_F1 }, { 0x3B, false, DOOM_KEY_F1 },
{ VK_F2, DOOM_KEY_F2 }, { 0x3C, false, DOOM_KEY_F2 },
{ VK_F3, DOOM_KEY_F3 }, { 0x3D, false, DOOM_KEY_F3 },
{ VK_F4, DOOM_KEY_F4 }, { 0x3E, false, DOOM_KEY_F4 },
{ VK_F5, DOOM_KEY_F5 }, { 0x3F, false, DOOM_KEY_F5 },
{ VK_F6, DOOM_KEY_F6 }, { 0x40, false, DOOM_KEY_F6 },
{ VK_F7, DOOM_KEY_F7 }, { 0x41, false, DOOM_KEY_F7 },
{ VK_F8, DOOM_KEY_F8 }, { 0x42, false, DOOM_KEY_F8 },
{ VK_F9, DOOM_KEY_F9 }, { 0x43, false, DOOM_KEY_F9 },
{ VK_F10, DOOM_KEY_F10 }, { 0x44, false, DOOM_KEY_F10 },
{ VK_F11, DOOM_KEY_F11 }, { 0x57, false, DOOM_KEY_F11 },
{ VK_F12, DOOM_KEY_F12 }, { 0x58, false, DOOM_KEY_F12 },
}; };
#define KEYMAP_COUNT (sizeof(g_keymap) / sizeof(g_keymap[0])) #define KEYMAP_COUNT (sizeof(g_keymap) / sizeof(g_keymap[0]))
static short g_prev_key[256]; static bool g_prev_key[KEYMAP_COUNT];
static void poll_input(void) static void poll_input(void)
{ {
for (int i = 0; i < (int)KEYMAP_COUNT; i++) { for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
int vk = g_keymap[i].vk; bool cur_down = (SysKbIsKeyDown(g_keymap[i].scancode, g_keymap[i].extended) != 0);
short cur = GetAsyncKeyState(vk); bool prev_down = g_prev_key[i];
int cur_down = (cur & 0x8000) != 0;
int prev_down = (g_prev_key[vk] & 0x8000) != 0;
if (cur_down && !prev_down) doom_key_down(g_keymap[i].key); if (cur_down && !prev_down) doom_key_down(g_keymap[i].key);
if (!cur_down && prev_down) doom_key_up (g_keymap[i].key); if (!cur_down && prev_down) doom_key_up (g_keymap[i].key);
g_prev_key[vk] = cur; g_prev_key[i] = cur_down;
} }
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Render — StretchDIBits to screen, same pipeline as video.c. // Render — Performs lightweight software upscale and blits to the screen
//
// NOTE: PureDOOM's framebuffer is RGBA; Win32 StretchDIBits with BI_RGB/32bpp
// expects BGRA. Channels will be swapped (red ↔ blue) until you either:
// a) swap in the blit loop below, or
// b) tell PureDOOM to give you BGRA if a future version supports it.
// For a first boot, blue-tinted demons are still demons.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#define DOOM_W 320 #define DOOM_W 320
#define DOOM_H 200 #define DOOM_H 200
@@ -239,55 +243,82 @@ static void poll_input(void)
#define DISP_W (DOOM_W * SCALE) #define DISP_W (DOOM_W * SCALE)
#define DISP_H (DOOM_H * SCALE) #define DISP_H (DOOM_H * SCALE)
static BITMAPINFO g_bmi; static uint32_t g_blit_buf[DISP_W * DISP_H];
static uint32_t g_blit_buf[DOOM_W * DOOM_H]; // BGRA-swapped copy
static void render_frame(const uint8_t* rgba) static void render_frame(const uint8_t* rgba)
{ {
// Swap R <-> B so Win32 GDI sees correct colours
const uint32_t* src = (const uint32_t*)rgba; const uint32_t* src = (const uint32_t*)rgba;
for (int i = 0; i < DOOM_W * DOOM_H; i++) {
uint32_t p = src[i]; // Software upscale loop mapping 320x200 buffer directly to scaled window buffer
// RGBA -> BGRA: swap byte 0 (R) and byte 2 (B) for (int y = 0; y < DOOM_H; y++) {
g_blit_buf[i] = (p & 0xFF00FF00u) for (int x = 0; x < DOOM_W; x++) {
| ((p & 0x00FF0000u) >> 16) uint32_t p = src[y * DOOM_W + x];
| ((p & 0x000000FFu) << 16);
// RGBA -> BGRA conversion (standard for linear x86/64 framebuffers)
uint32_t bgra = (p & 0xFF00FF00u)
| ((p & 0x00FF0000u) >> 16)
| ((p & 0x000000FFu) << 16);
// Populate SCALE * SCALE pixel block
for (int sy = 0; sy < SCALE; sy++) {
for (int sx = 0; sx < SCALE; sx++) {
g_blit_buf[(y * SCALE + sy) * DISP_W + (x * SCALE + sx)] = bgra;
}
}
}
} }
PAINTSTRUCT ps; SysScreenBlit(0, 0, DISP_W, DISP_H, g_blit_buf);
HDC hdc = BeginPaint((HWND)0, &ps); }
StretchDIBits(
hdc, static int build_argv_from_command_line(const char* cmdline, char* storage, size_t storageSize,
0, 0, DISP_W, DISP_H, // destination — scaled up const char** argv, int maxArgv) {
0, 0, DOOM_W, DOOM_H, // source — native 320×200 if (!storage || storageSize == 0 || !argv || maxArgv <= 1) return 0;
g_blit_buf,
&g_bmi, if (!cmdline || cmdline[0] == '\0') cmdline = "doom";
DIB_RGB_COLORS,
SRCCOPY size_t len = strlen(cmdline);
); if (len >= storageSize) len = storageSize - 1;
EndPaint((HWND)0, &ps); memcpy(storage, cmdline, len);
storage[len] = '\0';
int argc = 0;
char* p = storage;
while (*p && argc < maxArgv - 1) {
while (*p == ' ' || *p == '\t') p++;
if (!*p) break;
if (*p == '"') {
p++;
argv[argc++] = p;
while (*p && *p != '"') p++;
if (*p) *p++ = '\0';
} else {
argv[argc++] = p;
while (*p && *p != ' ' && *p != '\t') p++;
if (*p) *p++ = '\0';
}
}
argv[argc] = 0;
if (argc == 0) {
argv[0] = "doom";
argv[1] = 0;
argc = 1;
}
return argc;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// main // main
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
int main(const char* cmdline) int main(void)
{ {
(void)cmdline; const char* cmdline = SysGetCommandLine();
g_stdout = GetStdHandle(STD_OUTPUT_HANDLE); SysWriteConsole("doom: starting\n", 15);
WriteConsoleA(g_stdout, "doom: starting\n", 15, 0, 0);
// Prepare the BMI header once (top-down, like video.c) // Wire up PureDOOM core mechanics
memset(&g_bmi, 0, sizeof(g_bmi));
g_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
g_bmi.bmiHeader.biWidth = DOOM_W;
g_bmi.bmiHeader.biHeight = -DOOM_H; // negative = top-down
g_bmi.bmiHeader.biPlanes = 1;
g_bmi.bmiHeader.biBitCount = 32;
g_bmi.bmiHeader.biCompression = BI_RGB;
// Wire up PureDOOM
doom_set_print(doom_print_wrapper); doom_set_print(doom_print_wrapper);
doom_set_malloc(doom_malloc_wrapper, doom_free_wrapper); doom_set_malloc(doom_malloc_wrapper, doom_free_wrapper);
doom_set_file_io( doom_set_file_io(
@@ -312,19 +343,20 @@ int main(const char* cmdline)
doom_set_default_int("key_use", DOOM_KEY_E); doom_set_default_int("key_use", DOOM_KEY_E);
doom_set_default_int("mouse_move", 0); doom_set_default_int("mouse_move", 0);
// No extra args — PureDOOM will scan for doom1.wad / doom.wad char argvStorage[512];
const char* args[] = { "doom" }; const char* argv[32];
doom_init(1, (char**)args, 0); int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
doom_init(argc, argv, 0);
WriteConsoleA(g_stdout, "doom: entering game loop\n", 25, 0, 0); SysWriteConsole("doom: entering game loop\n", 25);
// Game loop — identical shape to video.c's Bink loop // Core execution loop
while (1) { while (1) {
poll_input(); poll_input();
doom_update(); doom_update();
render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */)); render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */));
} }
// Unreachable: doom_exit() -> ExitProcess() handles clean shutdown SysExitProcess(0);
ExitProcess(0); return 0;
} }
+20 -3
View File
@@ -669,11 +669,15 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
uint64_t offset, void *buf, uint64_t offset, void *buf,
size_t size, size_t *bytesRead) { size_t size, size_t *bytesRead) {
(void)fs; (void)fs;
if (!bytesRead) return STATUS_INVALID_PARAMETER;
*bytesRead = 0;
if (!fileCtx || !buf) return STATUS_INVALID_PARAMETER;
FAT32_FILE *f = (FAT32_FILE *)fileCtx; FAT32_FILE *f = (FAT32_FILE *)fileCtx;
FAT32_VOL *v = f->Vol; FAT32_VOL *v = f->Vol;
if (offset >= f->FileSize || size == 0) return STATUS_SUCCESS;
if (offset >= f->FileSize) { *bytesRead = 0; return STATUS_SUCCESS; }
if (offset + size > f->FileSize) size = (size_t)(f->FileSize - offset); if (offset + size > f->FileSize) size = (size_t)(f->FileSize - offset);
size_t wanted = size;
// Single allocation for the largest possible coalesced run. // Single allocation for the largest possible coalesced run.
uint8_t *runBuf = (uint8_t *)MmAllocatePool(MAX_RUN_SECTORS * 512u); uint8_t *runBuf = (uint8_t *)MmAllocatePool(MAX_RUN_SECTORS * 512u);
@@ -681,6 +685,7 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
size_t totalRead = 0; size_t totalRead = 0;
uint8_t *out = (uint8_t *)buf; uint8_t *out = (uint8_t *)buf;
KSTATUS readStatus = STATUS_SUCCESS;
uint32_t skipClusters = (uint32_t)(offset / v->BytesPerCluster); uint32_t skipClusters = (uint32_t)(offset / v->BytesPerCluster);
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster); uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
uint32_t cluster = f->StartCluster; uint32_t cluster = f->StartCluster;
@@ -701,8 +706,11 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
runSectors += v->SectorsPerCluster; runSectors += v->SectorsPerCluster;
} }
if (!KSUCCESS(ReadSectors(v, ClusterToLba(v, runStart), runSectors, runBuf))) KSTATUS rs = ReadSectors(v, ClusterToLba(v, runStart), runSectors, runBuf);
if (!KSUCCESS(rs)) {
readStatus = rs;
break; break;
}
uint32_t runBytes = runSectors * 512u; uint32_t runBytes = runSectors * 512u;
size_t avail = runBytes - offsetInCluster; size_t avail = runBytes - offsetInCluster;
@@ -716,8 +724,17 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
cluster = next; cluster = next;
} }
if (totalRead != wanted) {
// The directory entry says the file has more data than the cluster chain
// or disk read actually supplied. Clear the caller's remainder so stale
// memory cannot be interpreted as file contents.
memset(out, 0, wanted - totalRead);
}
MmFreePool(runBuf); MmFreePool(runBuf);
*bytesRead = totalRead; *bytesRead = totalRead;
if (totalRead != wanted)
return KSUCCESS(readStatus) ? STATUS_DEVICE_ERROR : readStatus;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
+3 -2
View File
@@ -48,12 +48,13 @@ void itoa(int num, char* str) {
} }
} }
void _start(const char* args) void _start(void)
{ {
const char* args = SysGetCommandLine();
uint32_t h = 0; uint32_t h = 0;
uint32_t s = 0; uint32_t s = 0;
for (;;) { while (true) {
SysDebugPrint("[init] starting\n"); SysDebugPrint("[init] starting\n");
s = SysCreateProcess(args, &h, NULL); s = SysCreateProcess(args, &h, NULL);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
+21 -2
View File
@@ -5,6 +5,7 @@
#include <io_file.h> #include <io_file.h>
#include <mm_virt.h> #include <mm_virt.h>
#include <ob_object.h> #include <ob_object.h>
#include <ke_userenv.h>
#define MAX_DRIVES 8 #define MAX_DRIVES 8
@@ -148,6 +149,23 @@ __declspec(dllexport) KSTATUS IoSetCwd(const char* path) {
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock); uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
memcpy(proc->CurrentDirectory, resolved, len + 1); memcpy(proc->CurrentDirectory, resolved, len + 1);
// Keep the user-visible PEB mirror in sync. The PEB's CWD buffer is a
// fixed SYS_CWD_MAX allocation made at process creation, so the pointer
// remains stable for personalities that cache SYS_PEB*.
if (proc->UserCwdBase && proc->UserCwdSize) {
size_t copyLen = len;
if (copyLen + 1 > proc->UserCwdSize) copyLen = proc->UserCwdSize - 1;
memcpy((void*)proc->UserCwdBase, resolved, copyLen);
((char*)proc->UserCwdBase)[copyLen] = '\0';
SYS_PEB* peb = (SYS_PEB*)proc->UserPebBase;
if (peb && peb->Magic == SYS_PEB_MAGIC) {
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
peb->CurrentDirectoryLength = copyLen;
}
}
KeReleaseSpinlock(&proc->CwdLock, flags); KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_SUCCESS; return STATUS_SUCCESS;
@@ -209,8 +227,9 @@ __declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
// IoReadFile - sequential read at current offset // IoReadFile - sequential read at current offset
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf, size_t size, size_t* bytesRead) { __declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf, size_t size, size_t* bytesRead) {
if (!file || !buf) return STATUS_INVALID_PARAMETER; if (bytesRead) *bytesRead = 0;
if (file->Offset >= file->Size) { *bytesRead = 0; return STATUS_SUCCESS; } if (!file || !buf || !bytesRead) return STATUS_INVALID_PARAMETER;
if (file->Offset >= file->Size || size == 0) return STATUS_SUCCESS;
// Clamp to remaining bytes. // Clamp to remaining bytes.
size_t remaining = (size_t)(file->Size - file->Offset); size_t remaining = (size_t)(file->Size - file->Offset);
+2 -2
View File
@@ -36,7 +36,7 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) {
} }
// KePanic // KePanic
__declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) { __declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...) {
// Silence! Stop the world! // Silence! Stop the world!
_disable(); _disable();
@@ -44,7 +44,7 @@ __declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
// We call PrintChar directly so we can set a red foreground. // We call PrintChar directly so we can set a red foreground.
// (DbgPrint uses green; we want this visually distinct.) // (DbgPrint uses green; we want this visually distinct.)
static const char banner[] = "\n*** KERNEL PANIC ***\n"; static const char banner[] = "\n*** KERNEL PANIC ***\n";
for (const char *p = banner; *p; p++) for (const char* p = banner; *p; p++)
PrintChar(*p, 0xFF2222, 0x000000); PrintChar(*p, 0xFF2222, 0x000000);
// Now print the caller's message through the normal vprintf path. // Now print the caller's message through the normal vprintf path.
+1 -1
View File
@@ -6,7 +6,7 @@
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v); void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
__declspec(dllexport) void DbgPrint(const char* fmt, ...); __declspec(dllexport) void DbgPrint(const char* fmt, ...);
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len); __declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
__declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...); __declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Fault dispatch - called from the three IDT fault stubs (ke_syscall.asm). // Fault dispatch - called from the three IDT fault stubs (ke_syscall.asm).
+130 -1
View File
@@ -5,6 +5,7 @@
#include <ke_thread.h> #include <ke_thread.h>
#include <ke_sched.h> #include <ke_sched.h>
#include <ke_event.h> #include <ke_event.h>
#include <ke_userenv.h>
#include <mm_virt.h> #include <mm_virt.h>
#include <ob_object.h> #include <ob_object.h>
@@ -84,12 +85,27 @@ static void ProcessDestroy(OBJECT* obj) {
} }
proc->ModuleCount = 0; proc->ModuleCount = 0;
// Release the user command line buffer. // Release the user-mode process environment.
if (proc->UserPebBase && proc->UserPebSize) {
MmFreeUserMemory((void *)proc->UserPebBase, proc->UserPebSize);
proc->UserPebBase = 0;
proc->UserPebSize = 0;
}
if (proc->UserImagePathBase && proc->UserImagePathSize) {
MmFreeUserMemory((void *)proc->UserImagePathBase, proc->UserImagePathSize);
proc->UserImagePathBase = 0;
proc->UserImagePathSize = 0;
}
if (proc->UserCmdlineBase && proc->UserCmdlineSize) { if (proc->UserCmdlineBase && proc->UserCmdlineSize) {
MmFreeUserMemory((void *)proc->UserCmdlineBase, proc->UserCmdlineSize); MmFreeUserMemory((void *)proc->UserCmdlineBase, proc->UserCmdlineSize);
proc->UserCmdlineBase = 0; proc->UserCmdlineBase = 0;
proc->UserCmdlineSize = 0; proc->UserCmdlineSize = 0;
} }
if (proc->UserCwdBase && proc->UserCwdSize) {
MmFreeUserMemory((void *)proc->UserCwdBase, proc->UserCwdSize);
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
}
// Release the exit event. // Release the exit event.
if (proc->ExitEvent) { if (proc->ExitEvent) {
@@ -129,9 +145,17 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->ExitCode = 0; proc->ExitCode = 0;
proc->ImageBase = 0; proc->ImageBase = 0;
proc->ImageSize = 0; proc->ImageSize = 0;
proc->UserPebBase = 0;
proc->UserPebSize = 0;
proc->UserImagePathBase = 0;
proc->UserImagePathSize = 0;
proc->UserCmdlineBase = 0; proc->UserCmdlineBase = 0;
proc->UserCmdlineSize = 0; proc->UserCmdlineSize = 0;
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
proc->ModuleCount = 0; proc->ModuleCount = 0;
proc->CurrentDirectory[0] = '\0';
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
// Slot 0 is the invalid handle - leave it NULL. // Slot 0 is the invalid handle - leave it NULL.
memset(proc->Handles, 0, sizeof(proc->Handles)); memset(proc->Handles, 0, sizeof(proc->Handles));
@@ -152,6 +176,111 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
// ---------------------------------------------------------------------------
// KeSetupUserProcessEnvironment
// ---------------------------------------------------------------------------
static KSTATUS CopyEnvironmentString(const char* src, uintptr_t* baseOut, size_t* sizeOut) {
if (!baseOut || !sizeOut) return STATUS_INVALID_PARAMETER;
if (!src) src = "";
size_t len = strlen(src) + 1;
char* dst = (char*)MmAllocateUserMemory(len);
if (!dst) return STATUS_NO_MEMORY;
memcpy(dst, src, len);
*baseOut = (uintptr_t)dst;
*sizeOut = len;
return STATUS_SUCCESS;
}
static KSTATUS CopyEnvironmentStringFixed(const char* src, size_t capacity,
uintptr_t* baseOut, size_t* sizeOut) {
if (!baseOut || !sizeOut || capacity == 0) return STATUS_INVALID_PARAMETER;
if (!src) src = "";
char* dst = (char*)MmAllocateUserMemory(capacity);
if (!dst) return STATUS_NO_MEMORY;
memset(dst, 0, capacity);
size_t len = strlen(src);
if (len >= capacity) len = capacity - 1;
memcpy(dst, src, len);
dst[len] = '\0';
*baseOut = (uintptr_t)dst;
*sizeOut = capacity;
return STATUS_SUCCESS;
}
static void FreePartialProcessEnvironment(PROCESS* proc) {
if (!proc) return;
if (proc->UserPebBase && proc->UserPebSize) {
MmFreeUserMemory((void*)proc->UserPebBase, proc->UserPebSize);
proc->UserPebBase = 0;
proc->UserPebSize = 0;
}
if (proc->UserImagePathBase && proc->UserImagePathSize) {
MmFreeUserMemory((void*)proc->UserImagePathBase, proc->UserImagePathSize);
proc->UserImagePathBase = 0;
proc->UserImagePathSize = 0;
}
if (proc->UserCmdlineBase && proc->UserCmdlineSize) {
MmFreeUserMemory((void*)proc->UserCmdlineBase, proc->UserCmdlineSize);
proc->UserCmdlineBase = 0;
proc->UserCmdlineSize = 0;
}
if (proc->UserCwdBase && proc->UserCwdSize) {
MmFreeUserMemory((void*)proc->UserCwdBase, proc->UserCwdSize);
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
}
}
KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
const char* imagePath,
const char* commandLine,
const char* currentDirectory) {
if (!proc || !imagePath) return STATUS_INVALID_PARAMETER;
const char* finalCommandLine = (commandLine && commandLine[0]) ? commandLine : imagePath;
const char* finalCwd = currentDirectory ? currentDirectory : proc->CurrentDirectory;
FreePartialProcessEnvironment(proc);
KSTATUS s = CopyEnvironmentString(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize);
if (!KSUCCESS(s)) goto fail;
s = CopyEnvironmentString(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize);
if (!KSUCCESS(s)) goto fail;
s = CopyEnvironmentStringFixed(finalCwd, SYS_CWD_MAX, &proc->UserCwdBase, &proc->UserCwdSize);
if (!KSUCCESS(s)) goto fail;
SYS_PEB* peb = (SYS_PEB*)MmAllocateUserMemory(sizeof(SYS_PEB));
if (!peb) { s = STATUS_NO_MEMORY; goto fail; }
memset(peb, 0, sizeof(SYS_PEB));
peb->Magic = SYS_PEB_MAGIC;
peb->Version = SYS_ENV_VERSION;
peb->ProcessId = proc->ProcessId;
peb->ImagePath = (const char*)proc->UserImagePathBase;
peb->ImagePathLength = proc->UserImagePathSize ? proc->UserImagePathSize - 1 : 0;
peb->CommandLine = (const char*)proc->UserCmdlineBase;
peb->CommandLineLength = proc->UserCmdlineSize ? proc->UserCmdlineSize - 1 : 0;
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
peb->CurrentDirectoryLength = strlen(finalCwd);
proc->UserPebBase = (uintptr_t)peb;
proc->UserPebSize = sizeof(SYS_PEB);
return STATUS_SUCCESS;
fail:
FreePartialProcessEnvironment(proc);
return s;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// KeExitProcess // KeExitProcess
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+9 -6
View File
@@ -7,6 +7,7 @@
#include <ke_spinlock.h> #include <ke_spinlock.h>
#include <ke_event.h> #include <ke_event.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#define PROCESS_NAME_MAX 64 #define PROCESS_NAME_MAX 64
#define HANDLE_TABLE_SIZE 256 #define HANDLE_TABLE_SIZE 256
@@ -26,11 +27,6 @@ typedef struct PROCESS {
uint32_t ProcessId; uint32_t ProcessId;
char Name[PROCESS_NAME_MAX]; /* UTF-8 */ char Name[PROCESS_NAME_MAX]; /* UTF-8 */
// Windows-style flat command line, stored at launch by SysCreateProcess.
// The initial thread receives this pointer as its UserArg so _start can
// parse it into argc/argv without an extra syscall.
char Cmdline[512];
char CurrentDirectory[512]; char CurrentDirectory[512];
SPINLOCK CwdLock; SPINLOCK CwdLock;
@@ -50,9 +46,15 @@ typedef struct PROCESS {
uintptr_t ImageBase; uintptr_t ImageBase;
size_t ImageSize; size_t ImageSize;
/* User command line buffer passed to the initial thread. */ /* User-mode process environment. The initial thread sees this through GS->TEB->PEB. */
uintptr_t UserPebBase;
size_t UserPebSize;
uintptr_t UserImagePathBase;
size_t UserImagePathSize;
uintptr_t UserCmdlineBase; uintptr_t UserCmdlineBase;
size_t UserCmdlineSize; size_t UserCmdlineSize;
uintptr_t UserCwdBase;
size_t UserCwdSize;
/* Per-process loaded modules (user processes only). */ /* Per-process loaded modules (user processes only). */
PROCESS_MODULE Modules[PROCESS_MODULE_MAX]; PROCESS_MODULE Modules[PROCESS_MODULE_MAX];
@@ -66,6 +68,7 @@ typedef struct PROCESS {
} PROCESS; } PROCESS;
KSTATUS KeCreateProcess(const char* name, PROCESS **out); KSTATUS KeCreateProcess(const char* name, PROCESS **out);
KSTATUS KeSetupUserProcessEnvironment(PROCESS* process, const char* imagePath, const char* commandLine, const char* currentDirectory);
void KeExitProcess(PROCESS* process, int exitCode); void KeExitProcess(PROCESS* process, int exitCode);
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut); KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
+3 -1
View File
@@ -4,12 +4,14 @@
; sizeof(OBJECT) = 16 (type:4, refcount:4, destroy:8) ; sizeof(OBJECT) = 16 (type:4, refcount:4, destroy:8)
; THREAD.SavedContext offset = 88 ; THREAD.SavedContext offset = 88
; THREAD.TssRsp0 offset = 104 ; THREAD.TssRsp0 offset = 104
; THREAD.Teb offset = 112
; THREAD.FpuContext offset = 168
; TSS64.RSP0 offset = 4 ; TSS64.RSP0 offset = 4
; ;
THREAD_SavedContext EQU 88 THREAD_SavedContext EQU 88
THREAD_TssRsp0 EQU 104 THREAD_TssRsp0 EQU 104
THREAD_Teb EQU 112 THREAD_Teb EQU 112
THREAD_FpuContext EQU 160 THREAD_FpuContext EQU 168
TSS_RSP0_OFFSET EQU 4 TSS_RSP0_OFFSET EQU 4
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
+34 -34
View File
@@ -756,18 +756,18 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
if (!KSUCCESS(s)) return s; if (!KSUCCESS(s)) return s;
uint64_t size = IoGetFileSize(f); uint64_t size = IoGetFileSize(f);
void* mem = MmAllocateUserMemory(size); void* mem = MmAllocatePool(size);
if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; } if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; }
size_t got; size_t got;
s = IoReadFile(f, mem, size, &got); s = IoReadFile(f, mem, size, &got);
IoCloseFile(f); IoCloseFile(f);
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; } if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
// Create the process object. // Create the process object.
PROCESS* proc; PROCESS* proc;
s = KeCreateProcess(path, &proc); s = KeCreateProcess(path, &proc);
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; } if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
// Inherit CWD from parent // Inherit CWD from parent
PROCESS* parent = KeGetCurrentProcess(); PROCESS* parent = KeGetCurrentProcess();
@@ -778,27 +778,13 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
KeReleaseSpinlock(&parent->CwdLock, flags); KeReleaseSpinlock(&parent->CwdLock, flags);
} }
// Store the command line in the process so _start can pick it up. s = KeSetupUserProcessEnvironment(proc, path, cmdline, proc->CurrentDirectory);
// If no cmdline was supplied default to just the image path. if (!KSUCCESS(s)) { MmFreePool(mem); ObDereferenceObject(&proc->Header); return s; }
if (cmdline && cmdline[0])
strncpy(proc->Cmdline, cmdline, sizeof(proc->Cmdline) - 1);
else
strncpy(proc->Cmdline, path, sizeof(proc->Cmdline) - 1);
proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0';
// proc->Cmdline is kernel pool - CPL=3 code can't read it.
// Copy into user memory so the initial thread can access it directly.
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
if (!userCmdline) { MmFreeUserMemory(mem, size); ObDereferenceObject(&proc->Header); return STATUS_NO_MEMORY; }
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
proc->UserCmdlineBase = (uintptr_t)userCmdline;
proc->UserCmdlineSize = cmdlineLen;
// Map the PE image into memory. // Map the PE image into memory.
uintptr_t imageBase = 0; uintptr_t imageBase = 0;
s = LdrLoadImage(mem, size, &imageBase, true); s = LdrLoadImage(mem, size, &imageBase, true);
MmFreeUserMemory(mem, size); MmFreePool(mem);
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
s = LdrResolveImportsForProcess(proc, imageBase); s = LdrResolveImportsForProcess(proc, imageBase);
@@ -808,27 +794,41 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
return s; return s;
} }
// Spin up the initial thread. Pass userCmdline as UserArg so _start proc->ImageBase = imageBase;
// receives it directly in RCX without an extra syscall. proc->ImageSize = LdrGetImageSize(imageBase);
// Give the caller a handle before making the child runnable. Otherwise a
// fast crash/exit can destroy the PROCESS while the syscall is still trying
// to finish wiring it up.
PROCESS* caller = KeGetCurrentProcess();
if (!caller) {
MmFreeUserMemory((void*)imageBase, proc->ImageSize);
ObDereferenceObject(&proc->Header);
return STATUS_FAILURE;
}
uint32_t handle;
s = KeAllocateHandle(caller, &proc->Header, &handle);
if (!KSUCCESS(s)) {
KeCloseHandle(caller, handle);
proc->ImageBase = 0;
proc->ImageSize = 0;
MmFreeUserMemory((void*)imageBase, proc->ImageSize);
ObDereferenceObject(&proc->Header);
return s;
}
// Spin up the initial thread last. KeCreateThread queues the thread before
// returning, so everything visible from exit/fault/wait paths must already
// be valid at this point.
THREAD* thread; THREAD* thread;
uintptr_t entryPoint = LdrGetEntryPoint(imageBase); uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
s = KeCreateThread(proc, entryPoint, (uintptr_t)userCmdline, s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread);
PriorityNormal, &thread);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
ObDereferenceObject(&proc->Header); ObDereferenceObject(&proc->Header);
return s; return s;
} }
proc->ImageBase = imageBase;
proc->ImageSize = LdrGetImageSize(imageBase);
// Give the caller a handle to the new process so they can wait on it.
PROCESS* caller = KeGetCurrentProcess();
if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; }
uint32_t handle;
s = KeAllocateHandle(caller, &proc->Header, &handle);
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
*handleOut = handle; *handleOut = handle;
return STATUS_SUCCESS; return STATUS_SUCCESS;
+34 -9
View File
@@ -4,6 +4,7 @@
#include <ke_thread.h> #include <ke_thread.h>
#include <ke_sched.h> #include <ke_sched.h>
#include <ke_event.h> #include <ke_event.h>
#include <ke_userenv.h>
#include <mm_virt.h> #include <mm_virt.h>
#include <ob_object.h> #include <ob_object.h>
@@ -35,7 +36,12 @@ static void ThreadDestroy(OBJECT* obj) {
thread->KernelStackBase = (void *)0; thread->KernelStackBase = (void *)0;
} }
// Assumes UserStackBase was added to THREAD struct in ke_thread.h. if (thread->Teb && thread->TebSize) {
MmFreeUserMemory(thread->Teb, thread->TebSize);
thread->Teb = (void*)0;
thread->TebSize = 0;
}
if (thread->UserStackBase) { if (thread->UserStackBase) {
MmFreeUserMemory(thread->UserStackBase, USTACK_SIZE); MmFreeUserMemory(thread->UserStackBase, USTACK_SIZE);
thread->UserStackBase = (void *)0; thread->UserStackBase = (void *)0;
@@ -43,7 +49,7 @@ static void ThreadDestroy(OBJECT* obj) {
} }
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg, __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out) { THREAD_PRIORITY priority, THREAD** out) {
if (!owner || !out) return STATUS_INVALID_PARAMETER; if (!owner || !out) return STATUS_INVALID_PARAMETER;
if (priority >= PriorityCount) return STATUS_INVALID_PARAMETER; if (priority >= PriorityCount) return STATUS_INVALID_PARAMETER;
@@ -71,6 +77,7 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
void* ustack = MmAllocateUserMemory(USTACK_SIZE); void* ustack = MmAllocateUserMemory(USTACK_SIZE);
if (!ustack) { if (!ustack) {
MmFreePool(kstack); MmFreePool(kstack);
thread->KernelStackBase = (void*)0;
ObDereferenceObject(&thread->Header); ObDereferenceObject(&thread->Header);
return STATUS_NO_MEMORY; return STATUS_NO_MEMORY;
} }
@@ -79,14 +86,25 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
// i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness // i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness
// this magic number will hunt me in my dreams // this magic number will hunt me in my dreams
// Initialize the dummy TEB SYS_TEB* teb = (SYS_TEB*)MmAllocateUserMemory(sizeof(SYS_TEB));
// offset 0x10 is StackLimit, which __chkstk checks. if (!teb) {
uint64_t* teb = (uint64_t*)ustack; MmFreeUserMemory(ustack, USTACK_SIZE);
teb[0] = 0; // ExceptionList (GS:0x00) thread->UserStackBase = (void*)0;
teb[1] = userStackTop; // StackBase MmFreePool(kstack);
teb[2] = (uintptr_t)ustack; // StackLimit thread->KernelStackBase = (void*)0;
ObDereferenceObject(&thread->Header);
return STATUS_NO_MEMORY;
}
memset(teb, 0, sizeof(SYS_TEB));
teb->Peb = (SYS_PEB*)owner->UserPebBase;
teb->StackBase = (void*)userStackTop;
teb->StackLimit = ustack;
teb->Self = teb;
teb->ThreadId = thread->ThreadId;
teb->Magic = SYS_TEB_MAGIC;
thread->Teb = teb; thread->Teb = teb;
thread->TebSize = sizeof(SYS_TEB);
thread->UserEntry = entry; thread->UserEntry = entry;
thread->UserArg = arg; thread->UserArg = arg;
@@ -114,9 +132,15 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
EVENT* exitEvent; EVENT* exitEvent;
KSTATUS s = KeEventCreate(EventManualReset, 0, &exitEvent); KSTATUS s = KeEventCreate(EventManualReset, 0, &exitEvent);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
if (thread->Teb && thread->TebSize) MmFreeUserMemory(thread->Teb, thread->TebSize);
thread->Teb = (void*)0;
thread->TebSize = 0;
MmFreeUserMemory(ustack, USTACK_SIZE); MmFreeUserMemory(ustack, USTACK_SIZE);
thread->UserStackBase = (void*)0;
MmFreePool(kstack); MmFreePool(kstack);
thread->KernelStackBase = (void*)0;
ObDereferenceObject(&owner->Header); ObDereferenceObject(&owner->Header);
thread->Owner = (PROCESS*)0;
ObDereferenceObject(&thread->Header); ObDereferenceObject(&thread->Header);
return s; return s;
} }
@@ -162,7 +186,7 @@ __declspec(dllexport) void KeExitThread(THREAD* thread, int code) {
extern void KeKernelThreadEntry(void); extern void KeKernelThreadEntry(void);
__declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg, __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out) { THREAD_PRIORITY priority, THREAD** out) {
// Kernel threads are owned by the idle process. We need a PROCESS* for // Kernel threads are owned by the idle process. We need a PROCESS* for
// the owner field; grab it from the idle/init thread that already exists. // the owner field; grab it from the idle/init thread that already exists.
THREAD* idleThread = KeGetCurrentThread(); // safe to call before scheduler starts THREAD* idleThread = KeGetCurrentThread(); // safe to call before scheduler starts
@@ -182,6 +206,7 @@ __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
thread->SchedLockFlags = 0x202ULL; thread->SchedLockFlags = 0x202ULL;
thread->UserStackBase = (void*)0; // no user stack thread->UserStackBase = (void*)0; // no user stack
thread->Teb = (void*)0; // no TEB; GS_BASE will be 0 in kernel ctx thread->Teb = (void*)0; // no TEB; GS_BASE will be 0 in kernel ctx
thread->TebSize = 0;
void* kstack = MmAllocatePool(KSTACK_SIZE); void* kstack = MmAllocatePool(KSTACK_SIZE);
if (!kstack) { if (!kstack) {
+5 -4
View File
@@ -59,8 +59,9 @@ typedef struct THREAD {
// Updated on every context switch so the CPU loads the right kernel stack. // Updated on every context switch so the CPU loads the right kernel stack.
uintptr_t TssRsp0; uintptr_t TssRsp0;
// Thread Environment Block // Thread Environment Block. GS base is loaded from this pointer.
void* Teb; void* Teb;
size_t TebSize;
// Ring-3 entry point and argument for newly created threads. // Ring-3 entry point and argument for newly created threads.
uintptr_t UserEntry; uintptr_t UserEntry;
@@ -79,14 +80,14 @@ typedef struct THREAD {
} THREAD; } THREAD;
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg, __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out); THREAD_PRIORITY priority, THREAD** out);
// Create a kernel-mode-only thread. fn is called directly in ring 0 with // Create a kernel-mode-only thread. fn is called directly in ring 0 with
// arg; when fn returns the thread exits automatically. No user stack or TEB // arg; when fn returns the thread exits automatically. No user stack or TEB
// is allocated. The thread is owned by the idle process and runs at the // is allocated. The thread is owned by the idle process and runs at the
// given priority. // given priority.
__declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg, __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out); THREAD_PRIORITY priority, THREAD** out);
// Mark a thread dead and deschedule it permanently. // Mark a thread dead and deschedule it permanently.
__declspec(dllexport) void KeExitThread(THREAD* thread, int code); __declspec(dllexport) void KeExitThread(THREAD* thread, int code);
+35
View File
@@ -0,0 +1,35 @@
#pragma once
// ke_userenv.h - kernel view of the user-mode process/thread environment.
// This is this OS' ABI, not a Windows compatibility structure.
#include <stdint.h>
#include <stddef.h>
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
#define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 512u
typedef struct SYS_PEB {
uint32_t Magic;
uint32_t Version;
uint32_t ProcessId;
const char* ImagePath;
size_t ImagePathLength;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
} SYS_PEB;
typedef struct SYS_TEB {
SYS_PEB* Peb;
void* StackBase;
void* StackLimit;
struct SYS_TEB* Self;
uint32_t ThreadId;
uint32_t Magic;
} SYS_TEB;
+17 -26
View File
@@ -250,8 +250,8 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
uint64_t size = IoGetFileSize(f); uint64_t size = IoGetFileSize(f);
DbgPrint("LaunchUserProcess: IoGetFileSize: %llu\n", size); DbgPrint("LaunchUserProcess: IoGetFileSize: %llu\n", size);
void* mem = MmAllocateUserMemory(size); void* mem = MmAllocatePool(size);
DbgPrint("LaunchUserProcess: MmAllocateUserMemory: 0x%llX\n", mem); DbgPrint("LaunchUserProcess: MmAllocatePool: 0x%llX\n", mem);
if (!mem) { if (!mem) {
IoCloseFile(f); IoCloseFile(f);
return 0; return 0;
@@ -262,7 +262,7 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s); DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s);
IoCloseFile(f); IoCloseFile(f);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
MmFreeUserMemory(mem, size); MmFreePool(mem);
return 0; return 0;
} }
@@ -273,35 +273,23 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
s = KeCreateProcess(filePath, &proc); s = KeCreateProcess(filePath, &proc);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("Failed to create process. System halted.\n"); DbgPrint("Failed to create process. System halted.\n");
MmFreeUserMemory(mem, size); MmFreePool(mem);
return 0; return 0;
} }
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath); DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, (args && args[0]) ? args : filePath);
if (args && args[0]) s = KeSetupUserProcessEnvironment(proc, filePath, args, proc->CurrentDirectory);
strncpy(proc->Cmdline, args, sizeof(proc->Cmdline) - 1); if (!KSUCCESS(s)) {
else DbgPrint("LaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
strncpy(proc->Cmdline, filePath, sizeof(proc->Cmdline) - 1); MmFreePool(mem);
proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0';
// proc->Cmdline is kernel pool (ffffc000...) - not readable at CPL=3.
// Copy into user memory before handing the pointer to the initial thread.
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
if (!userCmdline) {
DbgPrint("LaunchUserProcess: Failed to allocate user cmdline buffer.\n");
MmFreeUserMemory(mem, size);
ObDereferenceObject(&proc->Header); ObDereferenceObject(&proc->Header);
return 0; return 0;
} }
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
proc->UserCmdlineBase = (uintptr_t)userCmdline;
proc->UserCmdlineSize = cmdlineLen;
// Parse the raw PE file and map it into executable memory. // Parse the raw PE file and map it into executable memory.
uintptr_t imageBase = 0; uintptr_t imageBase = 0;
s = LdrLoadImage(mem, size, &imageBase, true); s = LdrLoadImage(mem, size, &imageBase, true);
MmFreeUserMemory(mem, size); MmFreePool(mem);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("LaunchUserProcess: LdrLoadImage failed with status: %d\n", s); DbgPrint("LaunchUserProcess: LdrLoadImage failed with status: %d\n", s);
ObDereferenceObject(&proc->Header); ObDereferenceObject(&proc->Header);
@@ -316,19 +304,22 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
return 0; return 0;
} }
proc->ImageBase = imageBase;
proc->ImageSize = LdrGetImageSize(imageBase);
uintptr_t entryPoint = LdrGetEntryPoint(imageBase); uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint); DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint);
// Create the main thread, pointing it to the PE Entry Point. // Create the main thread last. KeCreateThread queues it before returning.
s = KeCreateThread(proc, (uintptr_t)entryPoint, (uintptr_t)userCmdline, PriorityNormal, &thread); s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s); DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s);
proc->ImageBase = 0;
proc->ImageSize = 0;
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
ObDereferenceObject(&proc->Header); ObDereferenceObject(&proc->Header);
return 0; return 0;
} }
proc->ImageBase = imageBase;
proc->ImageSize = LdrGetImageSize(imageBase);
DbgPrint("LaunchUserProcess: Thread created and queued!\n"); DbgPrint("LaunchUserProcess: Thread created and queued!\n");
// Caller owns the reference returned by KeCreateProcess. // Caller owns the reference returned by KeCreateProcess.
+2
View File
@@ -323,6 +323,7 @@ static void* HeapAlloc(HEAP* h, size_t size) {
HdrToFtr(best)->Magic = BLOCK_MAGIC; HdrToFtr(best)->Magic = BLOCK_MAGIC;
HdrToFtr(best)->Size = best->Size; HdrToFtr(best)->Size = best->Size;
memset((char *)best + HDR_SIZE, 0, size);
KeReleaseSpinlock(&h->Lock, flags); KeReleaseSpinlock(&h->Lock, flags);
return (char *)best + HDR_SIZE; return (char *)best + HDR_SIZE;
} }
@@ -409,6 +410,7 @@ static void* HeapRealloc(HEAP* h, void* ptr, size_t new_size) {
if (!new_ptr) { if (!new_ptr) {
return (void*)0; // Out of memory, original block is left untouched return (void*)0; // Out of memory, original block is left untouched
} }
memset(new_ptr, 0, aligned_size);
// Copy the old data to the new block. // Copy the old data to the new block.
// We only copy up to the old block's usable size. // We only copy up to the old block's usable size.
+2
View File
@@ -78,6 +78,8 @@ typedef void* pthread_mutexattr_t;
typedef void* posix_spawn_file_actions_t; typedef void* posix_spawn_file_actions_t;
typedef void* posix_spawnattr_t; typedef void* posix_spawnattr_t;
__declspec(dllexport) const char* posix_get_command_line(void);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// dirent (used by posix_dir.c) // dirent (used by posix_dir.c)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+28 -8
View File
@@ -22,9 +22,12 @@ static int KsToErrno(uint32_t s) {
} }
} }
__declspec(dllexport) int ClearConsoleScreenBuffer(void* hConsole) { __declspec(dllexport) void clearscreen(void) {
SysScreenClear(0x00000000); SysScreenClear(0x00000000);
return 1; }
__declspec(dllexport) void clearkeyboard(void) {
while (SysKbPoll()) (void)SysKbReadChar();
} }
__declspec(dllexport) int open(const char* path, int flags, ...) { __declspec(dllexport) int open(const char* path, int flags, ...) {
@@ -154,14 +157,31 @@ __declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) {
FD_ENTRY* e = FdGet(pid, fd); FD_ENTRY* e = FdGet(pid, fd);
if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; } if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; }
uint64_t target; int64_t target = 0;
switch (whence) { switch (whence) {
case SEEK_SET: target = (uint64_t)offset; break; case SEEK_SET:
case SEEK_CUR: target = SysGetFilePos(e->KHandle) + (uint64_t)offset; break; target = (int64_t)offset;
case SEEK_END: target = SysGetFileSize(e->KHandle) + (uint64_t)offset; break; break;
default: errno = EINVAL; return -1; case SEEK_CUR: {
uint64_t cur = SysGetFilePos(e->KHandle);
if (cur > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
target = (int64_t)cur + (int64_t)offset;
break;
}
case SEEK_END: {
uint64_t size = SysGetFileSize(e->KHandle);
if (size > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
target = (int64_t)size + (int64_t)offset;
break;
}
default:
errno = EINVAL;
return -1;
} }
uint32_t s = SysSeekFile(e->KHandle, target);
if (target < 0 || target > 0x7FFFFFFFLL) { errno = EINVAL; return -1; }
uint32_t s = SysSeekFile(e->KHandle, (uint64_t)target);
if (s != 0) { errno = KsToErrno(s); return -1; } if (s != 0) { errno = KsToErrno(s); return -1; }
return (off_t)target; return (off_t)target;
} }
+14
View File
@@ -5,6 +5,20 @@
#include "posix.h" #include "posix.h"
#include "posix_path.h" #include "posix_path.h"
static SYS_PEB* gProcessPeb = NULL;
static SYS_PEB* PosixPeb(void) {
if (!gProcessPeb) gProcessPeb = SysGetPeb();
return gProcessPeb;
}
__declspec(dllexport) const char* posix_get_command_line(void) {
SYS_PEB* peb = PosixPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
return peb->CommandLine;
}
// pid -> proc handle table (user-mode side, for waitpid) // pid -> proc handle table (user-mode side, for waitpid)
#define PROC_TABLE_SIZE 32 #define PROC_TABLE_SIZE 32
typedef struct { typedef struct {
+12 -12
View File
@@ -41,7 +41,6 @@ typedef struct {
void* (*Fn)(void*); void* (*Fn)(void*);
void* Arg; void* Arg;
uint32_t Pid; // owner pid, for the retval table uint32_t Pid; // owner pid, for the retval table
uint32_t Tid; // filled in by pthread_create after KeCreateThread returns
} PTHREAD_TRAMP; } PTHREAD_TRAMP;
static void PthreadTrampoline(uintptr_t raw) { static void PthreadTrampoline(uintptr_t raw) {
@@ -49,14 +48,19 @@ static void PthreadTrampoline(uintptr_t raw) {
void* (*fn)(void*) = tr->Fn; void* (*fn)(void*) = tr->Fn;
void* arg = tr->Arg; void* arg = tr->Arg;
uint32_t pid = tr->Pid; uint32_t pid = tr->Pid;
uint32_t tid = tr->Tid; uint32_t tid = SysGetCurrentTid();
// Create the join slot from inside the new thread. KeCreateThread makes the
// thread runnable before the creator regains control, so the old design
// raced on tr->Tid and could observe zero/stale memory.
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
if (!slot) slot = PthreadSlotAlloc(pid, tid);
SysFree(tr, sizeof(PTHREAD_TRAMP)); SysFree(tr, sizeof(PTHREAD_TRAMP));
void* retval = fn(arg); void* retval = fn(arg);
// Store retval for pthread_join. If the slot isn't there (join was called // Store retval for pthread_join.
// before the thread was even created, which shouldn't happen) just drop it.
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
if (slot) slot->RetVal = retval; if (slot) slot->RetVal = retval;
SysExitThread(0); SysExitThread(0);
@@ -77,7 +81,6 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
tr->Fn = fn; tr->Fn = fn;
tr->Arg = arg; tr->Arg = arg;
tr->Pid = pid; tr->Pid = pid;
tr->Tid = 0; // filled below
uint32_t tid = 0; uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline, uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
@@ -86,12 +89,9 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
SysFree(tr, sizeof(PTHREAD_TRAMP)); SysFree(tr, sizeof(PTHREAD_TRAMP));
return EAGAIN; return EAGAIN;
} }
tr->Tid = tid; // safe: trampoline reads Tid after SysCreateThread returns // The trampoline allocates the retval slot itself after it knows its real
// TID, avoiding the creator/new-thread race.
// Reserve a retval slot before the trampoline can exit if (t) t->Tid = tid;
PthreadSlotAlloc(pid, tid);
t->Tid = tid;
return 0; return 0;
} }
+32 -3
View File
@@ -123,6 +123,27 @@ static void cmd_type(const char* filename) {
SysCloseHandle(hFile); print("\n"); SysCloseHandle(hFile); print("\n");
} }
static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
if (!cmdline || !out || outSize == 0) return false;
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return false;
size_t pos = 0;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
out[pos++] = *cmdline++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
out[pos++] = *cmdline++;
}
out[pos] = '\0';
return pos != 0;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Run one command // Run one command
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -134,13 +155,16 @@ static int runCommand(const char* cmd) {
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; } if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { SysSetCwd(cmd + 3); return 0; } if (strncmp(cmd, "cd ", 3) == 0) { if (SysSetCwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { SysDeleteFile(cmd + 4); return 0; } if (strncmp(cmd, "del ", 4) == 0) { SysDeleteFile(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
char program[MAX_CMD];
if (!extractProgramPath(cmd, program, sizeof(program))) return 0;
uint32_t hProc; uint32_t hProc;
if (SysCreateProcess(cmd, &hProc, cmd) == 0) { if (SysCreateProcess(program, &hProc, cmd) == 0) {
SysWaitForProcess(hProc); SysWaitForProcess(hProc);
SysCloseHandle(hProc); SysCloseHandle(hProc);
while (SysKbPoll()) (void)SysKbReadChar(); while (SysKbPoll()) (void)SysKbReadChar();
@@ -148,8 +172,13 @@ static int runCommand(const char* cmd) {
return 0; return 0;
} }
int main(int argc, char** argv) { int main(void) {
const char* cmdline = SysGetCommandLine();
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n"); print("shell: ready\n");
while (true) { while (true) {
print("> "); print("> ");
+48 -6
View File
@@ -2,6 +2,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <syscall.h>
#define MAX_CMD 256 #define MAX_CMD 256
#define NAME_MAX 259 #define NAME_MAX 259
@@ -21,7 +22,8 @@ extern int unlink(const char *pathname);
extern int posix_spawn(int *pid, const char *path, const void *act, const void *attr, char *const argv[], char *const envp[]); extern int posix_spawn(int *pid, const char *path, const void *act, const void *attr, char *const argv[], char *const envp[]);
extern int waitpid(int pid, int *status, int options); extern int waitpid(int pid, int *status, int options);
extern void exit(int status); extern void exit(int status);
extern int ClearConsoleScreenBuffer(void* h); extern void clearscreen(void);
extern void clearkeyboard(void);
extern void PosixFdInit(void); extern void PosixFdInit(void);
static int hIn = 0; static int hOut = 1; static int hIn = 0; static int hOut = 1;
@@ -52,6 +54,31 @@ static void readLine(char* buf, int len) {
buf[pos] = '\0'; buf[pos] = '\0';
} }
static int splitCommandLine(char* line, char** argv, int maxArgv) {
int argc = 0;
char* p = line;
while (*p && argc < maxArgv - 1) {
while (*p == ' ' || *p == '\t') p++;
if (!*p) break;
if (*p == '"') {
p++;
argv[argc++] = p;
while (*p && *p != '"') p++;
if (*p) *p++ = '\0';
} else {
argv[argc++] = p;
while (*p && *p != ' ' && *p != '\t') p++;
if (*p) *p++ = '\0';
}
}
argv[argc] = NULL;
return argc;
}
static char* trim(char* s) { static char* trim(char* s) {
while (*s == ' ') s++; while (*s == ' ') s++;
int len = (int)strlen(s); int len = (int)strlen(s);
@@ -111,25 +138,40 @@ static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return 0; if (!cmd || *cmd == '\0') return 0;
if (strcmp(cmd, "exit") == 0) return 1; if (strcmp(cmd, "exit") == 0) return 1;
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(0); return 0; } if (strcmp(cmd, "clear") == 0) { clearscreen(); return 0; }
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; } if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { chdir(cmd + 3); return 0; } if (strncmp(cmd, "cd ", 3) == 0) { if (chdir(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; } if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
int pid; char *argv[] = {(char*)cmd, NULL}; char cmdBuf[MAX_CMD];
if (posix_spawn(&pid, cmd, NULL, NULL, argv, NULL) == 0) { strncpy(cmdBuf, cmd, MAX_CMD - 1);
cmdBuf[MAX_CMD - 1] = '\0';
char* argv[32];
int argc = splitCommandLine(cmdBuf, argv, 32);
if (argc == 0) return 0;
int pid;
if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL) == 0) {
int status; waitpid(pid, &status, 0); int status; waitpid(pid, &status, 0);
clearkeyboard();
} else { print("shell: launch failed\n"); } } else { print("shell: launch failed\n"); }
return 0; return 0;
} }
int main() { int main(void) {
PosixFdInit(); PosixFdInit();
const char* cmdline = SysGetCommandLine();
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n"); print("shell: ready\n");
while (true) { while (true) {
print("> "); print("> ");
+12 -3
View File
@@ -3,6 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <syscall.h>
#define MAX_CMD 256 #define MAX_CMD 256
static HANDLE hIn = INVALID_HANDLE_VALUE; static HANDLE hIn = INVALID_HANDLE_VALUE;
@@ -126,7 +127,7 @@ static int runCommand(const char* cmd) {
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; } if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { SetCurrentDirectoryA(cmd + 3); return 0; } if (strncmp(cmd, "cd ", 3) == 0) { if (!SetCurrentDirectoryA(cmd + 3)) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(cmd + 4); return 0; } if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
@@ -137,17 +138,25 @@ static int runCommand(const char* cmd) {
cmdBuf[MAX_CMD - 1] = '\0'; cmdBuf[MAX_CMD - 1] = '\0';
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi)) { if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE); WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess); CloseHandle(pi.hThread); if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
CloseHandle(pi.hThread);
FlushConsoleInputBuffer(hIn);
} else { } else {
print("shell: launch failed\n"); print("shell: launch failed\n");
} }
return 0; return 0;
} }
int main() { int main(void) {
hIn = GetStdHandle(STD_INPUT_HANDLE); hIn = GetStdHandle(STD_INPUT_HANDLE);
hOut = GetStdHandle(STD_OUTPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE);
const char* cmdline = SysGetCommandLine();
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n"); print("shell: ready\n");
while (true) { while (true) {
print("> "); print("> ");
+50
View File
@@ -4,6 +4,56 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <intrin.h>
// ---------------------------------------------------------------------------
// User process/thread environment
// ---------------------------------------------------------------------------
// GS base points at SYS_TEB. GS:[0] is the process PEB pointer.
// This is this OS' ABI, not a Windows compatibility layout.
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
#define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 512u
typedef struct SYS_PEB {
uint32_t Magic;
uint32_t Version;
uint32_t ProcessId;
const char* ImagePath;
size_t ImagePathLength;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
} SYS_PEB;
typedef struct SYS_TEB {
SYS_PEB* Peb;
void* StackBase;
void* StackLimit;
struct SYS_TEB* Self;
uint32_t ThreadId;
uint32_t Magic;
} SYS_TEB;
static inline SYS_PEB* SysGetPeb(void) {
return (SYS_PEB*)(uintptr_t)__readgsqword(0);
}
static inline SYS_TEB* SysGetTeb(void) {
return (SYS_TEB*)(uintptr_t)__readgsqword(offsetof(SYS_TEB, Self));
}
static inline const char* SysGetCommandLine(void) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC) return "";
return peb->CommandLine ? peb->CommandLine : "";
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Thread & Process Management (0-7) // Thread & Process Management (0-7)
+40 -4
View File
@@ -5,16 +5,52 @@
#include <intrin.h> #include <intrin.h>
#include "bink.h" #include "bink.h"
int main(const char* cmdline) { static const char* commandLineSecondToken(const char* cmdline, char* out, size_t outSize) {
if (!out || outSize == 0) return "video.bk2";
strcpy(out, "video.bk2");
if (!cmdline) return out;
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return out;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"') cmdline++;
if (*cmdline == '"') cmdline++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t') cmdline++;
}
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return out;
size_t pos = 0;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
out[pos++] = *cmdline++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
out[pos++] = *cmdline++;
}
out[pos] = '\0';
return out;
}
int main(void) {
const char* cmdline = GetCommandLineA();
char videoPath[260];
const char* path = commandLineSecondToken(cmdline, videoPath, sizeof(videoPath));
void* h = GetStdHandle(STD_OUTPUT_HANDLE); void* h = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(h, "video: starting\n", 16, 0, 0); WriteConsoleA(h, "video: starting\n", 16, 0, 0);
WriteConsoleA(h, cmdline, (unsigned int)strlen(cmdline), 0, 0); WriteConsoleA(h, cmdline, (unsigned int)strlen(cmdline), 0, 0);
WriteConsoleA(h, "\n", 1, 0, 0); WriteConsoleA(h, "\n", 1, 0, 0);
void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0); void* f = CreateFileA(path, 0, 0, 0, 3, 0, 0);
if (!f || f == (void*)(unsigned long long)0xFFFFFFFF) { // fuckass if (!f || f == (void*)(unsigned long long)0xFFFFFFFF) { // fuckass
WriteConsoleA(h, "video: failed to open video.bk2\n", 32, 0, 0); WriteConsoleA(h, "video: failed to open input file\n", 33, 0, 0);
ExitProcess(1); ExitProcess(1);
} }
@@ -34,7 +70,7 @@ int main(const char* cmdline) {
WriteConsoleA(h, "video: ReadFile failed\n", 23, 0, 0); WriteConsoleA(h, "video: ReadFile failed\n", 23, 0, 0);
ExitProcess(1); ExitProcess(1);
} }
WriteConsoleA(h, "video: video.bk2 loaded\n", 24, 0, 0); WriteConsoleA(h, "video: input file loaded\n", 25, 0, 0);
CloseHandle(h); CloseHandle(h);
+194 -48
View File
@@ -76,9 +76,6 @@ typedef struct _WIN32_FIND_DATAA {
#define FILE_CURRENT 1 #define FILE_CURRENT 1
#define FILE_END 2 #define FILE_END 2
// HeapAlloc flags
#define HEAP_ZERO_MEMORY 0x00000008UL
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Handle conversion helpers // Handle conversion helpers
// uint32_t kernel handle <-> opaque Win32 HANDLE // uint32_t kernel handle <-> opaque Win32 HANDLE
@@ -87,9 +84,32 @@ typedef struct _WIN32_FIND_DATAA {
static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; } 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; } 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) {
uintptr_t v = (uintptr_t)h;
return v == WIN32_MUTEX_SENTINEL || v == WIN32_SEMA_SENTINEL;
}
// Path translator // Path translator
static void TranslatePath(const char* input, char* output) { static void TranslatePath(const char* input, char* output) {
if (input && input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) { if (!output) return;
if (!input) { output[0] = '\0'; return; }
if (input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) {
// Translate "C:\" -> "0:/" (assuming 'C' maps to '0', etc.) // Translate "C:\" -> "0:/" (assuming 'C' maps to '0', etc.)
// Map drive letter to index: 'C' or 'c' -> '0' // Map drive letter to index: 'C' or 'c' -> '0'
char drive = input[0]; char drive = input[0];
@@ -122,6 +142,13 @@ static void TranslatePath(const char* input, char* output) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static DWORD gLastError = 0; static DWORD gLastError = 0;
static SYS_PEB* gProcessPeb = NULL;
static SYS_PEB* Win32Peb(void) {
if (!gProcessPeb) gProcessPeb = SysGetPeb();
return gProcessPeb;
}
__declspec(dllexport) DWORD GetLastError(void) { __declspec(dllexport) DWORD GetLastError(void) {
return gLastError; return gLastError;
} }
@@ -162,8 +189,6 @@ __declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
(void)heap; (void)heap;
if (!size) return NULL; if (!size) return NULL;
void* ptr = SysAlloc(size); void* ptr = SysAlloc(size);
if (ptr && (flags & HEAP_ZERO_MEMORY))
memset(ptr, 0, size);
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return ptr; return ptr;
} }
@@ -395,7 +420,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD. // GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour. // Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) { __declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; } if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile)); uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32); if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL); return (DWORD)(sz & 0xFFFFFFFFULL);
@@ -405,11 +430,26 @@ __declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
__declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead, __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) { DWORD* bytesRead, void* overlapped) {
(void)overlapped; (void)overlapped;
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; } if (bytesRead) *bytesRead = 0;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) {
gLastError = 6;
return FALSE;
}
size_t got = 0; size_t got = 0;
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got); uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
if (bytesRead) *bytesRead = (DWORD)got; if (bytesRead) *bytesRead = (DWORD)got;
if (s != 0) { gLastError = 1; return FALSE; }
// 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;
}
return TRUE; return TRUE;
} }
@@ -418,7 +458,7 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite, __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) { DWORD* bytesWritten, void* overlapped) {
(void)overlapped; (void)overlapped;
if (!hFile || !buf) { gLastError = 6; return FALSE; } if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) { gLastError = 6; return FALSE; }
size_t written = 0; size_t written = 0;
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written); uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
if (bytesWritten) *bytesWritten = (DWORD)written; if (bytesWritten) *bytesWritten = (DWORD)written;
@@ -430,27 +470,41 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit
// Returns new position low DWORD, or 0xFFFFFFFF on error. // Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist, __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) { long* distHigh, DWORD method) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; } if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
uint32_t kh = HANDLE_TO_KH(hFile); uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t offset = 0; int64_t delta;
int64_t delta = distHigh if (distHigh) {
? ((int64_t)(uint64_t)(uint32_t)*distHigh << 32) | (uint32_t)dist uint64_t combined = ((uint64_t)(uint32_t)(*distHigh) << 32) | (uint32_t)dist;
: (int64_t)dist; delta = (int64_t)combined;
} else {
delta = (int64_t)dist;
}
int64_t target = 0;
if (method == FILE_BEGIN) { if (method == FILE_BEGIN) {
offset = (uint64_t)delta; target = delta;
} else if (method == FILE_CURRENT) { } else if (method == FILE_CURRENT) {
uint64_t cur = SysGetFilePos(kh); uint64_t cur = SysGetFilePos(kh);
if (cur == (uint64_t)-1) { gLastError = 6; return 0xFFFFFFFFUL; } if (cur == (uint64_t)-1 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
offset = (uint64_t)((int64_t)cur + delta); target = (int64_t)cur + delta;
} else if (method == FILE_END) { } else if (method == FILE_END) {
uint64_t sz = SysGetFileSize(kh); uint64_t sz = SysGetFileSize(kh);
offset = (uint64_t)((int64_t)sz + delta); if (sz > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)sz + delta;
} else { } else {
gLastError = 87; return 0xFFFFFFFFUL; gLastError = 87; return 0xFFFFFFFFUL;
} }
if (target < 0) {
gLastError = 131; // ERROR_NEGATIVE_SEEK
return 0xFFFFFFFFUL;
}
uint64_t offset = (uint64_t)target;
uint32_t s = SysSeekFile(kh, offset); uint32_t s = SysSeekFile(kh, offset);
if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; } if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; }
if (distHigh) *distHigh = (long)(int32_t)(offset >> 32); if (distHigh) *distHigh = (long)(int32_t)(offset >> 32);
@@ -459,7 +513,7 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
// SetEndOfFile - truncate or extend the file to the current seek position. // SetEndOfFile - truncate or extend the file to the current seek position.
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) { __declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; } if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile); uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh); uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; } if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
@@ -482,7 +536,12 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
// CloseHandle - unified close for files, mutexes, semaphores, events, queues. // CloseHandle - unified close for files, mutexes, semaphores, events, queues.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) { __declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject) { gLastError = 6; return FALSE; } 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)); SysCloseHandle(HANDLE_TO_KH(hObject));
return TRUE; return TRUE;
} }
@@ -607,9 +666,9 @@ __declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
} }
__declspec(dllexport) char* GetCommandLineA(void) { __declspec(dllexport) char* GetCommandLineA(void) {
// Return a valid but empty-ish command line. SYS_PEB* peb = Win32Peb();
static char cmdline[4] = ""; if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
return cmdline; return (char*)peb->CommandLine;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -633,18 +692,53 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
0 /* normal priority */, &tid); 0 /* normal priority */, &tid);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; } if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid; if (lpThreadId) *lpThreadId = (DWORD)tid;
return KH_TO_HANDLE(tid); return TID_TO_THREAD_HANDLE(tid);
} }
// WaitForSingleObject - blocks on process handles via SysWaitForProcess; // WaitForSingleObject - blocks on process handles via SysWaitForProcess;
// thread and other handle types are not yet waitable so return immediately. // thread and other handle types are not yet waitable so return immediately.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) { __declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
(void)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;
uint32_t kh = HANDLE_TO_KH(hHandle); uint32_t kh = HANDLE_TO_KH(hHandle);
if (kh) SysWaitForProcess(kh); // no-op KSTATUS if not a process handle if (kh) {
uint32_t s = SysWaitForProcess(kh);
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
}
return WAIT_OBJECT_0; return WAIT_OBJECT_0;
} }
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
if (!commandLine || !out || outSize == 0) return FALSE;
while (*commandLine == ' ' || *commandLine == '\t') commandLine++;
if (*commandLine == '\0') return FALSE;
size_t pos = 0;
if (*commandLine == '"') {
commandLine++;
while (*commandLine && *commandLine != '"' && pos + 1 < outSize)
out[pos++] = *commandLine++;
} else {
while (*commandLine && *commandLine != ' ' && *commandLine != '\t' && pos + 1 < outSize)
out[pos++] = *commandLine++;
}
out[pos] = '\0';
return pos != 0;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Process creation // Process creation
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -669,9 +763,9 @@ __declspec(dllexport) BOOL CreateProcessA(
const char* lpCurrentDirectory, const char* lpCurrentDirectory,
STARTUPINFOA* lpStartupInfo, STARTUPINFOA* lpStartupInfo,
PROCESS_INFORMATION* lpProcessInformation) { PROCESS_INFORMATION* lpProcessInformation) {
(void)lpCommandLine; (void)lpProcessAttributes; (void)lpThreadAttributes; (void)lpProcessAttributes; (void)lpThreadAttributes;
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment; (void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
(void)lpCurrentDirectory; (void)lpStartupInfo; (void)lpStartupInfo;
// lpProcessInformation is mandatory // lpProcessInformation is mandatory
if (!lpProcessInformation) { if (!lpProcessInformation) {
@@ -679,27 +773,56 @@ __declspec(dllexport) BOOL CreateProcessA(
return FALSE; return FALSE;
} }
// Standard Win32 behavior: use lpCommandLine if lpApplicationName is NULL char appPath[MAX_PATH];
const char* targetPath = lpApplicationName; const char* targetPath = lpApplicationName;
if (!targetPath) { if (!targetPath) {
targetPath = lpCommandLine; if (!ExtractApplicationPathA(lpCommandLine, appPath, sizeof(appPath))) {
gLastError = 87;
return FALSE;
}
targetPath = appPath;
} }
// If both are NULL, then it's truly an invalid parameter const char* cmdline = (lpCommandLine && lpCommandLine[0]) ? lpCommandLine : targetPath;
if (!targetPath) {
gLastError = 87;
return FALSE;
}
// Build the cmdline: prefer lpCommandLine if given, otherwise fall back to path
const char* cmdline = lpCommandLine ? lpCommandLine : targetPath;
char translatedPath[MAX_PATH]; char translatedPath[MAX_PATH];
TranslatePath(targetPath, translatedPath); TranslatePath(targetPath, translatedPath);
char savedCwd[SYS_CWD_MAX];
bool changedCwd = false;
savedCwd[0] = '\0';
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
char translatedCwd[MAX_PATH];
uint32_t gs = SysGetCwd(savedCwd, sizeof(savedCwd));
if (gs != 0) { gLastError = 3; return FALSE; }
TranslatePath(lpCurrentDirectory, translatedCwd);
gs = SysSetCwd(translatedCwd);
if (gs != 0) { gLastError = 3; return FALSE; }
changedCwd = true;
}
uint32_t kh = 0; uint32_t kh = 0;
// Call the syscall.dll stub // Call the syscall.dll stub. The kernel snapshots the current CWD into
// the child process, so the temporary lpCurrentDirectory switch can be
// restored immediately after creation.
uint32_t s = SysCreateProcess(translatedPath, &kh, cmdline); uint32_t s = SysCreateProcess(translatedPath, &kh, cmdline);
if (changedCwd) {
if (savedCwd[0]) {
char restoreCwd[SYS_CWD_MAX + 3];
restoreCwd[0] = '0'; restoreCwd[1] = ':'; restoreCwd[2] = '/';
size_t rlen = strlen(savedCwd);
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
memcpy(restoreCwd + 3, savedCwd, rlen);
restoreCwd[3 + rlen] = '\0';
SysSetCwd(restoreCwd);
} else {
SysSetCwd("0:/");
}
}
if (s != 0 || kh == 0) { if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND gLastError = 2; // ERROR_FILE_NOT_FOUND
return FALSE; return FALSE;
@@ -715,7 +838,7 @@ __declspec(dllexport) BOOL CreateProcessA(
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) { __declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
if (!lpExitCode) { gLastError = 87; return FALSE; } if (!lpExitCode) { gLastError = 87; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hProcess); uint32_t kh = HANDLE_TO_KH(hProcess);
if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE if (!kh || IS_THREAD_HANDLE(hProcess)) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
int code = 0; int code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code); uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; } if (s != 0) { gLastError = 6; return FALSE; }
@@ -1078,7 +1201,7 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner, __declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) { const char* lpName) {
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName; (void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
return (HANDLE)(uintptr_t)0x4D555458UL; // 'MUTX' sentinel return (HANDLE)WIN32_MUTEX_SENTINEL;
} }
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) { __declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
@@ -1091,7 +1214,7 @@ __declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
const char* lpName) { const char* lpName) {
(void)lpSemaphoreAttributes; (void)lInitialCount; (void)lpSemaphoreAttributes; (void)lInitialCount;
(void)lMaximumCount; (void)lpName; (void)lMaximumCount; (void)lpName;
return (HANDLE)(uintptr_t)0x53454D41UL; // 'SEMA' sentinel return (HANDLE)WIN32_SEMA_SENTINEL;
} }
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount, __declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
@@ -1303,17 +1426,40 @@ __declspec(dllexport) unsigned int GetWindowsDirectoryA(char* lpBuffer, unsigned
return needed - 1; return needed - 1;
} }
static const char* LookupStaticEnvA(const char* name) {
if (!name) return NULL;
if (strcmp(name, "HOME") == 0) return ".";
if (strcmp(name, "USERPROFILE") == 0) return ".";
if (strcmp(name, "HOMEDRIVE") == 0) return "C:";
if (strcmp(name, "HOMEPATH") == 0) return "\\";
return NULL;
}
__declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer, __declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer,
DWORD nSize) { DWORD nSize) {
(void)lpName; (void)lpBuffer; (void)nSize; const char* value = LookupStaticEnvA(lpName);
gLastError = 203; // ERROR_ENVVAR_NOT_FOUND if (!value) {
return 0; if (lpBuffer && nSize) lpBuffer[0] = '\0';
gLastError = 203; // ERROR_ENVVAR_NOT_FOUND
return 0;
}
DWORD len = (DWORD)strlen(value);
if (!lpBuffer || nSize == 0) return len;
if (len + 1 > nSize) {
if (nSize) lpBuffer[0] = '\0';
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return len + 1;
}
memcpy(lpBuffer, value, len + 1);
return len;
} }
__declspec(dllexport) char* GetEnvironmentStrings(void) { __declspec(dllexport) char* GetEnvironmentStrings(void) {
// Return a double-NUL-terminated empty environment block. // Double-NUL-terminated environment block.
static char emptyEnv[2] = { '\0', '\0' }; static char env[] = "HOME=.\0USERPROFILE=.\0HOMEDRIVE=C:\0HOMEPATH=\\\0\0";
return emptyEnv; return env;
} }
__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) { __declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) {
+13 -6
View File
@@ -1,5 +1,7 @@
# NT-Lite -- an x86-64 kernel # NT-Lite -- an x86-64 kernel
## The Monster From ~~Redmond~~ Kraków
Boots. Runs processes. Mimics Windows to some extent. Boots. Runs processes. Mimics Windows to some extent.
## what is it? ## what is it?
@@ -17,7 +19,7 @@ The codebase is roughly 20k lines of C and assembly.
- preemptive scheduler with priority levels - preemptive scheduler with priority levels
- processes and threads - processes and threads
- VFS with IRPs - VFS with IRPs
- drive-letter paths such as `0:\apps\shell.exe` - drive-letter paths such as `0:/apps/shell.exe`
- INI-based boot and driver configuration - INI-based boot and driver configuration
- PE import resolution and dynamic driver loading - PE import resolution and dynamic driver loading
- partial Win32 personality - partial Win32 personality
@@ -52,7 +54,7 @@ Currently shipping: AHCI (interrupts), IDE (PIO/DMA), and FAT32.
All file access flows through the VFS layer. That keeps disk access, filesystem parsing, and path translation separate instead of spreading storage logic across the rest of the kernel. All file access flows through the VFS layer. That keeps disk access, filesystem parsing, and path translation separate instead of spreading storage logic across the rest of the kernel.
Paths are not interpreted as Windows paths by default. A drive such as `0:\` is a mounted namespace, while the Win32 and POSIX personalities translate their own path styles into the same underlying VFS. Paths are not interpreted as Windows paths by default. A drive such as `0:/` is a mounted namespace, while the Win32 and POSIX personalities translate their own path styles into the same underlying VFS.
### processes, threads, and syscalls ### processes, threads, and syscalls
@@ -84,9 +86,9 @@ The Win32 personality supports programs that expect drive letters, familiar proc
## paths ## paths
```text ```text
"apps\shell.exe" -> current working directory "apps/shell.exe" -> current working directory
"0:\apps\shell.exe" -> drive 0 "0:/apps/shell.exe" -> drive 0
"2:\data\config.ini" -> drive 2 "2:/data/config.ini" -> drive 2
``` ```
Drives 0-7 are available. A drive only works if a filesystem is registered on it. The Win32 personality translates drive letters to drive numbers, while the POSIX personality maps drives onto a tree. Drives 0-7 are available. A drive only works if a filesystem is registered on it. The Win32 personality translates drive letters to drive numbers, while the POSIX personality maps drives onto a tree.
@@ -290,6 +292,9 @@ A: Learning. Fun.
Q: Does it run Doom? Q: Does it run Doom?
A: `doom.exe` A: `doom.exe`
Q: How often does it crash?
A: Yes.
Q: Is the Win32 layer real? Q: Is the Win32 layer real?
A: Real enough to load binaries that expect it. Not real enough to run Notepad. A: Real enough to load binaries that expect it. Not real enough to run Notepad.
@@ -299,7 +304,7 @@ A: No. But IPC is free.
Q: Is this finished? Q: Is this finished?
A: No. A: No.
Will it corrupt my data? Q: Will it corrupt my data?
A: It shouldn't. A: It shouldn't.
Q: Why is the path syntax so weird? Q: Why is the path syntax so weird?
@@ -313,3 +318,5 @@ A: This document.
Q: Can I use this in production? Q: Can I use this in production?
A: Don't. A: Don't.
#### Projekt dedykowany dla szanownego Pana profesora Leszka Wójcika przez którego znienawidziłem Unixa.