Browse Source

[Linux PAL] Rewrite signal handling

Chia-Che Tsai 5 years ago
parent
commit
8aaeafc0a3

+ 0 - 1
Pal/src/host/Linux/Makefile.am

@@ -8,7 +8,6 @@ AR	= ar rcs
 LD	= ld
 
 CFLAGS	= -Wall -fPIC -O2 -std=gnu99 -fgnu89-inline -U_FORTIFY_SOURCE \
-	  -fno-omit-frame-pointer \
 	  -fno-stack-protector -fno-builtin
 ASFLAGS = -DPIC -DSHARED -fPIC -DASSEMBLER -Wa,--noexecstack \
 	  -x assembler-with-cpp

+ 78 - 145
Pal/src/host/Linux/db_exception.c

@@ -29,6 +29,7 @@
 #include "pal.h"
 #include "pal_internal.h"
 #include "pal_linux.h"
+#include "pal_debug.h"
 #include "pal_error.h"
 #include "pal_security.h"
 #include "api.h"
@@ -114,14 +115,8 @@ typedef struct {
     PAL_IDX         event_num;
     PAL_CONTEXT     context;
     ucontext_t *    uc;
-    PAL_PTR         eframe;
 } PAL_EVENT;
 
-#define save_return_point(ptr)                      \
-    asm volatile ("leaq 0(%%rip), %%rax\r\n"        \
-                  "movq %%rax, %0\r\n"              \
-                  : "=b"(ptr) :: "memory", "rax")
-
 static int get_event_num (int signum)
 {
     switch(signum) {
@@ -136,8 +131,7 @@ static int get_event_num (int signum)
 }
 
 void _DkGenericEventTrigger (PAL_IDX event_num, PAL_EVENT_HANDLER upcall,
-                             PAL_NUM arg, struct pal_frame * frame,
-                             ucontext_t * uc, void * eframe)
+                             PAL_NUM arg, ucontext_t * uc)
 {
     PAL_EVENT event;
     event.event_num = event_num;
@@ -145,31 +139,13 @@ void _DkGenericEventTrigger (PAL_IDX event_num, PAL_EVENT_HANDLER upcall,
     if (uc)
         memcpy(&event.context, uc->uc_mcontext.gregs, sizeof(PAL_CONTEXT));
 
-    if (frame) {
-        event.context.r15 = frame->arch.r15;
-        event.context.r14 = frame->arch.r14;
-        event.context.r13 = frame->arch.r13;
-        event.context.r12 = frame->arch.r12;
-        event.context.rdi = frame->arch.rdi;
-        event.context.rsi = frame->arch.rsi;
-        event.context.rbx = frame->arch.rbx;
-        /* find last frame */
-        event.context.rsp = frame->arch.rbp + sizeof(unsigned long) * 2;
-        event.context.rbp = ((unsigned long *) frame->arch.rbp)[0];
-        event.context.rip = ((unsigned long *) frame->arch.rbp)[1];
-        /* making rax = 0 to tell the caller that this PAL call failed */
-        event.context.rax = 0;
-    }
-
     event.uc = uc;
-    event.eframe = eframe;
 
     (*upcall) ((PAL_PTR) &event, arg, &event.context);
 }
 
 static bool _DkGenericSignalHandle (int event_num, siginfo_t * info,
-                                    struct pal_frame * frame,
-                                    ucontext_t * uc, void * eframe)
+                                    ucontext_t * uc)
 {
     PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(event_num);
 
@@ -181,133 +157,113 @@ static bool _DkGenericSignalHandle (int event_num, siginfo_t * info,
             event_num == PAL_EVENT_ILLEGAL)
             arg = (PAL_NUM) (info ? info->si_addr : 0);
 
-        _DkGenericEventTrigger(event_num, upcall, arg, frame, uc, eframe);
+        _DkGenericEventTrigger(event_num, upcall, arg, uc);
         return true;
     }
 
     return false;
 }
 
