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:
1 parent
ddb5d5f5d8
commit
f5fd7bdd75
28 files changed
+941
-329
No files matched your search
Binary file not shown.
@@ -167,7 +167,7 @@ for %%f in (%SRC%\doom\*.c) do (
|
||||
:: ---- Link doom.exe ----
|
||||
echo Linking doom.exe...
|
||||
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 ----
|
||||
echo Compiling video...
|
||||
|
||||
+183
-151
@@ -1,43 +1,51 @@
|
||||
// doom.c — NT-Lite port of PureDOOM
|
||||
// Win32 personality carrying all the weight, just like the real ntoskrnl does.
|
||||
// doom.c — PureDOOM port
|
||||
// Native personality carrying all the weight.
|
||||
// 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 <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
int _fltused;
|
||||
#include <syscall.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Globals
|
||||
// ---------------------------------------------------------------------------
|
||||
static HANDLE g_stdout;
|
||||
#define DOOM_IMPLEMENTATION
|
||||
#include "PureDOOM.h"
|
||||
|
||||
int _fltused;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// doom_print_fn
|
||||
// ---------------------------------------------------------------------------
|
||||
static void doom_print_wrapper(const char* msg)
|
||||
{
|
||||
UINT len = (UINT)strlen(msg);
|
||||
WriteConsoleA(g_stdout, msg, len, 0, 0);
|
||||
SysWriteConsole(msg, strlen(msg));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// doom_malloc_fn / doom_free_fn
|
||||
// Prepend size tracking metadata to satisfy SysFree's size requirement.
|
||||
// ---------------------------------------------------------------------------
|
||||
static void* doom_malloc_wrapper(int size)
|
||||
{
|
||||
void* p = HeapAlloc(0, HEAP_ZERO_MEMORY, size);
|
||||
if (!p) ExitProcess(GetLastError());
|
||||
return p;
|
||||
size_t total_size = size + sizeof(size_t);
|
||||
void* p = SysAlloc(total_size);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
DWORD access = GENERIC_READ;
|
||||
DWORD disp = OPEN_EXISTING;
|
||||
uint32_t handle = 0;
|
||||
uint32_t result;
|
||||
|
||||
if (mode[0] == 'w') {
|
||||
access = GENERIC_WRITE;
|
||||
disp = CREATE_ALWAYS;
|
||||
} else if (mode[0] == 'r' && mode[1] == '+') {
|
||||
access = GENERIC_READ | GENERIC_WRITE;
|
||||
result = SysCreateFile(path, &handle);
|
||||
} else {
|
||||
result = SysOpenFile(path, &handle);
|
||||
}
|
||||
|
||||
HANDLE h = CreateFileA(path, access, FILE_SHARE_READ, 0, disp, 0, 0);
|
||||
return (h == INVALID_HANDLE_VALUE) ? 0 : h;
|
||||
return (result == 0) ? (void*)(uintptr_t)handle : 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DWORD read = 0;
|
||||
ReadFile((HANDLE)handle, buf, (DWORD)count, &read, 0);
|
||||
size_t read = 0;
|
||||
SysReadFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &read);
|
||||
return (int)read;
|
||||
}
|
||||
|
||||
static int doom_write_wrapper(void* handle, const void* buf, int count)
|
||||
{
|
||||
DWORD written = 0;
|
||||
WriteFile((HANDLE)handle, buf, (DWORD)count, &written, 0);
|
||||
size_t written = 0;
|
||||
SysWriteFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &written);
|
||||
return (int)written;
|
||||
}
|
||||
|
||||
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) {
|
||||
case DOOM_SEEK_SET: method = FILE_BEGIN; break;
|
||||
case DOOM_SEEK_CUR: method = FILE_CURRENT; break;
|
||||
case DOOM_SEEK_END: method = FILE_END; break;
|
||||
default: method = FILE_BEGIN; break;
|
||||
case DOOM_SEEK_SET:
|
||||
absolute_offset = (uint64_t)offset;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DWORD size = GetFileSize((HANDLE)handle, 0);
|
||||
DWORD pos = (DWORD)doom_tell_wrapper(handle);
|
||||
uint32_t h = (uint32_t)(uintptr_t)handle;
|
||||
uint64_t size = SysGetFileSize(h);
|
||||
uint64_t pos = SysGetFilePos(h);
|
||||
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)
|
||||
{
|
||||
DWORD t = GetTickCount(); // milliseconds
|
||||
*sec = (int)(t / 1000);
|
||||
*usec = (int)((t % 1000) * 1000); // ms remainder → us
|
||||
uint64_t t = SysGetTick();
|
||||
*sec = (int)(t / 1000000); // 1,000,000 us = 1 second
|
||||
*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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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");
|
||||
return home;
|
||||
}
|
||||
@@ -136,102 +154,88 @@ static char* doom_getenv_wrapper(const char* name)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Input — poll GetAsyncKeyState every frame
|
||||
// Generates key_down / key_up edge events, not held-key spam.
|
||||
// Input — Maps Scancode Set 1 values to PureDOOM
|
||||
// ---------------------------------------------------------------------------
|
||||
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[] = {
|
||||
// Letters — VK is uppercase, DOOM key is lowercase
|
||||
{ 'W', DOOM_KEY_W },
|
||||
{ 'A', DOOM_KEY_A },
|
||||
{ 'S', DOOM_KEY_S },
|
||||
{ 'D', DOOM_KEY_D },
|
||||
{ 'E', DOOM_KEY_E },
|
||||
{ 'Q', DOOM_KEY_Q },
|
||||
{ 'Y', DOOM_KEY_Y }, // yes/no menus
|
||||
{ 'N', DOOM_KEY_N },
|
||||
// Letters
|
||||
{ 0x11, false, DOOM_KEY_W },
|
||||
{ 0x1E, false, DOOM_KEY_A },
|
||||
{ 0x1F, false, DOOM_KEY_S },
|
||||
{ 0x20, false, DOOM_KEY_D },
|
||||
{ 0x12, false, DOOM_KEY_E },
|
||||
{ 0x10, false, DOOM_KEY_Q },
|
||||
{ 0x15, false, DOOM_KEY_Y },
|
||||
{ 0x31, false, DOOM_KEY_N },
|
||||
|
||||
// Arrow keys
|
||||
{ VK_UP, DOOM_KEY_UP_ARROW },
|
||||
{ VK_DOWN, DOOM_KEY_DOWN_ARROW },
|
||||
{ VK_LEFT, DOOM_KEY_LEFT_ARROW },
|
||||
{ VK_RIGHT, DOOM_KEY_RIGHT_ARROW },
|
||||
{ 0x48, true, DOOM_KEY_UP_ARROW },
|
||||
{ 0x50, true, DOOM_KEY_DOWN_ARROW },
|
||||
{ 0x4B, true, DOOM_KEY_LEFT_ARROW },
|
||||
{ 0x4D, true, DOOM_KEY_RIGHT_ARROW },
|
||||
|
||||
// Action keys — Ctrl=fire, Shift=run, Alt=strafe
|
||||
{ VK_CONTROL, DOOM_KEY_CTRL },
|
||||
{ VK_SPACE, DOOM_KEY_SPACE },
|
||||
{ VK_SHIFT, DOOM_KEY_SHIFT },
|
||||
{ VK_MENU, DOOM_KEY_ALT },
|
||||
// Action keys
|
||||
{ 0x1D, false, DOOM_KEY_CTRL },
|
||||
{ 0x39, false, DOOM_KEY_SPACE },
|
||||
{ 0x2A, false, DOOM_KEY_SHIFT },
|
||||
{ 0x38, false, DOOM_KEY_ALT },
|
||||
|
||||
// Navigation
|
||||
{ VK_ESCAPE, DOOM_KEY_ESCAPE },
|
||||
{ VK_RETURN, DOOM_KEY_ENTER },
|
||||
{ VK_TAB, DOOM_KEY_TAB },
|
||||
{ VK_BACK, DOOM_KEY_BACKSPACE },
|
||||
{ VK_PAUSE, DOOM_KEY_PAUSE },
|
||||
{ 0x01, false, DOOM_KEY_ESCAPE },
|
||||
{ 0x1C, false, DOOM_KEY_ENTER },
|
||||
{ 0x0F, false, DOOM_KEY_TAB },
|
||||
{ 0x0E, false, DOOM_KEY_BACKSPACE },
|
||||
|
||||
// Weapon slots
|
||||
{ '1', DOOM_KEY_1 },
|
||||
{ '2', DOOM_KEY_2 },
|
||||
{ '3', DOOM_KEY_3 },
|
||||
{ '4', DOOM_KEY_4 },
|
||||
{ '5', DOOM_KEY_5 },
|
||||
{ '6', DOOM_KEY_6 },
|
||||
{ '7', DOOM_KEY_7 },
|
||||
{ 0x02, false, DOOM_KEY_1 },
|
||||
{ 0x03, false, DOOM_KEY_2 },
|
||||
{ 0x04, false, DOOM_KEY_3 },
|
||||
{ 0x05, false, DOOM_KEY_4 },
|
||||
{ 0x06, false, DOOM_KEY_5 },
|
||||
{ 0x07, false, DOOM_KEY_6 },
|
||||
{ 0x08, false, DOOM_KEY_7 },
|
||||
|
||||
// Misc
|
||||
{ '-', DOOM_KEY_MINUS },
|
||||
{ '=', DOOM_KEY_EQUALS },
|
||||
{ ',', DOOM_KEY_COMMA }, // default strafe left
|
||||
{ '.', DOOM_KEY_PERIOD }, // default strafe right
|
||||
{ 0x0C, false, DOOM_KEY_MINUS },
|
||||
{ 0x0D, false, DOOM_KEY_EQUALS },
|
||||
{ 0x33, false, DOOM_KEY_COMMA },
|
||||
{ 0x34, false, DOOM_KEY_PERIOD },
|
||||
|
||||
// Function keys — save/load/volume/etc
|
||||
{ VK_F1, DOOM_KEY_F1 },
|
||||
{ VK_F2, DOOM_KEY_F2 },
|
||||
{ VK_F3, DOOM_KEY_F3 },
|
||||
{ VK_F4, DOOM_KEY_F4 },
|
||||
{ VK_F5, DOOM_KEY_F5 },
|
||||
{ VK_F6, DOOM_KEY_F6 },
|
||||
{ VK_F7, DOOM_KEY_F7 },
|
||||
{ VK_F8, DOOM_KEY_F8 },
|
||||
{ VK_F9, DOOM_KEY_F9 },
|
||||
{ VK_F10, DOOM_KEY_F10 },
|
||||
{ VK_F11, DOOM_KEY_F11 },
|
||||
{ VK_F12, DOOM_KEY_F12 },
|
||||
// Function keys
|
||||
{ 0x3B, false, DOOM_KEY_F1 },
|
||||
{ 0x3C, false, DOOM_KEY_F2 },
|
||||
{ 0x3D, false, DOOM_KEY_F3 },
|
||||
{ 0x3E, false, DOOM_KEY_F4 },
|
||||
{ 0x3F, false, DOOM_KEY_F5 },
|
||||
{ 0x40, false, DOOM_KEY_F6 },
|
||||
{ 0x41, false, DOOM_KEY_F7 },
|
||||
{ 0x42, false, DOOM_KEY_F8 },
|
||||
{ 0x43, false, DOOM_KEY_F9 },
|
||||
{ 0x44, false, DOOM_KEY_F10 },
|
||||
{ 0x57, false, DOOM_KEY_F11 },
|
||||
{ 0x58, false, DOOM_KEY_F12 },
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
|
||||
int vk = g_keymap[i].vk;
|
||||
short cur = GetAsyncKeyState(vk);
|
||||
int cur_down = (cur & 0x8000) != 0;
|
||||
int prev_down = (g_prev_key[vk] & 0x8000) != 0;
|
||||
bool cur_down = (SysKbIsKeyDown(g_keymap[i].scancode, g_keymap[i].extended) != 0);
|
||||
bool prev_down = g_prev_key[i];
|
||||
|
||||
if (cur_down && !prev_down) doom_key_down(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.
|
||||
//
|
||||
// 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.
|
||||
// Render — Performs lightweight software upscale and blits to the screen
|
||||
// ---------------------------------------------------------------------------
|
||||
#define DOOM_W 320
|
||||
#define DOOM_H 200
|
||||
@@ -239,55 +243,82 @@ static void poll_input(void)
|
||||
#define DISP_W (DOOM_W * SCALE)
|
||||
#define DISP_H (DOOM_H * SCALE)
|
||||
|
||||
static BITMAPINFO g_bmi;
|
||||
static uint32_t g_blit_buf[DOOM_W * DOOM_H]; // BGRA-swapped copy
|
||||
static uint32_t g_blit_buf[DISP_W * DISP_H];
|
||||
|
||||
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;
|
||||
for (int i = 0; i < DOOM_W * DOOM_H; i++) {
|
||||
uint32_t p = src[i];
|
||||
// RGBA -> BGRA: swap byte 0 (R) and byte 2 (B)
|
||||
g_blit_buf[i] = (p & 0xFF00FF00u)
|
||||
|
||||
// Software upscale loop mapping 320x200 buffer directly to scaled window buffer
|
||||
for (int y = 0; y < DOOM_H; y++) {
|
||||
for (int x = 0; x < DOOM_W; x++) {
|
||||
uint32_t p = src[y * DOOM_W + x];
|
||||
|
||||
// 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;
|
||||
HDC hdc = BeginPaint((HWND)0, &ps);
|
||||
StretchDIBits(
|
||||
hdc,
|
||||
0, 0, DISP_W, DISP_H, // destination — scaled up
|
||||
0, 0, DOOM_W, DOOM_H, // source — native 320×200
|
||||
g_blit_buf,
|
||||
&g_bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY
|
||||
);
|
||||
EndPaint((HWND)0, &ps);
|
||||
SysScreenBlit(0, 0, DISP_W, DISP_H, g_blit_buf);
|
||||
}
|
||||
|
||||
static int build_argv_from_command_line(const char* cmdline, char* storage, size_t storageSize,
|
||||
const char** argv, int maxArgv) {
|
||||
if (!storage || storageSize == 0 || !argv || maxArgv <= 1) return 0;
|
||||
|
||||
if (!cmdline || cmdline[0] == '\0') cmdline = "doom";
|
||||
|
||||
size_t len = strlen(cmdline);
|
||||
if (len >= storageSize) len = storageSize - 1;
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
int main(const char* cmdline)
|
||||
int main(void)
|
||||
{
|
||||
(void)cmdline;
|
||||
const char* cmdline = SysGetCommandLine();
|
||||
|
||||
g_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
WriteConsoleA(g_stdout, "doom: starting\n", 15, 0, 0);
|
||||
SysWriteConsole("doom: starting\n", 15);
|
||||
|
||||
// Prepare the BMI header once (top-down, like video.c)
|
||||
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
|
||||
// Wire up PureDOOM core mechanics
|
||||
doom_set_print(doom_print_wrapper);
|
||||
doom_set_malloc(doom_malloc_wrapper, doom_free_wrapper);
|
||||
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("mouse_move", 0);
|
||||
|
||||
// No extra args — PureDOOM will scan for doom1.wad / doom.wad
|
||||
const char* args[] = { "doom" };
|
||||
doom_init(1, (char**)args, 0);
|
||||
char argvStorage[512];
|
||||
const char* argv[32];
|
||||
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) {
|
||||
poll_input();
|
||||
doom_update();
|
||||
render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */));
|
||||
}
|
||||
|
||||
// Unreachable: doom_exit() -> ExitProcess() handles clean shutdown
|
||||
ExitProcess(0);
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -669,11 +669,15 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, void *buf,
|
||||
size_t size, size_t *bytesRead) {
|
||||
(void)fs;
|
||||
if (!bytesRead) return STATUS_INVALID_PARAMETER;
|
||||
*bytesRead = 0;
|
||||
if (!fileCtx || !buf) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
||||
FAT32_VOL *v = f->Vol;
|
||||
|
||||
if (offset >= f->FileSize) { *bytesRead = 0; return STATUS_SUCCESS; }
|
||||
if (offset >= f->FileSize || size == 0) return STATUS_SUCCESS;
|
||||
if (offset + size > f->FileSize) size = (size_t)(f->FileSize - offset);
|
||||
size_t wanted = size;
|
||||
|
||||
// Single allocation for the largest possible coalesced run.
|
||||
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;
|
||||
uint8_t *out = (uint8_t *)buf;
|
||||
KSTATUS readStatus = STATUS_SUCCESS;
|
||||
uint32_t skipClusters = (uint32_t)(offset / v->BytesPerCluster);
|
||||
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint32_t cluster = f->StartCluster;
|
||||
@@ -701,8 +706,11 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t runBytes = runSectors * 512u;
|
||||
size_t avail = runBytes - offsetInCluster;
|
||||
@@ -716,8 +724,17 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
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);
|
||||
*bytesRead = totalRead;
|
||||
if (totalRead != wanted)
|
||||
return KSUCCESS(readStatus) ? STATUS_DEVICE_ERROR : readStatus;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -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 s = 0;
|
||||
|
||||
for (;;) {
|
||||
while (true) {
|
||||
SysDebugPrint("[init] starting\n");
|
||||
s = SysCreateProcess(args, &h, NULL);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
|
||||
+21
-2
@@ -5,6 +5,7 @@
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_userenv.h>
|
||||
|
||||
#define MAX_DRIVES 8
|
||||
|
||||
@@ -148,6 +149,23 @@ __declspec(dllexport) KSTATUS IoSetCwd(const char* path) {
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||
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);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -209,8 +227,9 @@ __declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
|
||||
// IoReadFile - sequential read at current offset
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf, size_t size, size_t* bytesRead) {
|
||||
if (!file || !buf) return STATUS_INVALID_PARAMETER;
|
||||
if (file->Offset >= file->Size) { *bytesRead = 0; return STATUS_SUCCESS; }
|
||||
if (bytesRead) *bytesRead = 0;
|
||||
if (!file || !buf || !bytesRead) return STATUS_INVALID_PARAMETER;
|
||||
if (file->Offset >= file->Size || size == 0) return STATUS_SUCCESS;
|
||||
|
||||
// Clamp to remaining bytes.
|
||||
size_t remaining = (size_t)(file->Size - file->Offset);
|
||||
|
||||
@@ -36,7 +36,7 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) {
|
||||
}
|
||||
|
||||
// KePanic
|
||||
__declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
|
||||
__declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...) {
|
||||
// Silence! Stop the world!
|
||||
_disable();
|
||||
|
||||
@@ -44,7 +44,7 @@ __declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
|
||||
// We call PrintChar directly so we can set a red foreground.
|
||||
// (DbgPrint uses green; we want this visually distinct.)
|
||||
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);
|
||||
|
||||
// Now print the caller's message through the normal vprintf path.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
|
||||
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
|
||||
__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).
|
||||
|
||||
+130
-1
@@ -5,6 +5,7 @@
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_userenv.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
@@ -84,12 +85,27 @@ static void ProcessDestroy(OBJECT* obj) {
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Release the exit event.
|
||||
if (proc->ExitEvent) {
|
||||
@@ -129,9 +145,17 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
||||
proc->ExitCode = 0;
|
||||
proc->ImageBase = 0;
|
||||
proc->ImageSize = 0;
|
||||
proc->UserPebBase = 0;
|
||||
proc->UserPebSize = 0;
|
||||
proc->UserImagePathBase = 0;
|
||||
proc->UserImagePathSize = 0;
|
||||
proc->UserCmdlineBase = 0;
|
||||
proc->UserCmdlineSize = 0;
|
||||
proc->UserCwdBase = 0;
|
||||
proc->UserCwdSize = 0;
|
||||
proc->ModuleCount = 0;
|
||||
proc->CurrentDirectory[0] = '\0';
|
||||
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
|
||||
// Slot 0 is the invalid handle - leave it NULL.
|
||||
memset(proc->Handles, 0, sizeof(proc->Handles));
|
||||
@@ -152,6 +176,111 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_event.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define PROCESS_NAME_MAX 64
|
||||
#define HANDLE_TABLE_SIZE 256
|
||||
@@ -26,11 +27,6 @@ typedef struct PROCESS {
|
||||
uint32_t ProcessId;
|
||||
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];
|
||||
SPINLOCK CwdLock;
|
||||
|
||||
@@ -50,9 +46,15 @@ typedef struct PROCESS {
|
||||
uintptr_t ImageBase;
|
||||
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;
|
||||
size_t UserCmdlineSize;
|
||||
uintptr_t UserCwdBase;
|
||||
size_t UserCwdSize;
|
||||
|
||||
/* Per-process loaded modules (user processes only). */
|
||||
PROCESS_MODULE Modules[PROCESS_MODULE_MAX];
|
||||
@@ -66,6 +68,7 @@ typedef struct PROCESS {
|
||||
} PROCESS;
|
||||
|
||||
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);
|
||||
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
; sizeof(OBJECT) = 16 (type:4, refcount:4, destroy:8)
|
||||
; THREAD.SavedContext offset = 88
|
||||
; THREAD.TssRsp0 offset = 104
|
||||
; THREAD.Teb offset = 112
|
||||
; THREAD.FpuContext offset = 168
|
||||
; TSS64.RSP0 offset = 4
|
||||
;
|
||||
THREAD_SavedContext EQU 88
|
||||
THREAD_TssRsp0 EQU 104
|
||||
THREAD_Teb EQU 112
|
||||
THREAD_FpuContext EQU 160
|
||||
THREAD_FpuContext EQU 168
|
||||
TSS_RSP0_OFFSET EQU 4
|
||||
|
||||
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
|
||||
|
||||
+34
-34
@@ -756,18 +756,18 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
uint64_t size = IoGetFileSize(f);
|
||||
void* mem = MmAllocateUserMemory(size);
|
||||
void* mem = MmAllocatePool(size);
|
||||
if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; }
|
||||
|
||||
size_t got;
|
||||
s = IoReadFile(f, mem, size, &got);
|
||||
IoCloseFile(f);
|
||||
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
|
||||
if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
|
||||
|
||||
// Create the process object.
|
||||
PROCESS* proc;
|
||||
s = KeCreateProcess(path, &proc);
|
||||
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
|
||||
if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
|
||||
|
||||
// Inherit CWD from parent
|
||||
PROCESS* parent = KeGetCurrentProcess();
|
||||
@@ -778,27 +778,13 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
KeReleaseSpinlock(&parent->CwdLock, flags);
|
||||
}
|
||||
|
||||
// Store the command line in the process so _start can pick it up.
|
||||
// If no cmdline was supplied default to just the image path.
|
||||
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;
|
||||
s = KeSetupUserProcessEnvironment(proc, path, cmdline, proc->CurrentDirectory);
|
||||
if (!KSUCCESS(s)) { MmFreePool(mem); ObDereferenceObject(&proc->Header); return s; }
|
||||
|
||||
// Map the PE image into memory.
|
||||
uintptr_t imageBase = 0;
|
||||
s = LdrLoadImage(mem, size, &imageBase, true);
|
||||
MmFreeUserMemory(mem, size);
|
||||
MmFreePool(mem);
|
||||
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
||||
|
||||
s = LdrResolveImportsForProcess(proc, imageBase);
|
||||
@@ -808,27 +794,41 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
return s;
|
||||
}
|
||||
|
||||
// Spin up the initial thread. Pass userCmdline as UserArg so _start
|
||||
// receives it directly in RCX without an extra syscall.
|
||||
proc->ImageBase = imageBase;
|
||||
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;
|
||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||
s = KeCreateThread(proc, entryPoint, (uintptr_t)userCmdline,
|
||||
PriorityNormal, &thread);
|
||||
s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
ObDereferenceObject(&proc->Header);
|
||||
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;
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
+34
-9
@@ -4,6 +4,7 @@
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_userenv.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
@@ -35,7 +36,12 @@ static void ThreadDestroy(OBJECT* obj) {
|
||||
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) {
|
||||
MmFreeUserMemory(thread->UserStackBase, USTACK_SIZE);
|
||||
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,
|
||||
THREAD_PRIORITY priority, THREAD **out) {
|
||||
THREAD_PRIORITY priority, THREAD** out) {
|
||||
if (!owner || !out) 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);
|
||||
if (!ustack) {
|
||||
MmFreePool(kstack);
|
||||
thread->KernelStackBase = (void*)0;
|
||||
ObDereferenceObject(&thread->Header);
|
||||
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
|
||||
// this magic number will hunt me in my dreams
|
||||
|
||||
// Initialize the dummy TEB
|
||||
// offset 0x10 is StackLimit, which __chkstk checks.
|
||||
uint64_t* teb = (uint64_t*)ustack;
|
||||
teb[0] = 0; // ExceptionList (GS:0x00)
|
||||
teb[1] = userStackTop; // StackBase
|
||||
teb[2] = (uintptr_t)ustack; // StackLimit
|
||||
SYS_TEB* teb = (SYS_TEB*)MmAllocateUserMemory(sizeof(SYS_TEB));
|
||||
if (!teb) {
|
||||
MmFreeUserMemory(ustack, USTACK_SIZE);
|
||||
thread->UserStackBase = (void*)0;
|
||||
MmFreePool(kstack);
|
||||
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->TebSize = sizeof(SYS_TEB);
|
||||
|
||||
thread->UserEntry = entry;
|
||||
thread->UserArg = arg;
|
||||
@@ -114,9 +132,15 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
|
||||
EVENT* exitEvent;
|
||||
KSTATUS s = KeEventCreate(EventManualReset, 0, &exitEvent);
|
||||
if (!KSUCCESS(s)) {
|
||||
if (thread->Teb && thread->TebSize) MmFreeUserMemory(thread->Teb, thread->TebSize);
|
||||
thread->Teb = (void*)0;
|
||||
thread->TebSize = 0;
|
||||
MmFreeUserMemory(ustack, USTACK_SIZE);
|
||||
thread->UserStackBase = (void*)0;
|
||||
MmFreePool(kstack);
|
||||
thread->KernelStackBase = (void*)0;
|
||||
ObDereferenceObject(&owner->Header);
|
||||
thread->Owner = (PROCESS*)0;
|
||||
ObDereferenceObject(&thread->Header);
|
||||
return s;
|
||||
}
|
||||
@@ -162,7 +186,7 @@ __declspec(dllexport) void KeExitThread(THREAD* thread, int code) {
|
||||
extern void KeKernelThreadEntry(void);
|
||||
|
||||
__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
|
||||
// the owner field; grab it from the idle/init thread that already exists.
|
||||
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->UserStackBase = (void*)0; // no user stack
|
||||
thread->Teb = (void*)0; // no TEB; GS_BASE will be 0 in kernel ctx
|
||||
thread->TebSize = 0;
|
||||
|
||||
void* kstack = MmAllocatePool(KSTACK_SIZE);
|
||||
if (!kstack) {
|
||||
|
||||
@@ -59,8 +59,9 @@ typedef struct THREAD {
|
||||
// Updated on every context switch so the CPU loads the right kernel stack.
|
||||
uintptr_t TssRsp0;
|
||||
|
||||
// Thread Environment Block
|
||||
// Thread Environment Block. GS base is loaded from this pointer.
|
||||
void* Teb;
|
||||
size_t TebSize;
|
||||
|
||||
// Ring-3 entry point and argument for newly created threads.
|
||||
uintptr_t UserEntry;
|
||||
@@ -79,14 +80,14 @@ typedef struct THREAD {
|
||||
} THREAD;
|
||||
|
||||
__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
|
||||
// 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
|
||||
// given priority.
|
||||
__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.
|
||||
__declspec(dllexport) void KeExitThread(THREAD* thread, int code);
|
||||
|
||||
@@ -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
@@ -250,8 +250,8 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
|
||||
uint64_t size = IoGetFileSize(f);
|
||||
DbgPrint("LaunchUserProcess: IoGetFileSize: %llu\n", size);
|
||||
|
||||
void* mem = MmAllocateUserMemory(size);
|
||||
DbgPrint("LaunchUserProcess: MmAllocateUserMemory: 0x%llX\n", mem);
|
||||
void* mem = MmAllocatePool(size);
|
||||
DbgPrint("LaunchUserProcess: MmAllocatePool: 0x%llX\n", mem);
|
||||
if (!mem) {
|
||||
IoCloseFile(f);
|
||||
return 0;
|
||||
@@ -262,7 +262,7 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
|
||||
DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s);
|
||||
IoCloseFile(f);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreeUserMemory(mem, size);
|
||||
MmFreePool(mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -273,35 +273,23 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
|
||||
s = KeCreateProcess(filePath, &proc);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("Failed to create process. System halted.\n");
|
||||
MmFreeUserMemory(mem, size);
|
||||
MmFreePool(mem);
|
||||
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])
|
||||
strncpy(proc->Cmdline, args, sizeof(proc->Cmdline) - 1);
|
||||
else
|
||||
strncpy(proc->Cmdline, filePath, sizeof(proc->Cmdline) - 1);
|
||||
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);
|
||||
s = KeSetupUserProcessEnvironment(proc, filePath, args, proc->CurrentDirectory);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
|
||||
MmFreePool(mem);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
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.
|
||||
uintptr_t imageBase = 0;
|
||||
s = LdrLoadImage(mem, size, &imageBase, true);
|
||||
MmFreeUserMemory(mem, size);
|
||||
MmFreePool(mem);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LaunchUserProcess: LdrLoadImage failed with status: %d\n", s);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
@@ -316,19 +304,22 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
proc->ImageBase = imageBase;
|
||||
proc->ImageSize = LdrGetImageSize(imageBase);
|
||||
|
||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||
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.
|
||||
s = KeCreateThread(proc, (uintptr_t)entryPoint, (uintptr_t)userCmdline, PriorityNormal, &thread);
|
||||
// Create the main thread last. KeCreateThread queues it before returning.
|
||||
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
||||
proc->ImageBase = 0;
|
||||
proc->ImageSize = 0;
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
proc->ImageBase = imageBase;
|
||||
proc->ImageSize = LdrGetImageSize(imageBase);
|
||||
DbgPrint("LaunchUserProcess: Thread created and queued!\n");
|
||||
|
||||
// Caller owns the reference returned by KeCreateProcess.
|
||||
|
||||
@@ -323,6 +323,7 @@ static void* HeapAlloc(HEAP* h, size_t size) {
|
||||
HdrToFtr(best)->Magic = BLOCK_MAGIC;
|
||||
HdrToFtr(best)->Size = best->Size;
|
||||
|
||||
memset((char *)best + HDR_SIZE, 0, size);
|
||||
KeReleaseSpinlock(&h->Lock, flags);
|
||||
return (char *)best + HDR_SIZE;
|
||||
}
|
||||
@@ -409,6 +410,7 @@ static void* HeapRealloc(HEAP* h, void* ptr, size_t new_size) {
|
||||
if (!new_ptr) {
|
||||
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.
|
||||
// We only copy up to the old block's usable size.
|
||||
|
||||
@@ -78,6 +78,8 @@ typedef void* pthread_mutexattr_t;
|
||||
typedef void* posix_spawn_file_actions_t;
|
||||
typedef void* posix_spawnattr_t;
|
||||
|
||||
__declspec(dllexport) const char* posix_get_command_line(void);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// dirent (used by posix_dir.c)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
+28
-8
@@ -22,9 +22,12 @@ static int KsToErrno(uint32_t s) {
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(dllexport) int ClearConsoleScreenBuffer(void* hConsole) {
|
||||
__declspec(dllexport) void clearscreen(void) {
|
||||
SysScreenClear(0x00000000);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void clearkeyboard(void) {
|
||||
while (SysKbPoll()) (void)SysKbReadChar();
|
||||
}
|
||||
|
||||
__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);
|
||||
if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; }
|
||||
|
||||
uint64_t target;
|
||||
int64_t target = 0;
|
||||
switch (whence) {
|
||||
case SEEK_SET: target = (uint64_t)offset; break;
|
||||
case SEEK_CUR: target = SysGetFilePos(e->KHandle) + (uint64_t)offset; break;
|
||||
case SEEK_END: target = SysGetFileSize(e->KHandle) + (uint64_t)offset; break;
|
||||
default: errno = EINVAL; return -1;
|
||||
case SEEK_SET:
|
||||
target = (int64_t)offset;
|
||||
break;
|
||||
case SEEK_CUR: {
|
||||
uint64_t cur = SysGetFilePos(e->KHandle);
|
||||
if (cur > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
|
||||
target = (int64_t)cur + (int64_t)offset;
|
||||
break;
|
||||
}
|
||||
uint32_t s = SysSeekFile(e->KHandle, target);
|
||||
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;
|
||||
}
|
||||
|
||||
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; }
|
||||
return (off_t)target;
|
||||
}
|
||||
@@ -5,6 +5,20 @@
|
||||
#include "posix.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)
|
||||
#define PROC_TABLE_SIZE 32
|
||||
typedef struct {
|
||||
|
||||
+12
-12
@@ -41,7 +41,6 @@ typedef struct {
|
||||
void* (*Fn)(void*);
|
||||
void* Arg;
|
||||
uint32_t Pid; // owner pid, for the retval table
|
||||
uint32_t Tid; // filled in by pthread_create after KeCreateThread returns
|
||||
} PTHREAD_TRAMP;
|
||||
|
||||
static void PthreadTrampoline(uintptr_t raw) {
|
||||
@@ -49,14 +48,19 @@ static void PthreadTrampoline(uintptr_t raw) {
|
||||
void* (*fn)(void*) = tr->Fn;
|
||||
void* arg = tr->Arg;
|
||||
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));
|
||||
|
||||
void* retval = fn(arg);
|
||||
|
||||
// Store retval for pthread_join. If the slot isn't there (join was called
|
||||
// before the thread was even created, which shouldn't happen) just drop it.
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
||||
// Store retval for pthread_join.
|
||||
if (slot) slot->RetVal = retval;
|
||||
|
||||
SysExitThread(0);
|
||||
@@ -77,7 +81,6 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
|
||||
tr->Fn = fn;
|
||||
tr->Arg = arg;
|
||||
tr->Pid = pid;
|
||||
tr->Tid = 0; // filled below
|
||||
|
||||
uint32_t tid = 0;
|
||||
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));
|
||||
return EAGAIN;
|
||||
}
|
||||
tr->Tid = tid; // safe: trampoline reads Tid after SysCreateThread returns
|
||||
|
||||
// Reserve a retval slot before the trampoline can exit
|
||||
PthreadSlotAlloc(pid, tid);
|
||||
|
||||
t->Tid = tid;
|
||||
// The trampoline allocates the retval slot itself after it knows its real
|
||||
// TID, avoiding the creator/new-thread race.
|
||||
if (t) t->Tid = tid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,27 @@ static void cmd_type(const char* filename) {
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -134,13 +155,16 @@ static int runCommand(const char* cmd) {
|
||||
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; 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, "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, "type ", 5) == 0) { cmd_type(cmd + 5); 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;
|
||||
if (SysCreateProcess(cmd, &hProc, cmd) == 0) {
|
||||
if (SysCreateProcess(program, &hProc, cmd) == 0) {
|
||||
SysWaitForProcess(hProc);
|
||||
SysCloseHandle(hProc);
|
||||
while (SysKbPoll()) (void)SysKbReadChar();
|
||||
@@ -148,8 +172,13 @@ static int runCommand(const char* cmd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(void) {
|
||||
const char* cmdline = SysGetCommandLine();
|
||||
char lineBuf[MAX_CMD];
|
||||
|
||||
print("shell: command line: ");
|
||||
print(cmdline);
|
||||
print("\n");
|
||||
print("shell: ready\n");
|
||||
while (true) {
|
||||
print("> ");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#define MAX_CMD 256
|
||||
#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 waitpid(int pid, int *status, int options);
|
||||
extern void exit(int status);
|
||||
extern int ClearConsoleScreenBuffer(void* h);
|
||||
extern void clearscreen(void);
|
||||
extern void clearkeyboard(void);
|
||||
extern void PosixFdInit(void);
|
||||
|
||||
static int hIn = 0; static int hOut = 1;
|
||||
@@ -52,6 +54,31 @@ static void readLine(char* buf, int len) {
|
||||
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) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
@@ -111,25 +138,40 @@ static int runCommand(const char* cmd) {
|
||||
if (!cmd || *cmd == '\0') return 0;
|
||||
if (strcmp(cmd, "exit") == 0) return 1;
|
||||
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, "dir") == 0) { cmd_dir("."); 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, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
|
||||
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
|
||||
|
||||
int pid; char *argv[] = {(char*)cmd, NULL};
|
||||
if (posix_spawn(&pid, cmd, NULL, NULL, argv, NULL) == 0) {
|
||||
char cmdBuf[MAX_CMD];
|
||||
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);
|
||||
clearkeyboard();
|
||||
} else { print("shell: launch failed\n"); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(void) {
|
||||
PosixFdInit();
|
||||
|
||||
const char* cmdline = SysGetCommandLine();
|
||||
char lineBuf[MAX_CMD];
|
||||
|
||||
print("shell: command line: ");
|
||||
print(cmdline);
|
||||
print("\n");
|
||||
print("shell: ready\n");
|
||||
while (true) {
|
||||
print("> ");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#define MAX_CMD 256
|
||||
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, "dir") == 0) { cmd_dir("."); 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, "type ", 5) == 0) { cmd_type(cmd + 5); 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';
|
||||
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
|
||||
if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(pi.hThread);
|
||||
FlushConsoleInputBuffer(hIn);
|
||||
} else {
|
||||
print("shell: launch failed\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(void) {
|
||||
hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
const char* cmdline = SysGetCommandLine();
|
||||
char lineBuf[MAX_CMD];
|
||||
|
||||
print("shell: command line: ");
|
||||
print(cmdline);
|
||||
print("\n");
|
||||
print("shell: ready\n");
|
||||
while (true) {
|
||||
print("> ");
|
||||
|
||||
@@ -4,6 +4,56 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.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)
|
||||
|
||||
+40
-4
@@ -5,16 +5,52 @@
|
||||
#include <intrin.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);
|
||||
WriteConsoleA(h, "video: starting\n", 16, 0, 0);
|
||||
|
||||
WriteConsoleA(h, cmdline, (unsigned int)strlen(cmdline), 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
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -34,7 +70,7 @@ int main(const char* cmdline) {
|
||||
WriteConsoleA(h, "video: ReadFile failed\n", 23, 0, 0);
|
||||
ExitProcess(1);
|
||||
}
|
||||
WriteConsoleA(h, "video: video.bk2 loaded\n", 24, 0, 0);
|
||||
WriteConsoleA(h, "video: input file loaded\n", 25, 0, 0);
|
||||
|
||||
CloseHandle(h);
|
||||
|
||||
|
||||
+191
-45
@@ -76,9 +76,6 @@ typedef struct _WIN32_FIND_DATAA {
|
||||
#define FILE_CURRENT 1
|
||||
#define FILE_END 2
|
||||
|
||||
// HeapAlloc flags
|
||||
#define HEAP_ZERO_MEMORY 0x00000008UL
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handle conversion helpers
|
||||
// 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 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
|
||||
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.)
|
||||
// Map drive letter to index: 'C' or 'c' -> '0'
|
||||
char drive = input[0];
|
||||
@@ -122,6 +142,13 @@ static void TranslatePath(const char* input, char* output) {
|
||||
// ---------------------------------------------------------------------------
|
||||
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) {
|
||||
return gLastError;
|
||||
}
|
||||
@@ -162,8 +189,6 @@ __declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
|
||||
(void)heap;
|
||||
if (!size) return NULL;
|
||||
void* ptr = SysAlloc(size);
|
||||
if (ptr && (flags & HEAP_ZERO_MEMORY))
|
||||
memset(ptr, 0, size);
|
||||
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
|
||||
return ptr;
|
||||
}
|
||||
@@ -395,7 +420,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
|
||||
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
|
||||
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
|
||||
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE) { 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));
|
||||
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
|
||||
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,
|
||||
DWORD* bytesRead, 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;
|
||||
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
DWORD* bytesWritten, 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;
|
||||
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &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.
|
||||
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
|
||||
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);
|
||||
uint64_t offset = 0;
|
||||
int64_t delta = distHigh
|
||||
? ((int64_t)(uint64_t)(uint32_t)*distHigh << 32) | (uint32_t)dist
|
||||
: (int64_t)dist;
|
||||
int64_t delta;
|
||||
if (distHigh) {
|
||||
uint64_t combined = ((uint64_t)(uint32_t)(*distHigh) << 32) | (uint32_t)dist;
|
||||
delta = (int64_t)combined;
|
||||
} else {
|
||||
delta = (int64_t)dist;
|
||||
}
|
||||
|
||||
int64_t target = 0;
|
||||
if (method == FILE_BEGIN) {
|
||||
offset = (uint64_t)delta;
|
||||
target = delta;
|
||||
} else if (method == FILE_CURRENT) {
|
||||
uint64_t cur = SysGetFilePos(kh);
|
||||
if (cur == (uint64_t)-1) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
offset = (uint64_t)((int64_t)cur + delta);
|
||||
if (cur == (uint64_t)-1 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
target = (int64_t)cur + delta;
|
||||
} else if (method == FILE_END) {
|
||||
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 {
|
||||
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);
|
||||
if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; }
|
||||
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.
|
||||
__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);
|
||||
uint64_t pos = SysGetFilePos(kh);
|
||||
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.
|
||||
__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));
|
||||
return TRUE;
|
||||
}
|
||||
@@ -607,9 +666,9 @@ __declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
|
||||
}
|
||||
|
||||
__declspec(dllexport) char* GetCommandLineA(void) {
|
||||
// Return a valid but empty-ish command line.
|
||||
static char cmdline[4] = "";
|
||||
return cmdline;
|
||||
SYS_PEB* peb = Win32Peb();
|
||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
|
||||
return (char*)peb->CommandLine;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -633,16 +692,51 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
|
||||
0 /* normal priority */, &tid);
|
||||
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
|
||||
if (lpThreadId) *lpThreadId = (DWORD)tid;
|
||||
return KH_TO_HANDLE(tid);
|
||||
return TID_TO_THREAD_HANDLE(tid);
|
||||
}
|
||||
|
||||
// WaitForSingleObject - blocks on process handles via SysWaitForProcess;
|
||||
// thread and other handle types are not yet waitable so return immediately.
|
||||
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
|
||||
(void)dwMilliseconds;
|
||||
uint32_t kh = HANDLE_TO_KH(hHandle);
|
||||
if (kh) SysWaitForProcess(kh); // no-op KSTATUS if not a process handle
|
||||
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);
|
||||
if (kh) {
|
||||
uint32_t s = SysWaitForProcess(kh);
|
||||
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -669,9 +763,9 @@ __declspec(dllexport) BOOL CreateProcessA(
|
||||
const char* lpCurrentDirectory,
|
||||
STARTUPINFOA* lpStartupInfo,
|
||||
PROCESS_INFORMATION* lpProcessInformation) {
|
||||
(void)lpCommandLine; (void)lpProcessAttributes; (void)lpThreadAttributes;
|
||||
(void)lpProcessAttributes; (void)lpThreadAttributes;
|
||||
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
|
||||
(void)lpCurrentDirectory; (void)lpStartupInfo;
|
||||
(void)lpStartupInfo;
|
||||
|
||||
// lpProcessInformation is mandatory
|
||||
if (!lpProcessInformation) {
|
||||
@@ -679,27 +773,56 @@ __declspec(dllexport) BOOL CreateProcessA(
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Standard Win32 behavior: use lpCommandLine if lpApplicationName is NULL
|
||||
char appPath[MAX_PATH];
|
||||
const char* targetPath = lpApplicationName;
|
||||
if (!targetPath) {
|
||||
targetPath = lpCommandLine;
|
||||
}
|
||||
|
||||
// If both are NULL, then it's truly an invalid parameter
|
||||
if (!targetPath) {
|
||||
if (!ExtractApplicationPathA(lpCommandLine, appPath, sizeof(appPath))) {
|
||||
gLastError = 87;
|
||||
return FALSE;
|
||||
}
|
||||
targetPath = appPath;
|
||||
}
|
||||
|
||||
// Build the cmdline: prefer lpCommandLine if given, otherwise fall back to path
|
||||
const char* cmdline = lpCommandLine ? lpCommandLine : targetPath;
|
||||
const char* cmdline = (lpCommandLine && lpCommandLine[0]) ? lpCommandLine : targetPath;
|
||||
|
||||
char translatedPath[MAX_PATH];
|
||||
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;
|
||||
// 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);
|
||||
|
||||
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) {
|
||||
gLastError = 2; // ERROR_FILE_NOT_FOUND
|
||||
return FALSE;
|
||||
@@ -715,7 +838,7 @@ __declspec(dllexport) BOOL CreateProcessA(
|
||||
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
|
||||
if (!lpExitCode) { gLastError = 87; return FALSE; }
|
||||
uint32_t kh = HANDLE_TO_KH(hProcess);
|
||||
if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
|
||||
if (!kh || IS_THREAD_HANDLE(hProcess)) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
|
||||
int code = 0;
|
||||
uint32_t s = SysGetProcessExitCode(kh, &code);
|
||||
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,
|
||||
const char* 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) {
|
||||
@@ -1091,7 +1214,7 @@ __declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
|
||||
const char* lpName) {
|
||||
(void)lpSemaphoreAttributes; (void)lInitialCount;
|
||||
(void)lMaximumCount; (void)lpName;
|
||||
return (HANDLE)(uintptr_t)0x53454D41UL; // 'SEMA' sentinel
|
||||
return (HANDLE)WIN32_SEMA_SENTINEL;
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
|
||||
@@ -1303,17 +1426,40 @@ __declspec(dllexport) unsigned int GetWindowsDirectoryA(char* lpBuffer, unsigned
|
||||
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,
|
||||
DWORD nSize) {
|
||||
(void)lpName; (void)lpBuffer; (void)nSize;
|
||||
const char* value = LookupStaticEnvA(lpName);
|
||||
if (!value) {
|
||||
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) {
|
||||
// Return a double-NUL-terminated empty environment block.
|
||||
static char emptyEnv[2] = { '\0', '\0' };
|
||||
return emptyEnv;
|
||||
// Double-NUL-terminated environment block.
|
||||
static char env[] = "HOME=.\0USERPROFILE=.\0HOMEDRIVE=C:\0HOMEPATH=\\\0\0";
|
||||
return env;
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# NT-Lite -- an x86-64 kernel
|
||||
|
||||
## The Monster From ~~Redmond~~ Kraków
|
||||
|
||||
Boots. Runs processes. Mimics Windows to some extent.
|
||||
|
||||
## what is it?
|
||||
@@ -17,7 +19,7 @@ The codebase is roughly 20k lines of C and assembly.
|
||||
- preemptive scheduler with priority levels
|
||||
- processes and threads
|
||||
- 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
|
||||
- PE import resolution and dynamic driver loading
|
||||
- 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.
|
||||
|
||||
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
|
||||
|
||||
@@ -84,9 +86,9 @@ The Win32 personality supports programs that expect drive letters, familiar proc
|
||||
## paths
|
||||
|
||||
```text
|
||||
"apps\shell.exe" -> current working directory
|
||||
"0:\apps\shell.exe" -> drive 0
|
||||
"2:\data\config.ini" -> drive 2
|
||||
"apps/shell.exe" -> current working directory
|
||||
"0:/apps/shell.exe" -> drive 0
|
||||
"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.
|
||||
@@ -290,6 +292,9 @@ A: Learning. Fun.
|
||||
Q: Does it run Doom?
|
||||
A: `doom.exe`
|
||||
|
||||
Q: How often does it crash?
|
||||
A: Yes.
|
||||
|
||||
Q: Is the Win32 layer real?
|
||||
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?
|
||||
A: No.
|
||||
|
||||
Will it corrupt my data?
|
||||
Q: Will it corrupt my data?
|
||||
A: It shouldn't.
|
||||
|
||||
Q: Why is the path syntax so weird?
|
||||
@@ -313,3 +318,5 @@ A: This document.
|
||||
|
||||
Q: Can I use this in production?
|
||||
A: Don't.
|
||||
|
||||
#### Projekt dedykowany dla szanownego Pana profesora Leszka Wójcika przez którego znienawidziłem Unixa.
|
||||
Reference in new issue
Block a user