diff options
Diffstat (limited to '05')
-rw-r--r-- | 05/constants.b | 2 | ||||
-rw-r--r-- | 05/main.c | 9 | ||||
-rw-r--r-- | 05/setjmp.h | 2 | ||||
-rw-r--r-- | 05/signal.h | 85 |
4 files changed, 96 insertions, 2 deletions
diff --git a/05/constants.b b/05/constants.b index 5027198..dc0d21e 100644 --- a/05/constants.b +++ b/05/constants.b @@ -5,7 +5,7 @@ ; read-only data 4MB addresses 0x800000-0xbfffff ; read-write data 4MB addresses 0xc00000-0xffffff ; note that file offsets and runtime addresses are the same. -; you should be able to change these constants (in a way that's consistent) without breaking anything: +; if you want to change these constants, make sure you update signal.h's _SIGNAL_HANDLERS! #define ENTRY_ADDR 0x200000 #define FUNCTIONS_ADDR 0x400000 #define FUNCTIONS_END 0x800000 @@ -1,9 +1,16 @@ #define _STDLIB_DEBUG #include <math.h> #include <stdio.h> +#include <signal.h> + +void test_signal_handler(int x) { + printf("interompu\n"); + _Exit(0); +} int main(int argc, char **argv) { - printf("%.16g\n", cosh(10)); + signal(SIGINT, test_signal_handler); + while (1){} return 0; } diff --git a/05/setjmp.h b/05/setjmp.h new file mode 100644 index 0000000..ad74d75 --- /dev/null +++ b/05/setjmp.h @@ -0,0 +1,2 @@ +// @NONSTANDARD +#error "longjmp is not supported." diff --git a/05/signal.h b/05/signal.h new file mode 100644 index 0000000..dc10e41 --- /dev/null +++ b/05/signal.h @@ -0,0 +1,85 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + + +#include <stdc_common.h> + +typedef long sig_atomic_t; // there are no "asynchronous interrupts" + +#define SIG_DFL 0 +#define SIG_IGN _sig_ign +#define SIG_ERR (-1) + +typedef void (*_Sighandler)(int); + +struct sigaction { + void (*handler)(int); + unsigned long flags; + void (*restorer)(void); + unsigned long mask; +}; + +unsigned char _signal_restorer[] = { + 0x48,0xb8,15,0,0,0,0,0,0,0, // mov rax, 15 (sigreturn) + 0x0f,0x05 // syscall +}; + +#define _SIGNAL_HANDLERS 0xfff000 +#define _LE64(x) (x)&0xff, ((x)>> 8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff, \ + ((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, (x)>>56 + +// we need to do this weird indirection because linux has a different +// calling convention from us. +unsigned char _signal_handler[] = { + // signal # passed in rdi + 0x48,0x89,0xf8, // mov rax, rdi (signal #) + 0x50, // push rax + 0x50, // push rax (allocate space for return value) + 0x48,0xb8,_LE64(_SIGNAL_HANDLERS), // mov rax, _SIGNAL_HANDLERS + 0x48,0x89,0xc3, // mov rbx, rax + 0x48,0x89,0xf8, // mov rax, rdi (signal #) + 0x48,0xc1,0xe0,0x03, // shl rax, 3 + 0x48,0x01,0xd8, // add rax, rbx + 0x48,0x89,0xc3, // mov rbx, rax + 0x48,0x8b,0x03, // mov rax, [rbx] + 0xff,0xd0, // call rax + 0x48,0x81,0xc4,16,0,0,0, // add rsp, 16 + 0xc3 // ret +}; + +#define _SA_RESTORER 0x04000000 + +int __sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { + return __syscall(13, signum, act, oldact, 8, 0, 0); +} + + +void _sig_ign(int signal) { + return; +} + +static unsigned long _sig_mask = 0; + +_Sighandler signal(int sig, _Sighandler func) { + if (func == SIG_DFL) { + // @TODO + return 0; + } + if (func == SIG_IGN) { + func = _sig_ign; + } + + void **handlers = _SIGNAL_HANDLERS; + handlers[sig] = func; + + _sig_mask |= 1ul << (sig-1); + struct sigaction act = {0}; + act.handler = _signal_handler; + act.mask = _sig_mask; + act.flags = _SA_RESTORER; + act.restorer = _signal_restorer; + __sigaction(sig, &act, NULL); + return 0;//@TODO +} + +#endif // _SIGNAL_H |