-#define ADDR_IN_PAL(addr) \
-        ((void *) (addr) > TEXT_START && (void *) (addr) < TEXT_END)
-
-/* This function walks the stack to find the PAL_FRAME
- * that was saved upon entry to the PAL, if an exception/interrupt
- * comes in during a PAL call.  This is needed to support the behavior that an
- * exception in the PAL has Unix-style, EAGAIN semantics.
- * 
- * The PAL_FRAME is supposed to be in the first PAL frame, and we look for 
- * it by matching a special magic number, that should only appear on the stack
- * once.
- * 
- * If an exception comes in while we are not in the PAL, this PAL_FRAME won't
- * exist, and it is ok to return NULL.
- */
-static struct pal_frame * get_frame (ucontext_t * uc)
-{
-    unsigned long rip = uc->uc_mcontext.gregs[REG_RIP];
-    unsigned long rbp = uc->uc_mcontext.gregs[REG_RBP];
-    unsigned long last_rbp = rbp - 64;
-
-    if (!ADDR_IN_PAL(rip))
-        return NULL;
-
-    while (ADDR_IN_PAL(((unsigned long *) rbp)[1])) {
-        last_rbp = rbp;
-        rbp = *(unsigned long *) rbp;
-    }
-
-    /* search frame record in the top frame of PAL */
-    for (unsigned long ptr = rbp - sizeof(unsigned long) ;
-         ptr > last_rbp ; ptr -= 8) {
-        struct pal_frame * frame = (struct pal_frame *) ptr;
-        if (frame->identifier == PAL_FRAME_IDENTIFIER)
-            return frame;
-    }
-
-    return NULL;
-}
-
-static void return_frame (struct pal_frame * frame, int err)
-{
-    if (err)
-        _DkRaiseFailure(err);
-
-    __clear_frame(frame);
-    arch_restore_frame(&frame->arch);
-
-    asm volatile ("xor %rax, %rax\r\n"
-                  "leaveq\r\n"
-                  "retq\r\n");
-}
-
-#if BLOCK_SIGFAULT == 1
-static char exception_msg[24] = "--- SIGSEGV --- [     ]\n";
-static volatile bool cont_exec = false;
-#endif
-
 static void _DkGenericSighandler (int signum, siginfo_t * info,
                                   struct ucontext * uc)
 {
-#if BLOCK_SIGFUALT == 1
-    /* reseurrect this code if signal handler if giving segmentation fault */
-    if (signum == SIGSEGV) {
-        int pid = INLINE_SYSCALL(getpid, 0);
-        exception_msg[17] = '0' + pid / 10000;
-        exception_msg[18] = '0' + (pid / 1000) % 10;
-        exception_msg[19] = '0' + (pid / 100) % 10;
-        exception_msg[20] = '0' + (pid / 10) % 10;
-        exception_msg[21] = '0' + pid % 10;
-        INLINE_SYSCALL(write, 3, 1, exception_msg, 24);
-        while(!cont_exec);
-    }
-#endif
-
-    struct pal_frame * frame = get_frame(uc);
-    void * eframe;
-
-    if (signum == SIGCONT && frame && frame->func == DkObjectsWaitAny)
+    int event_num = get_event_num(signum);
+    if (event_num == -1)
         return;
 
-    asm volatile ("movq %%rbp, %0" : "=r"(eframe));
+    uintptr_t rip = uc->uc_mcontext.gregs[REG_RIP];
+    if (ADDR_IN_PAL(rip)) {
+        // We expect none of the memory faults, illegal instructions, or arithmetic exceptions
+        // will happen in PAL. If these exceptions happen in PAL, exit the thread with loud warning.
+        int pid = INLINE_SYSCALL(getpid, 0);
+        int tid = INLINE_SYSCALL(gettid, 0);
+        const char * name = "exception";
+        switch(event_num) {
+            case PAL_EVENT_DIVZERO:  name = "div-by-zero exception"; break;
+            case PAL_EVENT_MEMFAULT: name = "memory fault"; break;
+            case PAL_EVENT_ILLEGAL:  name = "illegal instruction"; break;
+        }
 
-    if (frame && frame->func != &_DkGenericSighandler &&
-        signum != SIGCONT &&
-        signum != SIGINT  &&
-        signum != SIGTERM) {
-        return_frame(frame, PAL_ERROR_BADADDR);
+        printf("*** An unexpected %s occurred inside PAL. Exiting the thread. "
+               "(PID = %d, TID = %d, RIP = +%p) ***\n",
+               name, pid, tid, rip - (uintptr_t) TEXT_START);
+        _DkThreadExit();
         return;
     }
 
-    int event_num = get_event_num(signum);
-    if (event_num == -1)
-        return;
-
-    _DkGenericSignalHandle(event_num, info, frame, uc, eframe);
+    _DkGenericSignalHandle(event_num, info, uc);
 }
 
 static void _DkTerminateSighandler (int signum, siginfo_t * info,
                                     struct ucontext * uc)
 {
-    struct pal_frame * frame = get_frame(uc);
-    void * eframe;
-
-    asm volatile ("movq %%rbp, %0" : "=r"(eframe));
-
     int event_num = get_event_num(signum);
     if (event_num == -1)
         return;
 
-    if (!_DkGenericSignalHandle(event_num, NULL, frame, uc, eframe))
+    uintptr_t rip = uc->uc_mcontext.gregs[REG_RIP];
+
+    // If the signal arrives in the middle of a PAL call, add the event
+    // to pending in the current TCB.
+    if (ADDR_IN_PAL(rip)) {
+        PAL_TCB * tcb = get_tcb();
+        assert(tcb);
+        if (!tcb->pending_event) {
+            // Use the preserved pending event slot
+            tcb->pending_event = event_num;
+        } else {
+            // If there is already a pending event, add the new event to the queue.
+            // (a relatively rare case.)
+            struct event_queue * ev = malloc(sizeof(*ev));
+            if (!ev)
+                return;
+
+            INIT_LIST_HEAD(ev, list);
+            ev->event_num = event_num;
+            listp_add_tail(ev, &tcb->pending_queue, list);
+        }
+        return;
+    }
+
+    // Call the event handler. If there is no handler, terminate the thread
+    // unless it is a resuming event (then ignore the event).
+    if (!_DkGenericSignalHandle(event_num, NULL, uc) && event_num != PAL_EVENT_RESUME)
         _DkThreadExit();
 }
 
 static void _DkPipeSighandler (int signum, siginfo_t * info,
                                struct ucontext * uc)
 {
+    uintptr_t rip = uc->uc_mcontext.gregs[REG_RIP];
+    assert(ADDR_IN_PAL(rip)); // This signal can only happens inside PAL
     return;
 }
 
+/*
+ * __check_pending_event(): checks the existence of a pending event in the TCB
+ * and handles the event consequently.
+ */
+void __check_pending_event (void)
+{
+    PAL_TCB * tcb = get_tcb();
+    assert(tcb);
+    if (tcb->pending_event) {
+        int event = tcb->pending_event;
+        tcb->pending_event = 0;
+        _DkGenericSignalHandle(event, NULL, NULL);
+
+        if (!listp_empty(&tcb->pending_queue)) {
+            // If there are more than one pending events, process them from the queue
+            struct event_queue * ev, * n;
+            listp_for_each_entry_safe(ev, n, &tcb->pending_queue, list) {
+                listp_del(ev, &tcb->pending_queue, list);
+                _DkGenericSignalHandle(ev->event_num, NULL, NULL);
+                free(ev);
+            }
+        }
+    }
+}
+
 void _DkRaiseFailure (int error)
 {
     PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(PAL_EVENT_FAILURE);
@@ -318,7 +274,6 @@ void _DkRaiseFailure (int error)
     PAL_EVENT event;
     event.event_num = PAL_EVENT_FAILURE;
     event.uc = NULL;
-    event.eframe = NULL;
 
     (*upcall) ((PAL_PTR) &event, error, NULL);
 }
@@ -340,7 +295,7 @@ struct signal_ops on_signals[] = {
         [PAL_EVENT_SUSPEND]     = { .signum = { SIGINT, 0 },
                                     .handler = _DkTerminateSighandler },
         [PAL_EVENT_RESUME]      = { .signum = { SIGCONT, 0 },
-                                    .handler = _DkGenericSighandler },
+                                    .handler = _DkTerminateSighandler },
     };
 
 static int _DkPersistentSighandlerSetup (int event_num)
