duct taped posix.dll (just like microsoft), new cursor, more info in the fault handlers, processes now have a parse-it-yourself cmdline, add SYS_WAIT_THREAD and SYS_GET_PROCESS_PID, writing still doesn't work

This commit is contained in:
maksw committed 2026-05-05 15:24:58 +02:00
1 parent a37f5b1581
commit b5c831f593
26 files changed
+1107 -56

No files matched your search

+98
View File
@@ -0,0 +1,98 @@
#pragma once
// posix.h - POSIX type and constant definitions for posix.dll
// Covers just what open/read/write/close/lseek/spawn/pthread/chdir need.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// ---------------------------------------------------------------------------
// errno
// ---------------------------------------------------------------------------
#ifdef errno
#undef errno
#endif
#ifdef EAGAIN
#undef EAGAIN
#endif
#ifdef ENOSYS
#undef ENOSYS
#endif
extern int errno; // defined in posix_fd.c; single global in v1
#define EPERM 1
#define ENOENT 2
#define ESRCH 3
#define EINTR 4
#define EIO 5
#define EBADF 9
#define ENOMEM 12
#define EACCES 13
#define EEXIST 17
#define ENODEV 19
#define ENOTDIR 20
#define EINVAL 22
#define EMFILE 24
#define ENOSPC 28
#define ERANGE 34
#define EAGAIN 35
#define ENOSYS 38
// ---------------------------------------------------------------------------
// open() flags
// ---------------------------------------------------------------------------
#define O_RDONLY 0x0000
#define O_WRONLY 0x0001
#define O_RDWR 0x0002
#define O_CREAT 0x0040
#define O_TRUNC 0x0200
#define O_APPEND 0x0400
// ---------------------------------------------------------------------------
// lseek() whence
// ---------------------------------------------------------------------------
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
// ---------------------------------------------------------------------------
// Scalar types
// ---------------------------------------------------------------------------
typedef int32_t pid_t;
typedef int32_t off_t;
typedef uint32_t mode_t;
typedef int64_t ssize_t;
// ---------------------------------------------------------------------------
// pthread types
// ---------------------------------------------------------------------------
typedef struct { uint32_t Tid; } pthread_t;
typedef struct { uint32_t Handle; } pthread_mutex_t;
typedef void* pthread_attr_t; // unused in v1
typedef void* pthread_mutexattr_t;
// ---------------------------------------------------------------------------
// posix_spawn types (file_actions and attrp both ignored in v1)
// ---------------------------------------------------------------------------
typedef void* posix_spawn_file_actions_t;
typedef void* posix_spawnattr_t;
// ---------------------------------------------------------------------------
// dirent (used by posix_dir.c)
// ---------------------------------------------------------------------------
#define NAME_MAX 259
typedef struct dirent {
char d_name[NAME_MAX + 1];
unsigned char d_type; // DT_REG or DT_DIR
} dirent;
#define DT_UNKNOWN 0
#define DT_REG 8
#define DT_DIR 4
typedef struct DIR {
uint32_t KHandle; // kernel DIR_OBJECT handle
dirent Current;
bool Valid;
} DIR;
+78
View File
@@ -0,0 +1,78 @@
// posix_dir.c - chdir, getcwd, opendir, readdir, closedir
#include <string.h>
#include "posix.h"
#include "posix_path.h"
extern uint32_t SysGetCurrentPid(void);
extern uint32_t SysSetCwd(const char* path);
extern uint32_t SysGetCwd(char* buf, size_t size);
extern uint32_t SysOpenDir(const char* path, uint32_t* handleOut);
extern uint32_t SysReadDir(uint32_t handle, void* entryOut);
extern void SysCloseHandle(uint32_t handle);
extern void* SysAlloc(size_t);
extern void SysFree(void*, size_t);
// KDIR_ENTRY layout mirrors io_file.h - duplicated here to avoid
// pulling kernel headers into posix.dll
#define KDIR_NAME_MAX 260
typedef struct {
char Name[KDIR_NAME_MAX];
uint8_t Attr;
uint32_t FileSize;
} KDIR_ENTRY;
#define KATTR_DIR 0x10
int chdir(const char* path) {
char native[512];
PosixTranslatePath(path, native, sizeof(native));
uint32_t s = SysSetCwd(native);
if (s != 0) { errno = ENOENT; return -1; }
return 0;
}
char* getcwd(char* buf, size_t size) {
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
char native[512];
uint32_t s = SysGetCwd(native, sizeof(native));
if (s != 0) { errno = ERANGE; return (char*)0; }
NativeToPosix(native, buf, size);
return buf;
}
DIR* opendir(const char* path) {
char native[512];
PosixTranslatePath(path, native, sizeof(native));
uint32_t kh;
uint32_t s = SysOpenDir(native, &kh);
if (s != 0) { errno = ENOENT; return (DIR*)0; }
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack.
DIR* d = (DIR*)SysAlloc(sizeof(DIR));
if (!d) { SysCloseHandle(kh); errno = ENOMEM; return (DIR*)0; }
d->KHandle = kh;
d->Valid = false;
return d;
}
struct dirent* readdir(DIR* d) {
if (!d) { errno = EBADF; return (struct dirent*)0; }
KDIR_ENTRY ke;
uint32_t s = SysReadDir(d->KHandle, &ke);
if (s != 0) return (struct dirent*)0; // end of directory or error
size_t len = strlen(ke.Name);
if (len > NAME_MAX) len = NAME_MAX;
memcpy(d->Current.d_name, ke.Name, len);
d->Current.d_name[len] = '\0';
d->Current.d_type = (ke.Attr & KATTR_DIR) ? DT_DIR : DT_REG;
d->Valid = true;
return &d->Current;
}
int closedir(DIR* d) {
if (!d) { errno = EBADF; return -1; }
SysCloseHandle(d->KHandle);
SysFree(d, sizeof(DIR));
return 0;
}
+55
View File
@@ -0,0 +1,55 @@
// posix_fd.c - file descriptor table
#include "posix.h"
#include "posix_fd.h"
// yeah
#ifdef errno
#undef errno
#endif
int errno = 0;
static FD_ENTRY gFdTable[FD_TABLE_PIDS][FD_MAX];
// Syscall stub declarations (from syscall.dll)
extern uint32_t SysGetCurrentPid(void);
void PosixFdInit(void) {
uint32_t pid = SysGetCurrentPid();
uint32_t slot = pid % FD_TABLE_PIDS;
// Clear this process's slice (handles inherited state from a previous
// process that happened to land on the same slot - unlikely but safe).
for (int i = 0; i < FD_MAX; i++)
gFdTable[slot][i] = (FD_ENTRY){ FD_FREE, 0, false };
gFdTable[slot][0] = (FD_ENTRY){ FD_CONSOLE_R, 0, true };
gFdTable[slot][1] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
gFdTable[slot][2] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
}
int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle) {
uint32_t slot = pid % FD_TABLE_PIDS;
for (int fd = 3; fd < FD_MAX; fd++) {
if (!gFdTable[slot][fd].Used) {
gFdTable[slot][fd] = (FD_ENTRY){ type, khandle, true };
return fd;
}
}
errno = EMFILE;
return -1;
}
FD_ENTRY* FdGet(uint32_t pid, int fd) {
if (fd < 0 || fd >= FD_MAX) { errno = EBADF; return (FD_ENTRY*)0; }
uint32_t slot = pid % FD_TABLE_PIDS;
FD_ENTRY* e = &gFdTable[slot][fd];
if (!e->Used) { errno = EBADF; return (FD_ENTRY*)0; }
return e;
}
void FdFree(uint32_t pid, int fd) {
if (fd < 0 || fd >= FD_MAX) return;
uint32_t slot = pid % FD_TABLE_PIDS;
gFdTable[slot][fd] = (FD_ENTRY){ FD_FREE, 0, false };
}
+34
View File
@@ -0,0 +1,34 @@
#pragma once
// posix_fd.h - file descriptor table (internal to posix.dll)
#include <stdint.h>
#include <stdbool.h>
typedef enum {
FD_FREE,
FD_FILE,
FD_CONSOLE_R, // fd 0 - reads from keyboard via SysKbReadChar
FD_CONSOLE_W, // fd 1, 2 - writes via SysWriteConsole
FD_DEVNULL, // /dev/null
FD_DEVZERO, // /dev/zero
} FD_TYPE;
typedef struct {
FD_TYPE Type;
uint32_t KHandle; // kernel handle; 0 for console/devnull/devzero
bool Used;
} FD_ENTRY;
#define FD_MAX 64
#define FD_TABLE_PIDS 32 // circular, indexed by pid % FD_TABLE_PIDS
// Called from _start before main(); sets up fds 0/1/2 for this process.
void PosixFdInit(void);
// Allocate the lowest free fd >= 3. Returns -1 and sets errno=EMFILE on failure.
int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle);
// Look up an fd. Returns NULL and sets errno=EBADF if invalid.
FD_ENTRY* FdGet(uint32_t pid, int fd);
// Release an fd slot (does NOT close the kernel handle).
void FdFree(uint32_t pid, int fd);
+158
View File
@@ -0,0 +1,158 @@
// posix_io.c - open, read, write, close, lseek
#include <stddef.h>
#include <string.h>
#include "posix.h"
#include "posix_fd.h"
#include "posix_path.h"
extern uint32_t SysGetCurrentPid(void);
extern uint32_t SysOpenFile (const char* path, uint32_t* handleOut);
extern uint32_t SysCreateFile (const char* path, uint32_t* handleOut);
extern uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
extern uint32_t SysReadFile (uint32_t handle, void* buf, size_t size, size_t* bytesRead);
extern uint32_t SysWriteFile (uint32_t handle, const void* buf, size_t size, size_t* written);
extern uint32_t SysSeekFile (uint32_t handle, uint64_t offset);
extern uint64_t SysGetFilePos (uint32_t handle);
extern uint64_t SysGetFileSize(uint32_t handle);
extern void SysCloseHandle(uint32_t handle);
extern void SysWriteConsole(const char* buf, size_t len);
extern char SysKbReadChar(void);
// KSTATUS -> errno
static int KsToErrno(uint32_t s) {
switch (s) {
case 0x0000: return 0; // STATUS_SUCCESS
case 0x0022: return ENOENT; // STATUS_NOT_FOUND
case 0x0021: return EACCES; // STATUS_ACCESS_DENIED
case 0x0020: return ENOMEM; // STATUS_NO_MEMORY
case 0x0023: return EEXIST; // STATUS_ALREADY_EXISTS
case 0x0010: return EINVAL; // STATUS_INVALID_PARAMETER
case 0x0012: return ERANGE; // STATUS_BUFFER_TOO_SMALL
case 0x0002: return ENOSYS; // STATUS_NOT_SUPPORTED
case 0x0031: return EAGAIN; // STATUS_QUEUE_FULL
default: return EIO;
}
}
int open(const char* path, int flags, ...) {
char native[512];
const char* p = PosixTranslatePath(path, native, sizeof(native));
uint32_t pid = SysGetCurrentPid();
FD_TYPE type = FD_FILE;
// Synthetic devices
if (strcmp(p, SYNTH_DEVNULL) == 0) {
int fd = FdAlloc(pid, FD_DEVNULL, 0);
return fd;
}
if (strcmp(p, SYNTH_DEVZERO) == 0) {
int fd = FdAlloc(pid, FD_DEVZERO, 0);
return fd;
}
uint32_t kh = 0;
uint32_t s;
if (flags & O_WRONLY) {
s = SysCreateFile(p, &kh);
} else {
s = SysOpenFile(p, &kh);
}
if (s != 0) { errno = KsToErrno(s); return -1; }
if ((flags & O_TRUNC) && (flags & O_WRONLY))
SysTruncateFile(kh, 0);
int fd = FdAlloc(pid, type, kh);
if (fd < 0) {
SysCloseHandle(kh); // FdAlloc already set errno
return -1;
}
return fd;
}
ssize_t read(int fd, void* buf, size_t count) {
if (count == 0) return 0;
uint32_t pid = SysGetCurrentPid();
FD_ENTRY* e = FdGet(pid, fd);
if (!e) return -1;
switch (e->Type) {
case FD_CONSOLE_R: {
// Read up to count chars from keyboard, blocking per char.
char* dst = (char*)buf;
for (size_t i = 0; i < count; i++) {
dst[i] = SysKbReadChar();
if (dst[i] == '\n' || dst[i] == '\r') return (ssize_t)(i + 1);
}
return (ssize_t)count;
}
case FD_DEVNULL:
return 0;
case FD_DEVZERO:
memset(buf, 0, count);
return (ssize_t)count;
case FD_FILE: {
size_t got = 0;
uint32_t s = SysReadFile(e->KHandle, buf, count, &got);
if (s != 0 && got == 0) { errno = KsToErrno(s); return -1; }
return (ssize_t)got;
}
default:
errno = EBADF;
return -1;
}
}
ssize_t write(int fd, const void* buf, size_t count) {
if (count == 0) return 0;
uint32_t pid = SysGetCurrentPid();
FD_ENTRY* e = FdGet(pid, fd);
if (!e) return -1;
switch (e->Type) {
case FD_CONSOLE_W:
SysWriteConsole((const char*)buf, count);
return (ssize_t)count;
case FD_DEVNULL:
case FD_DEVZERO:
return (ssize_t)count;
case FD_FILE: {
size_t written = 0;
uint32_t s = SysWriteFile(e->KHandle, buf, count, &written);
if (s != 0 && written == 0) { errno = KsToErrno(s); return -1; }
return (ssize_t)written;
}
default:
errno = EBADF;
return -1;
}
}
int close(int fd) {
uint32_t pid = SysGetCurrentPid();
FD_ENTRY* e = FdGet(pid, fd);
if (!e) return -1;
if (e->Type == FD_FILE)
SysCloseHandle(e->KHandle);
FdFree(pid, fd);
return 0;
}
off_t lseek(int fd, off_t offset, int whence) {
uint32_t pid = SysGetCurrentPid();
FD_ENTRY* e = FdGet(pid, fd);
if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; }
uint64_t target;
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;
}
uint32_t s = SysSeekFile(e->KHandle, target);
if (s != 0) { errno = KsToErrno(s); return -1; }
return (off_t)target;
}
+62
View File
@@ -0,0 +1,62 @@
// posix_path.c - POSIX <-> native path translation
#include <stddef.h>
#include <string.h>
#include "posix_path.h"
// The drive letter used as the POSIX filesystem root.
// A future SYS_GET_BOOT_DRIVE could populate this; hardcoded for v1.
static const char gRootDrive = 'C';
static void FlipSlashes(char* s) {
for (; *s; s++) if (*s == '/') *s = '\\';
}
const char* PosixTranslatePath(const char* in, char* out, size_t outSize) {
if (!in || !out || outSize < 4) return out;
// Synthetic devices
if (strcmp(in, "/dev/null") == 0) return SYNTH_DEVNULL;
if (strcmp(in, "/dev/zero") == 0) return SYNTH_DEVZERO;
// /proc/self/* - read-only synthesized files; flag with a sentinel so
// posix_io.c can intercept them without opening a real file.
// (In v1 only /proc/self/pid is handled; others fall through to ENOENT.)
if (in[0] == '/') {
// Absolute POSIX path -> C:\...
// outSize must fit "C:\" + rest
out[0] = gRootDrive;
out[1] = ':';
out[2] = '\\';
size_t rest = strlen(in + 1);
if (rest + 4 > outSize) rest = outSize - 4;
memcpy(out + 3, in + 1, rest);
out[3 + rest] = '\0';
} else {
// Relative - pass through with slash flip only
size_t len = strlen(in);
if (len + 1 > outSize) len = outSize - 1;
memcpy(out, in, len);
out[len] = '\0';
}
FlipSlashes(out);
return out;
}
void NativeToPosix(const char* in, char* out, size_t outSize) {
if (!in || !out || outSize == 0) return;
// Strip "X:\" prefix if present
const char* src = in;
if (src[0] != '\0' && src[1] == ':' && (src[2] == '\\' || src[2] == '/'))
src += 2; // skip "C:", leave the leading backslash
size_t len = strlen(src);
if (len + 1 > outSize) len = outSize - 1;
memcpy(out, src, len);
out[len] = '\0';
// Backslash -> forward slash
for (char* p = out; *p; p++) if (*p == '\\') *p = '/';
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
// posix_path.h - POSIX <-> native path translation
// SYNTHETIC sentinel returned from PosixTranslatePath for /dev/null and
// /dev/zero; the caller must handle these before passing to any syscall.
#define SYNTH_DEVNULL "\x01<devnull>"
#define SYNTH_DEVZERO "\x01<devzero>"
// Translate a POSIX path to a native kernel path.
// Returns a pointer to 'out' on success, or SYNTH_DEVNULL / SYNTH_DEVZERO
// for those synthetic devices. 'out' must be at least 512 bytes.
const char* PosixTranslatePath(const char* in, char* out, size_t outSize);
// Translate a native kernel path back to a POSIX-style path.
// Strips the drive letter prefix and flips backslashes.
void NativeToPosix(const char* in, char* out, size_t outSize);
+126
View File
@@ -0,0 +1,126 @@
// posix_proc.c - posix_spawn, waitpid, getpid
#include <stddef.h>
#include <string.h>
#include "posix.h"
#include "posix_path.h"
extern uint32_t SysGetCurrentPid(void);
extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut,
const char* cmdline);
extern uint32_t SysWaitForProcess(uint32_t handle);
extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
extern uint32_t SysGetProcessPid(uint32_t handle, uint32_t* pidOut);
extern void SysCloseHandle(uint32_t handle);
// pid -> proc handle table (user-mode side, for waitpid)
#define PROC_TABLE_SIZE 32
typedef struct {
pid_t pid;
uint32_t handle;
bool used;
} ProcEntry;
static ProcEntry gProcTable[PROC_TABLE_SIZE];
static void ProcTableInsert(pid_t pid, uint32_t handle) {
for (int i = 0; i < PROC_TABLE_SIZE; i++) {
if (!gProcTable[i].used) {
gProcTable[i] = (ProcEntry){ pid, handle, true };
return;
}
}
SysCloseHandle(handle);
}
static uint32_t ProcTableFind(pid_t pid) {
for (int i = 0; i < PROC_TABLE_SIZE; i++)
if (gProcTable[i].used && gProcTable[i].pid == pid)
return gProcTable[i].handle;
return 0;
}
static void ProcTableRemove(pid_t pid) {
for (int i = 0; i < PROC_TABLE_SIZE; i++)
if (gProcTable[i].used && gProcTable[i].pid == pid)
gProcTable[i].used = false;
}
// Build a flat Windows-style cmdline from a NULL-terminated argv array.
// Each token that contains a space is wrapped in double-quotes.
// argv[0] is included (it becomes the executable token visible to _start).
static void BuildCmdline(char* const argv[], char* out, size_t outSize) {
size_t pos = 0;
for (int i = 0; argv && argv[i]; i++) {
if (i > 0 && pos < outSize - 1) out[pos++] = ' ';
const char* tok = argv[i];
bool needQuote = false;
for (const char* c = tok; *c; c++) if (*c == ' ' || *c == '\t') { needQuote = true; break; }
if (needQuote && pos < outSize - 1) out[pos++] = '"';
for (; *tok && pos < outSize - 2; tok++) {
if (*tok == '"') { out[pos++] = '\\'; }
out[pos++] = *tok;
}
if (needQuote && pos < outSize - 1) out[pos++] = '"';
}
if (pos < outSize) out[pos] = '\0';
}
// ---------------------------------------------------------------------------
// POSIX API
// ---------------------------------------------------------------------------
pid_t getpid(void) {
return (pid_t)SysGetCurrentPid();
}
int posix_spawn(pid_t* pidOut,
const char* path,
const posix_spawn_file_actions_t* file_actions, // ignored v1
const posix_spawnattr_t* attrp, // ignored v1
char* const argv[],
char* const envp[]) // ignored v1
{
(void)file_actions; (void)attrp; (void)envp;
char native[512];
PosixTranslatePath(path, native, sizeof(native));
char cmdline[512];
BuildCmdline(argv, cmdline, sizeof(cmdline));
// If argv was NULL or empty, fall back to just the path so _start still
// gets a meaningful argv[0].
if (cmdline[0] == '\0') {
size_t len = strlen(path);
if (len >= sizeof(cmdline)) len = sizeof(cmdline) - 1;
memcpy(cmdline, path, len);
cmdline[len] = '\0';
}
uint32_t procHandle;
uint32_t s = SysCreateProcess(native, &procHandle, cmdline);
if (s != 0) { errno = ENOENT; return -1; }
uint32_t kpid = 0;
SysGetProcessPid(procHandle, &kpid);
ProcTableInsert((pid_t)kpid, procHandle);
if (pidOut) *pidOut = (pid_t)kpid;
return 0;
}
// options: WNOHANG not supported in v1 (always blocks).
pid_t waitpid(pid_t pid, int* status, int options) {
(void)options;
uint32_t handle = ProcTableFind(pid);
if (!handle) { errno = ESRCH; return -1; }
SysWaitForProcess(handle);
int exitCode = 0;
SysGetProcessExitCode(handle, &exitCode);
if (status) *status = exitCode << 8; // WEXITSTATUS convention
SysCloseHandle(handle);
ProcTableRemove(pid);
return pid;
}
+160
View File
@@ -0,0 +1,160 @@
// posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_*
#include <stddef.h>
#include "posix.h"
extern uint32_t SysGetCurrentPid(void);
extern uint32_t SysGetCurrentTid(void);
extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg,
uint32_t priority, uint32_t* tidOut);
extern uint32_t SysWaitThread(uint32_t tid);
extern void SysExitThread(int code);
extern void* SysAlloc(size_t size);
extern void SysFree(void* ptr, size_t size);
extern uint32_t SysMutexCreate(uint32_t* handleOut);
extern uint32_t SysMutexAcquire(uint32_t handle);
extern uint32_t SysMutexTryAcquire(uint32_t handle);
extern uint32_t SysMutexRelease(uint32_t handle);
extern void SysCloseHandle(uint32_t handle);
// Thread return-value table (PID-keyed like the fd table)
#define PTHREAD_TABLE_PIDS 32
#define PTHREAD_TABLE_SIZE 32
typedef struct {
uint32_t Tid;
void* RetVal;
bool Used;
} PTHREAD_SLOT;
static PTHREAD_SLOT gPthreadTable[PTHREAD_TABLE_PIDS][PTHREAD_TABLE_SIZE];
static PTHREAD_SLOT* PthreadSlotAlloc(uint32_t pid, uint32_t tid) {
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++) {
if (!table[i].Used) {
table[i] = (PTHREAD_SLOT){ tid, (void*)0, true };
return &table[i];
}
}
return (PTHREAD_SLOT*)0;
}
static PTHREAD_SLOT* PthreadSlotFind(uint32_t pid, uint32_t tid) {
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++)
if (table[i].Used && table[i].Tid == tid)
return &table[i];
return (PTHREAD_SLOT*)0;
}
// ---------------------------------------------------------------------------
// Trampoline - this is what actually runs as the new thread
// ---------------------------------------------------------------------------
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) {
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)raw;
void* (*fn)(void*) = tr->Fn;
void* arg = tr->Arg;
uint32_t pid = tr->Pid;
uint32_t tid = tr->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);
if (slot) slot->RetVal = retval;
SysExitThread(0);
}
// ---------------------------------------------------------------------------
// pthread API
// ---------------------------------------------------------------------------
int pthread_create(pthread_t* t, const pthread_attr_t* attr,
void* (*fn)(void*), void* arg) {
(void)attr;
uint32_t pid = SysGetCurrentPid();
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)SysAlloc(sizeof(PTHREAD_TRAMP));
if (!tr) return ENOMEM;
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,
(uint64_t)tr, 1 /*PriorityNormal*/, &tid);
if (s != 0) {
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;
return 0;
}
int pthread_join(pthread_t t, void** retval) {
uint32_t pid = SysGetCurrentPid();
uint32_t s = SysWaitThread(t.Tid);
// STATUS_NOT_FOUND (0x22) means already dead - treat as success
if (s != 0 && s != 0x0022) return ESRCH;
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
if (retval) *retval = slot ? slot->RetVal : (void*)0;
if (slot) slot->Used = false;
return 0;
}
void pthread_exit(void* retval) {
uint32_t pid = SysGetCurrentPid();
uint32_t tid = SysGetCurrentTid();
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
if (slot) slot->RetVal = retval;
SysExitThread(0);
while (1); // unreachable
}
int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) {
(void)attr;
uint32_t h;
uint32_t s = SysMutexCreate(&h);
if (s != 0) return ENOMEM;
m->Handle = h;
return 0;
}
int pthread_mutex_destroy(pthread_mutex_t* m) {
SysCloseHandle(m->Handle);
m->Handle = 0;
return 0;
}
int pthread_mutex_lock(pthread_mutex_t* m) {
uint32_t s = SysMutexAcquire(m->Handle);
return s == 0 ? 0 : EINVAL;
}
int pthread_mutex_trylock(pthread_mutex_t* m) {
uint32_t s = SysMutexTryAcquire(m->Handle);
return s == 0 ? 0 : EAGAIN;
}
int pthread_mutex_unlock(pthread_mutex_t* m) {
uint32_t s = SysMutexRelease(m->Handle);
return s == 0 ? 0 : EINVAL;
}