summaryrefslogtreecommitdiff
path: root/vere/pkg/noun/platform
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-10-05 21:56:51 +0700
committerpolwex <polwex@sortug.com>2025-10-05 21:56:51 +0700
commitfcedfddf00b3f994e4f4e40332ac7fc192c63244 (patch)
tree51d38e62c7bdfcc5f9a5e9435fe820c93cfc9a3d /vere/pkg/noun/platform
claude is gud
Diffstat (limited to 'vere/pkg/noun/platform')
-rw-r--r--vere/pkg/noun/platform/darwin/rsignal.h13
-rw-r--r--vere/pkg/noun/platform/linux/rsignal.h13
-rw-r--r--vere/pkg/noun/platform/windows/rsignal.c167
-rw-r--r--vere/pkg/noun/platform/windows/rsignal.h21
-rw-r--r--vere/pkg/noun/platform/windows/veh_handler.c26
-rw-r--r--vere/pkg/noun/platform/windows/veh_handler.h1
6 files changed, 241 insertions, 0 deletions
diff --git a/vere/pkg/noun/platform/darwin/rsignal.h b/vere/pkg/noun/platform/darwin/rsignal.h
new file mode 100644
index 0000000..a14670e
--- /dev/null
+++ b/vere/pkg/noun/platform/darwin/rsignal.h
@@ -0,0 +1,13 @@
+/// @file
+
+#ifndef NOUN_PLATFORM_DARWIN_RSIGNAL_H
+#define NOUN_PLATFORM_DARWIN_RSIGNAL_H
+
+#define rsignal_jmpbuf sigjmp_buf
+#define rsignal_setjmp(buf) sigsetjmp((buf), 1)
+#define rsignal_longjmp siglongjmp
+#define rsignal_install_handler signal
+#define rsignal_deinstall_handler(sig) signal((sig), SIG_IGN)
+#define rsignal_setitimer setitimer
+
+#endif /* ifndef NOUN_PLATFORM_DARWIN_RSIGNAL_H */
diff --git a/vere/pkg/noun/platform/linux/rsignal.h b/vere/pkg/noun/platform/linux/rsignal.h
new file mode 100644
index 0000000..9c55cee
--- /dev/null
+++ b/vere/pkg/noun/platform/linux/rsignal.h
@@ -0,0 +1,13 @@
+/// @file
+
+#ifndef NOUN_PLATFORM_LINUX_RSIGNAL_H
+#define NOUN_PLATFORM_LINUX_RSIGNAL_H
+
+#define rsignal_jmpbuf sigjmp_buf
+#define rsignal_setjmp(buf) sigsetjmp((buf), 1)
+#define rsignal_longjmp siglongjmp
+#define rsignal_install_handler signal
+#define rsignal_deinstall_handler(sig) signal((sig), SIG_IGN)
+#define rsignal_setitimer setitimer
+
+#endif /* ifndef NOUN_PLATFORM_LINUX_RSIGNAL_H */
diff --git a/vere/pkg/noun/platform/windows/rsignal.c b/vere/pkg/noun/platform/windows/rsignal.c
new file mode 100644
index 0000000..d927835
--- /dev/null
+++ b/vere/pkg/noun/platform/windows/rsignal.c
@@ -0,0 +1,167 @@
+#include "noun.h"
+#include "rsignal.h"
+#include <windows.h>
+
+int err_win_to_posix(DWORD winerr);
+
+// The current implementation of rsignal_ is single-threaded,
+// but it can be extended to multi-threaded by replacing these
+// static variables with a thread id-based hash map.
+//
+static __p_sig_fn_t _fns[SIG_COUNT];
+static volatile DWORD _tid;
+static HANDLE _hvt;
+
+void rsignal_install_handler(int sig, __p_sig_fn_t fn)
+{
+ if (sig < 0 || sig >= SIG_COUNT)
+ return;
+
+ DWORD newtid = GetCurrentThreadId();
+ DWORD oldtid = InterlockedExchange((volatile LONG*)&_tid, newtid);
+ if (oldtid != 0 && oldtid != newtid) {
+ fprintf(stderr, "\r\nrsignal_install_handler: %lu -> %lu\r\n", oldtid, newtid);
+ return;
+ }
+
+ __p_sig_fn_t oldfn = InterlockedExchangePointer((PVOID*)&_fns[sig], fn);
+ if (fn != 0 && oldfn != 0 && oldfn != fn) {
+ fprintf(stderr, "\r\nrsignal_install_handler: %p -> %p\r\n", oldfn, fn);
+ }
+}
+
+void rsignal_deinstall_handler(int sig)
+{
+ rsignal_install_handler(sig, 0);
+}
+
+void rsignal_raise(int sig)
+{
+ if (sig < 0 || sig >= SIG_COUNT)
+ return;
+
+ __p_sig_fn_t oldfn = InterlockedExchangePointer((PVOID*)&_fns[sig], 0);
+ if (oldfn == 0)
+ return;
+
+ if (_tid == GetCurrentThreadId()) {
+ oldfn(sig);
+ return;
+ }
+
+ HANDLE hthread = OpenThread(THREAD_ALL_ACCESS, FALSE, _tid);
+ if (!hthread) {
+ fprintf(stderr, "\r\nrsignal_raise: OpenThread(%lu): %lu\r\n", _tid, GetLastError());
+ return;
+ }
+
+ if (SuspendThread(hthread) < 0) {
+ fprintf(stderr, "\r\nrsignal_raise: SuspendThread(%lu): %lu\r\n", _tid, GetLastError());
+ goto cleanup;
+ }
+
+ oldfn(sig);
+
+ if (!ResumeThread(hthread)) {
+ fprintf(stderr, "\r\nrsignal_raise: ResumeThread(%lu): %lu\r\n", _tid, GetLastError());
+
+ // abort because the main thread is stuck
+ abort();
+ }
+
+cleanup:
+ CloseHandle(hthread);
+}
+
+static void _rsignal_vt_cb(PVOID param, BOOLEAN timedOut)
+{
+ rsignal_raise(SIGVTALRM);
+}
+
+int rsignal_setitimer(int type, struct itimerval *in, struct itimerval *out)
+{
+ if (in == 0) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (type != ITIMER_VIRTUAL || out != 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ if (_hvt != NULL) {
+ DeleteTimerQueueTimer(NULL, _hvt, NULL);
+ _hvt = NULL;
+ }
+
+ if (timerisset(&in->it_value) && !CreateTimerQueueTimer(&_hvt, NULL, _rsignal_vt_cb, NULL,
+ in->it_value.tv_sec * 1000 + in->it_value.tv_usec / 1000,
+ in->it_interval.tv_sec * 1000 + in->it_interval.tv_usec / 1000, 0))
+ {
+ errno = err_win_to_posix(GetLastError());
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+// direct import from ntdll.dll
+extern DWORD64 __imp_KiUserExceptionDispatcher;
+
+static void _rsignal_longjmp(intptr_t* builtin_jb)
+{
+ __builtin_longjmp((void**)builtin_jb, 1);
+}
+
+void rsignal_post_longjmp(DWORD tid, intptr_t* builtin_jb)
+{
+ HANDLE hthread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);
+ if (!hthread) {
+ fprintf(stderr, "\r\nrsignal: OpenThread(%lu): %lu\r\n", tid, GetLastError());
+ return;
+ }
+
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext(hthread, &context)) {
+ fprintf(stderr, "\r\nrsignal: GetThreadContext(%lu): %lu\r\n", tid, GetLastError());
+ goto cleanup;
+ }
+
+ // see if the thread is currently handling a structured exception
+ // if so, let the handler (usually the libsigsegv handler) finish
+ // and set up the the signal to run at the exception resume point
+ // otherwise, passing a parameter to fn is completely unreliable
+ //
+ DWORD64 kibase;
+ PRUNTIME_FUNCTION ki = RtlLookupFunctionEntry(__imp_KiUserExceptionDispatcher, &kibase, NULL);
+ CONTEXT c = context;
+ while (1)
+ {
+ DWORD64 base, frame;
+ PRUNTIME_FUNCTION f = RtlLookupFunctionEntry(c.Rip, &base, NULL);
+ if (!f) break;
+ if (f == ki)
+ {
+ // KiUserExceptionDispatcher has a "bare" frame
+ // with $rsp pointing to the CONTEXT structure
+ //
+ ((PCONTEXT)c.Rsp)->Rip = (DWORD64)_rsignal_longjmp;
+ ((PCONTEXT)c.Rsp)->Rcx = (DWORD64)builtin_jb;
+ goto cleanup;
+ }
+ PVOID handler_data;
+ RtlVirtualUnwind(0, base, c.Rip, f, &c, &handler_data, &frame, NULL);
+ }
+
+ context.Rip = (DWORD64)_rsignal_longjmp;
+ context.Rcx = (DWORD64)builtin_jb;
+ if (!SetThreadContext(hthread, &context)) {
+ fprintf(stderr, "\r\nrsignal: SetThreadContext(%lu): %lu\r\n", tid, GetLastError());
+ goto cleanup;
+ }
+
+cleanup:
+ CloseHandle(hthread);
+}
diff --git a/vere/pkg/noun/platform/windows/rsignal.h b/vere/pkg/noun/platform/windows/rsignal.h
new file mode 100644
index 0000000..998652c
--- /dev/null
+++ b/vere/pkg/noun/platform/windows/rsignal.h
@@ -0,0 +1,21 @@
+#include "windows.h"
+
+#ifndef _RSIGNAL_H
+#define _RSIGNAL_H
+
+#define rsignal_setjmp setjmp
+#define rsignal_longjmp longjmp
+
+void rsignal_raise(int sig);
+void rsignal_install_handler(int sig, __p_sig_fn_t fn);
+void rsignal_deinstall_handler(int sig);
+void rsignal_post_longjmp(unsigned long tid, intptr_t* builtin_jb);
+
+#define ITIMER_VIRTUAL 1
+struct itimerval {
+ struct timeval it_value, it_interval;
+};
+
+int rsignal_setitimer(int type, struct itimerval *in, struct itimerval *out);
+
+#endif//_RSIGNAL_H
diff --git a/vere/pkg/noun/platform/windows/veh_handler.c b/vere/pkg/noun/platform/windows/veh_handler.c
new file mode 100644
index 0000000..d2a035c
--- /dev/null
+++ b/vere/pkg/noun/platform/windows/veh_handler.c
@@ -0,0 +1,26 @@
+#include "noun.h"
+#include "rsignal.h"
+
+c3_i
+u3m_fault(void* adr_v, c3_i ser_i);
+
+/* _windows_exception_filter: replaces libsigsegv on windows
+ using vectored exception handling
+ */
+LONG WINAPI
+_windows_exception_filter(struct _EXCEPTION_POINTERS *ExceptionInfo)
+{
+ EXCEPTION_RECORD ExceptionRecord = *ExceptionInfo->ExceptionRecord;
+ if (ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
+ ExceptionRecord.ExceptionInformation[0] == 1 &&
+ u3m_fault((void*)ExceptionRecord.ExceptionInformation[1], 1))
+ {
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+
+ if (ExceptionRecord.ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
+ rsignal_raise(SIGSTK);
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
diff --git a/vere/pkg/noun/platform/windows/veh_handler.h b/vere/pkg/noun/platform/windows/veh_handler.h
new file mode 100644
index 0000000..dfa4b69
--- /dev/null
+++ b/vere/pkg/noun/platform/windows/veh_handler.h
@@ -0,0 +1 @@
+LONG WINAPI _windows_exception_filter(struct _EXCEPTION_POINTERS *ExceptionInfo);