@@ -389,28 +344,6 @@ err:
 void _DkExceptionReturn (void * event)
 {
     PAL_EVENT * e = event;
-
-    if (e->eframe) {
-        struct pal_frame * frame = (struct pal_frame *) e->eframe;
-        int err = 0;
-
-        switch (e->event_num) {
-            case PAL_EVENT_MEMFAULT:
-                err = PAL_ERROR_BADADDR;
-                break;
-            case PAL_EVENT_QUIT:
-            case PAL_EVENT_SUSPEND:
-            case PAL_EVENT_RESUME:
-                err = PAL_ERROR_INTERRUPTED;
-                break;
-        }
-
-        if (err)
-            _DkRaiseFailure(err);
-
-        __clear_frame(frame);
-    }
-
     if (e->uc) {
         /* copy the context back to ucontext */
         memcpy(e->uc->uc_mcontext.gregs, &e->context, sizeof(PAL_CONTEXT));

+ 20 - 4
Pal/src/host/Linux/db_main.c

@@ -229,6 +229,26 @@ void pal_linux_main (void * args)
 
     init_slab_mgr(pagesz);
 
+    first_thread = malloc(HANDLE_SIZE(thread));
+    if (!first_thread)
+        init_fail(PAL_ERROR_NOMEM, "Out of memory");
+    SET_HANDLE_TYPE(first_thread, thread);
+    first_thread->thread.tid = INLINE_SYSCALL(gettid, 0);
+
+    void * alt_stack = NULL;
+    _DkVirtualMemoryAlloc(&alt_stack, ALT_STACK_SIZE, 0, PAL_PROT_READ|PAL_PROT_WRITE);
+    if (!alt_stack)
+        init_fail(PAL_ERROR_NOMEM, "Out of memory");
+
+    // Initialize TCB at the top of the alternative stack.
+    PAL_TCB * tcb  = alt_stack + ALT_STACK_SIZE - sizeof(PAL_TCB);
+    tcb->self      = tcb;
+    tcb->handle    = first_thread;
+    tcb->alt_stack = alt_stack; // Stack bottom
+    tcb->callback  = NULL;
+    tcb->param     = NULL;
+    pal_thread_init(tcb);
+
     setup_pal_map(&pal_map);
 
 #if USE_VDSO_GETTIME == 1
@@ -287,10 +307,6 @@ done_init:
         return;
     }
 
-    first_thread = malloc(HANDLE_SIZE(thread));
-    SET_HANDLE_TYPE(first_thread, thread);
-    first_thread->thread.tid = linux_state.pid;
-
     signal_setup();
 
     /* call to main function */

+ 3 - 2
Pal/src/host/Linux/db_misc.c

@@ -186,7 +186,7 @@ int _DkSegmentRegisterSet (int reg, const void * addr)
     if (reg == PAL_SEGMENT_FS) {
         ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_SET_FS, addr);
     } else if (reg == PAL_SEGMENT_GS) {
-        ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_SET_GS, addr);
+        return -PAL_ERROR_DENIED;
     } else {
         return -PAL_ERROR_INVAL;
     }
@@ -216,7 +216,8 @@ int _DkSegmentRegisterGet (int reg, void ** addr)
     if (reg == PAL_SEGMENT_FS) {
         ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_GET_FS, &ret_addr);
     } else if (reg == PAL_SEGMENT_GS) {
-        ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_GET_GS, &ret_addr);
+        // The GS segment is used for the internal TCB of PAL
+        return -PAL_ERROR_DENIED;
     } else {
         return -PAL_ERROR_INVAL;
     }

+ 10 - 0
Pal/src/host/Linux/db_mutex.c

@@ -197,6 +197,16 @@ void _DkMutexRelease (PAL_HANDLE handle)
     return;
 }
 
+int _DkInternalLock (PAL_LOCK* lock) {
+    while (_DkMutexLock(lock) < 0); // Retry the lock if being interrupted by signals
+    return 0;
+}
+
+int _DkInternalUnlock (PAL_LOCK* lock) {
+    _DkMutexUnlock(lock);
+    return 0;
+}
+
 static int mutex_wait (PAL_HANDLE handle, uint64_t timeout)
 {
     return _DkMutexAcquireTimeout(handle, timeout);

+ 72 - 22
Pal/src/host/Linux/db_threading.c

@@ -39,12 +39,46 @@
 #include <linux/types.h>
 #include <linux/wait.h>
 
-/* default size of a new thread stack */
-/* DEP 2/4/17: There is enough stuff allocated on the PAL stack now
- *   that we need two pages in Linux host mode.  In SGX mode,
- *   the enclave/non-clave split makes 1 page in/1 out sufficient.
+#if defined(__i386__)
+#include <asm/ldt.h>
+#else
+#include <asm/prctl.h>
+#endif
+
+/*
+ * pal_thread_init(): An initialization wrapper of a newly-created thread (including
+ * the first thread). This function accepts a TCB pointer to be set to the GS register
+ * of the thread. The rest of the TCB is used as the alternative stack for signal
+ * handling.
  */
-#define THREAD_STACK_SIZE   (pal_state.alloc_align * 2) 
+int pal_thread_init (void * tcbptr)
+{
+    PAL_TCB * tcb = tcbptr;
+    int ret;
+
+    ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_SET_GS, tcb);
+    if (IS_ERR(ret))
+        return -ERRNO(ret);
+
+    if (tcb->alt_stack) {
+        // Align stack to 16 bytes
+        void * alt_stack_top = (void *) ((uint64_t) tcb & ~16);
+        assert(alt_stack_top > tcb->alt_stack);
+        stack_t ss;
+        ss.ss_sp    = alt_stack_top;
+        ss.ss_flags = 0;
+        ss.ss_size  = alt_stack_top - tcb->alt_stack;
+
+        ret = INLINE_SYSCALL(sigaltstack, 2, &ss, NULL);
+        if (IS_ERR(ret))
+            return -ERRNO(ret);
+    }
+
+    if (tcb->callback)
+        return (*tcb->callback) (tcb->param);
+
+    return 0;
+}
 
 /* _DkThreadCreate for internal use. Create an internal thread
    inside the current process. The arguments callback and param
@@ -52,35 +86,51 @@
 int _DkThreadCreate (PAL_HANDLE * handle, int (*callback) (void *),
                      const void * param, int flags)
 {
-    void * child_stack = NULL;
+    void * stack = NULL;
+    int ret = _DkVirtualMemoryAlloc(&stack, THREAD_STACK_SIZE + ALT_STACK_SIZE,
+                                    0, PAL_PROT_READ|PAL_PROT_WRITE);
+    if (ret < 0)
+        return ret;
 
-    if (_DkVirtualMemoryAlloc(&child_stack, THREAD_STACK_SIZE, 0,
-                              PAL_PROT_READ|PAL_PROT_WRITE) < 0)
-        return -PAL_ERROR_NOMEM;
+    void * child_stack = stack + THREAD_STACK_SIZE;
 
-    /* move child_stack to the top of stack. */
-    child_stack += THREAD_STACK_SIZE;
+    PAL_HANDLE hdl = malloc(HANDLE_SIZE(thread));
+    if (!hdl) {
+        ret = -ENOMEM;
+        goto err;
+    }
+    SET_HANDLE_TYPE(hdl, thread);
+
+    // Initialize TCB at the top of the alternative stack.
+    PAL_TCB * tcb  = child_stack + ALT_STACK_SIZE - sizeof(PAL_TCB);
+    tcb->self      = tcb;
+    tcb->handle    = hdl;
+    tcb->alt_stack = child_stack; // Stack bottom
+    tcb->callback  = callback;
+    tcb->param     = (void *) param;
 
     /* align child_stack to 16 */
     child_stack = (void *) ((uintptr_t) child_stack & ~16);
 
-    int tid = 0;
-    int ret = clone(callback, child_stack,
+    ret = clone(pal_thread_init, child_stack,
                     CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SYSVSEM|
                     CLONE_THREAD|CLONE_SIGHAND|CLONE_PTRACE|
                     CLONE_PARENT_SETTID,
-                    param, &tid, NULL);
-    if (IS_ERR(ret))
-        return -PAL_ERROR_DENIED;
+                    (void *) tcb, &hdl->thread.tid, NULL);
 
-    PAL_HANDLE hdl = malloc(HANDLE_SIZE(thread));
-    SET_HANDLE_TYPE(hdl, thread);
-    hdl->thread.tid = tid;
-    *handle = hdl;
-
-    /* _DkThreadAdd(tid); */
+    if (IS_ERR(ret)) {
+        ret = -PAL_ERROR_DENIED;
+        goto err;
+    }
 
+    *handle = hdl;
     return 0;
+err:
+    if (stack)
+        _DkVirtualMemoryFree(stack, THREAD_STACK_SIZE + ALT_STACK_SIZE);
+    if (hdl)
+        free(hdl);
+    return ret;
 }
 
 int _DkThreadDelayExecution (unsigned long * duration)

+ 4 - 110
Pal/src/host/Linux/pal_host.h

@@ -53,9 +53,6 @@ typedef struct mutex_handle {
 #define LOCK_INIT MUTEX_HANDLE_INIT
 #define INIT_LOCK(lock) INIT_MUTEX_HANDLE(lock);
 
-#define _DkInternalLock _DkMutexLock
-#define _DkInternalUnlock _DkMutexUnlock
-
 /* Locking and unlocking of Mutexes */
 int _DkMutexLock (struct mutex_handle * mut);
 int _DkMutexLockTimeout (struct mutex_handle * mut, uint64_t timeout);
@@ -183,115 +180,12 @@ typedef struct pal_handle
 
 #define HANDLE_TYPE(handle)  ((handle)->hdr.type)
 
-struct arch_frame {
-#ifdef __x86_64__
-    uint64_t rsp, rbp, rbx, rsi, rdi, r12, r13, r14, r15;
-#else
-# error "unsupported architecture"
-#endif
-};
-
-#ifdef __x86_64__
-# define store_register(reg, var)     \
-    asm volatile ("movq %%" #reg ", %0" : "=a" (var) :: "memory");
-
-# define store_register_in_frame(reg, f)     store_register(reg, (f)->reg)
-
-# define arch_store_frame(f)                     \
-    store_register_in_frame(rsp, f)              \
-    store_register_in_frame(rbp, f)              \
-    store_register_in_frame(rbx, f)              \
-    store_register_in_frame(rsi, f)              \
-    store_register_in_frame(rdi, f)              \
-    store_register_in_frame(r12, f)              \
-    store_register_in_frame(r13, f)              \
-    store_register_in_frame(r14, f)              \
-    store_register_in_frame(r15, f)
-
-# define restore_register(reg, var, clobber...)  \
-    asm volatile ("movq %0, %%" #reg :: "g" (var) : "memory", ##clobber);
-
-# define restore_register_in_frame(reg, f)       \
-    restore_register(reg, (f)->reg,              \
-                     "r15", "r14", "r13", "r12", "rdi", "rsi", "rbx")
-
-# define arch_restore_frame(f)                   \
-    restore_register_in_frame(r15, f)            \
-    restore_register_in_frame(r14, f)            \
-    restore_register_in_frame(r13, f)            \
-    restore_register_in_frame(r12, f)            \
-    restore_register_in_frame(rdi, f)            \
-    restore_register_in_frame(rsi, f)            \
-    restore_register_in_frame(rbx, f)            \
-    restore_register_in_frame(rbp, f)            \
-    restore_register_in_frame(rsp, f)
-#else /* __x86_64__ */
-# error "unsupported architecture"
-#endif
-
-#define PAL_FRAME_IDENTIFIER    (0xdeaddeadbeefbeef)
-
-struct pal_frame {
-    volatile uint64_t           identifier;
-    void *                      func;
-    const char *                funcname;
-    struct arch_frame           arch;
-};
-
-/* When a PAL call is issued, a special PAL_FRAME is placed on the stack.
- * This stores both a magic identifier, debugging information, 
- * as well as callee-saved state.  This is used as a way to deal
- * with PAL-internal failures where the goal is to exit the PAL and return a
- * failure.
- * 
- * Arguably, an alternative is to unwind the stack and handle error cases at
- * each stage.  In general, this is probably more robust, but would take work
- * in the short term.  The one exception where the current strategy is
- * probably better is when the PAL gets in a state where the code is
- * unrecoverable, but ideally, this shouldn't happen.
- */
-
-/* DEP 12/25/17: This frame storage thing is important to mark volatile.
- * The compiler should not optimize out any of these changes, and 
- * because some accesses can happen during an exception, these are not
- * visible to the compiler in an otherwise stack-local variable (so the
- * compiler will try to optimize out these assignments.
- */
-static inline
-void __store_frame (volatile struct pal_frame * frame,
-                    void * func, const char * funcname)
-{
-    arch_store_frame(&frame->arch)
-    frame->func = func;
-    frame->funcname = funcname;
-    asm volatile ("nop" ::: "memory");
-    frame->identifier = PAL_FRAME_IDENTIFIER;
-}
-
-#define ENTER_PAL_CALL(name)                \
-    struct pal_frame frame;                 \
-    __store_frame(&frame, &(name), #name)
-
-
-static inline
-void __clear_frame (volatile struct pal_frame * frame)
-{
-    if (frame->identifier == PAL_FRAME_IDENTIFIER) {
-        asm volatile ("nop" ::: "memory");
-        frame->identifier = 0;
-    }
-}
+extern void __check_pending_event (void);
 
-#define LEAVE_PAL_CALL()                    \
-    do {                                    \
-        __clear_frame(&frame);              \
-    } while (0)
+#define LEAVE_PAL_CALL() do { __check_pending_event(); } while (0)
 
-#define LEAVE_PAL_CALL_RETURN(retval)       \
-    do {                                    \
-        __clear_frame(&frame);              \
-        return (retval);                    \
-    } while (0)
+#define LEAVE_PAL_CALL_RETURN(retval) \
+    do { __check_pending_event(); return (retval); } while (0)
 
 #if TRACE_HEAP_LEAK == 1
 

+ 32 - 0
Pal/src/host/Linux/pal_linux.h

@@ -25,6 +25,7 @@
 #include "pal.h"
 #include "pal_internal.h"
 #include "pal_linux_error.h"
+#include "list.h"
 
 #define PAL_LOADER RUNTIME_FILE("pal-Linux")
 
@@ -172,4 +173,35 @@ extern char __text_start, __text_end, __data_start, __data_end;
 #define DATA_START (void *) (&__text_start)
 #define DATA_END   (void *) (&__text_end)
 
+#define ADDR_IN_PAL(addr) \
+        ((void *) (addr) > TEXT_START && (void *) (addr) < TEXT_END)
+
+DEFINE_LIST(event_queue);
+struct event_queue {
+    LIST_TYPE(event_queue) list;
+    int event_num;
+};
+
+DEFINE_LISTP(event_queue);
+typedef struct pal_tcb {
+    struct pal_tcb *  self;
+    int               pending_event;
+    LISTP_TYPE(event_queue) pending_queue;
+    PAL_HANDLE        handle;
+    void *            alt_stack;
+    int               (*callback) (void *);
+    void *            param;
+} PAL_TCB;
+
+int pal_thread_init (void * tcbptr);
+
+static inline PAL_TCB * get_tcb (void)
+{
+    PAL_TCB * tcb;
+    asm ("movq %%gs:%c1,%q0"
+         : "=r" (tcb)
+         : "i" (offsetof(PAL_TCB, self)));
+    return tcb;
+}
+
 #endif /* PAL_LINUX_H */

+ 3 - 0
Pal/src/host/Linux/pal_linux_defs.h

@@ -7,6 +7,9 @@
 #define USER_ADDRESS_RESERVED   0x100000000
 #define USER_ADDRESS_LOWEST     0x10000
 
+#define THREAD_STACK_SIZE       (PRESET_PAGESIZE * 2)
+#define ALT_STACK_SIZE          (PRESET_PAGESIZE)
+
 /* internal wrap native pipe inside pipe streams */
 #define USE_PIPE_SYSCALL        0