mirror of
https://github.com/clearlinux/graphene.git
synced 2026-09-06 13:51:28 +00:00
[LibOS] use dedicated syscall stack and set signal frame on signal
For syscall emulation, use dedicated stack for it. If application stack is small(e.g. go goroutines), stack overflow happens and crash. Especially when pushing all the GP registers at the beginning of syscalldb. In order to avoid it, allocate dedicated stack area for syscall emulation and switch them back and forth. When LibOS emulates unix signal, - If app is interrupted, setup signal frame directly and return. - If Pal or LibOS is interrupted, it means app invoked system call and interrupt. Queue it and check if the signal is queue when LibOS returns to app and setup signal frame. - implemented sigreturn and sigaltstack So LibOS isn't re-entered by signal emulation because app signal handler isn't triggered while Pal or LibOS is interrupted.
This commit is contained in:
@@ -173,9 +173,12 @@ attribute_nofp void fpstate_reset(void);
|
||||
|
||||
/* definition for syscall table */
|
||||
void handle_signal (void);
|
||||
void handle_sysret_signal(void);
|
||||
void handle_exit_signal(void);
|
||||
int handle_next_signal(ucontext_t* user_uc);
|
||||
long convert_pal_errno (long err);
|
||||
void syscall_wrapper(void);
|
||||
void syscall_wrapper_after_syscalldb(void);
|
||||
void syscalldb_check_sigpending(void);
|
||||
|
||||
#define PAL_ERRNO convert_pal_errno(PAL_NATIVE_ERRNO)
|
||||
|
||||
@@ -210,13 +213,12 @@ static inline int64_t get_cur_preempt (void) {
|
||||
SHIM_ARG_TYPE ret = 0; \
|
||||
int64_t preempt = get_cur_preempt(); \
|
||||
__UNUSED(preempt); \
|
||||
/* handle_signal(); */ \
|
||||
/* check_stack_hook(); */ \
|
||||
BEGIN_SYSCALL_PROFILE();
|
||||
|
||||
#define END_SHIM(name) \
|
||||
END_SYSCALL_PROFILE(name); \
|
||||
handle_signal(); \
|
||||
handle_sysret_signal(); \
|
||||
assert(preempt == get_cur_preempt()); \
|
||||
return ret; \
|
||||
}
|
||||
@@ -494,8 +496,6 @@ static inline void __enable_preempt (shim_tcb_t * tcb)
|
||||
//debug("enable preempt: %d\n", preempt);
|
||||
}
|
||||
|
||||
void __handle_signal (shim_tcb_t * tcb, int sig);
|
||||
|
||||
static inline void enable_preempt (shim_tcb_t * tcb)
|
||||
{
|
||||
if (!tcb && !(tcb = shim_get_tcb()))
|
||||
@@ -505,9 +505,6 @@ static inline void enable_preempt (shim_tcb_t * tcb)
|
||||
if (!preempt)
|
||||
return;
|
||||
|
||||
if (preempt == 1)
|
||||
__handle_signal(tcb, 0);
|
||||
|
||||
__enable_preempt(tcb);
|
||||
}
|
||||
|
||||
@@ -771,9 +768,13 @@ static inline bool memory_migrated(void * mem)
|
||||
return mem >= migrated_memory_start && mem < migrated_memory_end;
|
||||
}
|
||||
|
||||
|
||||
extern void * __load_address, * __load_address_end;
|
||||
extern void * __code_address, * __code_address_end;
|
||||
extern void* __syscallas_return_begin;
|
||||
extern void* __syscallas_return_before_jmp;
|
||||
extern void* __syscallas_return_end;
|
||||
extern void* __syscalldb_check_sigpending_begin;
|
||||
extern void* __syscalldb_check_sigpending_end;
|
||||
|
||||
unsigned long parse_int (const char * str);
|
||||
|
||||
|
||||
@@ -93,9 +93,6 @@ __SIGSETFN (shim_sigdelset, ((__set->__val[__word] &= ~__mask), 0), )
|
||||
/* NB: Check shim_signal.c if this changes. Some memset(0) elision*/
|
||||
struct shim_signal {
|
||||
siginfo_t info;
|
||||
bool context_stored;
|
||||
ucontext_t context;
|
||||
PAL_CONTEXT * pal_context;
|
||||
};
|
||||
|
||||
#define MAX_SIGNAL_LOG 32
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
struct shim_regs {
|
||||
uint64_t orig_rax;
|
||||
uint64_t rsp;
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
@@ -26,12 +25,12 @@ struct shim_regs {
|
||||
uint64_t rbp;
|
||||
uint64_t rflags;
|
||||
uint64_t rip;
|
||||
uint64_t rsp;
|
||||
};
|
||||
|
||||
struct shim_context {
|
||||
struct shim_regs * regs;
|
||||
uint64_t fs_base;
|
||||
struct shim_context * next;
|
||||
uint64_t enter_time;
|
||||
struct atomic_int preempt;
|
||||
};
|
||||
@@ -47,6 +46,9 @@ struct shim_tcb {
|
||||
unsigned int tid;
|
||||
int pal_errno;
|
||||
struct debug_buf * debug_buf;
|
||||
#define SHIM_FLAG_SIGPENDING 0
|
||||
unsigned long flags;
|
||||
uint64_t tmp_rip;
|
||||
|
||||
/* syscall stack range */
|
||||
void* syscall_stack_low;
|
||||
|
||||
@@ -274,6 +274,17 @@ enum
|
||||
# define REG_CR2 REG_CR2
|
||||
};
|
||||
|
||||
union csgsfs {
|
||||
struct {
|
||||
uint16_t cs;
|
||||
uint16_t gs;
|
||||
uint16_t fs;
|
||||
uint16_t ss;
|
||||
|
||||
};
|
||||
uint64_t csgsfs;
|
||||
};
|
||||
|
||||
struct _libc_fpxreg {
|
||||
unsigned short int significand[4];
|
||||
unsigned short int exponent;
|
||||
@@ -360,14 +371,28 @@ typedef struct {
|
||||
|
||||
/* Userlevel context. */
|
||||
typedef struct ucontext {
|
||||
#define UC_FP_XSTATE 0x1
|
||||
#define UC_SIGCONTEXT_SS 0x2
|
||||
#define UC_STRICT_RESTORE_SS 0x4
|
||||
unsigned long int uc_flags;
|
||||
struct ucontext *uc_link;
|
||||
// stack_t::ss_flags
|
||||
#define SS_ONSTACK 1
|
||||
#define SS_DISABLE 2
|
||||
#define SS_AUTODISARM (1U << 31) /* disable sas during sighandling */
|
||||
stack_t uc_stack;
|
||||
mcontext_t uc_mcontext;
|
||||
__sigset_t uc_sigmask;
|
||||
struct _libc_fpstate __fpregs_mem;
|
||||
} ucontext_t;
|
||||
|
||||
struct sigframe {
|
||||
void * restorer;
|
||||
ucontext_t uc;
|
||||
siginfo_t info;
|
||||
/* fpstate follows */
|
||||
};
|
||||
|
||||
#define RED_ZONE_SIZE 128
|
||||
|
||||
/* bits/ustat.h */
|
||||
|
||||
@@ -34,11 +34,14 @@
|
||||
|
||||
#include <asm/signal.h>
|
||||
|
||||
static void __handle_signal(shim_tcb_t * tcb, int sig, PAL_CONTEXT* context);
|
||||
|
||||
// __rt_sighandler_t is different from __sighandler_t in <asm-generic/signal-defs.h>:
|
||||
// typedef void __signalfn_t(int);
|
||||
// typedef __signalfn_t *__sighandler_t
|
||||
|
||||
typedef void (*__rt_sighandler_t)(int, siginfo_t*, void*);
|
||||
typedef void (*restorer_t)(void);
|
||||
|
||||
static __rt_sighandler_t default_sighandler[NUM_SIGS];
|
||||
|
||||
@@ -65,6 +68,7 @@ allocate_signal_log (struct shim_thread * thread, int sig)
|
||||
head, tail, thread->has_signal.counter + 1);
|
||||
|
||||
atomic_inc(&thread->has_signal);
|
||||
set_bit(SHIM_FLAG_SIGPENDING, &thread->shim_tcb->flags);
|
||||
|
||||
return &log->logs[old_tail];
|
||||
}
|
||||
@@ -102,54 +106,12 @@ fetch_signal_log (struct shim_thread * thread, int sig)
|
||||
return signal;
|
||||
}
|
||||
|
||||
static void
|
||||
__handle_one_signal (shim_tcb_t * tcb, int sig, struct shim_signal * signal);
|
||||
|
||||
static void __store_info (siginfo_t * info, struct shim_signal * signal)
|
||||
{
|
||||
if (info)
|
||||
memcpy(&signal->info, info, sizeof(siginfo_t));
|
||||
}
|
||||
|
||||
void __store_context (shim_tcb_t * tcb, PAL_CONTEXT * pal_context,
|
||||
struct shim_signal * signal)
|
||||
{
|
||||
ucontext_t * context = &signal->context;
|
||||
|
||||
if (tcb && tcb->context.regs && tcb->context.regs->orig_rax) {
|
||||
struct shim_context * ct = &tcb->context;
|
||||
|
||||
if (ct->regs) {
|
||||
struct shim_regs * regs = ct->regs;
|
||||
context->uc_mcontext.gregs[REG_RIP] = regs->rip;
|
||||
context->uc_mcontext.gregs[REG_EFL] = regs->rflags;
|
||||
context->uc_mcontext.gregs[REG_R15] = regs->r15;
|
||||
context->uc_mcontext.gregs[REG_R14] = regs->r14;
|
||||
context->uc_mcontext.gregs[REG_R13] = regs->r13;
|
||||
context->uc_mcontext.gregs[REG_R12] = regs->r12;
|
||||
context->uc_mcontext.gregs[REG_R11] = regs->r11;
|
||||
context->uc_mcontext.gregs[REG_R10] = regs->r10;
|
||||
context->uc_mcontext.gregs[REG_R9] = regs->r9;
|
||||
context->uc_mcontext.gregs[REG_R8] = regs->r8;
|
||||
context->uc_mcontext.gregs[REG_RCX] = regs->rcx;
|
||||
context->uc_mcontext.gregs[REG_RDX] = regs->rdx;
|
||||
context->uc_mcontext.gregs[REG_RSI] = regs->rsi;
|
||||
context->uc_mcontext.gregs[REG_RDI] = regs->rdi;
|
||||
context->uc_mcontext.gregs[REG_RBX] = regs->rbx;
|
||||
context->uc_mcontext.gregs[REG_RBP] = regs->rbp;
|
||||
context->uc_mcontext.gregs[REG_RSP] = regs->rsp;
|
||||
}
|
||||
|
||||
signal->context_stored = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pal_context) {
|
||||
memcpy(context->uc_mcontext.gregs, pal_context, sizeof(PAL_CONTEXT));
|
||||
signal->context_stored = true;
|
||||
}
|
||||
}
|
||||
|
||||
void deliver_signal (siginfo_t * info, PAL_CONTEXT * context)
|
||||
{
|
||||
shim_tcb_t * tcb = shim_get_tcb();
|
||||
@@ -163,30 +125,26 @@ void deliver_signal (siginfo_t * info, PAL_CONTEXT * context)
|
||||
struct shim_thread * cur_thread = (struct shim_thread *) tcb->tp;
|
||||
int sig = info->si_signo;
|
||||
|
||||
struct shim_signal* signal = malloc(sizeof(*signal));
|
||||
if (!signal) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t preempt = __disable_preempt(tcb);
|
||||
|
||||
struct shim_signal * signal = __alloca(sizeof(struct shim_signal));
|
||||
/* save in signal */
|
||||
memset(signal, 0, sizeof(struct shim_signal));
|
||||
__store_info(info, signal);
|
||||
__store_context(tcb, context, signal);
|
||||
signal->pal_context = context;
|
||||
|
||||
if (preempt > 1 ||
|
||||
__sigismember(&cur_thread->signal_mask, sig)) {
|
||||
struct shim_signal ** signal_log = NULL;
|
||||
if ((signal = malloc_copy(signal,sizeof(struct shim_signal))) &&
|
||||
(signal_log = allocate_signal_log(cur_thread, sig))) {
|
||||
*signal_log = signal;
|
||||
}
|
||||
if (signal && !signal_log) {
|
||||
SYS_PRINTF("signal queue is full (TID = %u, SIG = %d)\n",
|
||||
tcb->tid, sig);
|
||||
free(signal);
|
||||
}
|
||||
struct shim_signal** signal_log = allocate_signal_log(cur_thread, sig);
|
||||
if (signal_log) {
|
||||
*signal_log = signal;
|
||||
} else {
|
||||
__handle_signal(tcb, sig);
|
||||
__handle_one_signal(tcb, sig, signal);
|
||||
SYS_PRINTF("signal queue is full (TID = %u, SIG = %d)\n",
|
||||
tcb->tid, sig);
|
||||
free(signal);
|
||||
}
|
||||
if (preempt <= 1) {
|
||||
__handle_signal(tcb, sig, context);
|
||||
}
|
||||
|
||||
__enable_preempt(tcb);
|
||||
@@ -208,11 +166,16 @@ void deliver_signal (siginfo_t * info, PAL_CONTEXT * context)
|
||||
#define IP eip
|
||||
#endif
|
||||
|
||||
static inline bool context_is_internal(PAL_CONTEXT * context)
|
||||
{
|
||||
static inline bool DkInPal(PAL_CONTEXT* context) {
|
||||
return context &&
|
||||
(void *) context->IP >= (void *) &__code_address &&
|
||||
(void *) context->IP < (void *) &__code_address_end;
|
||||
PAL_CB(pal_text.start) <= (void*)context->IP &&
|
||||
(void*)context->IP < PAL_CB(pal_text.end);
|
||||
}
|
||||
|
||||
static inline bool context_is_internal(PAL_CONTEXT* context) {
|
||||
return context &&
|
||||
(void *)&__code_address <= (void *)context->IP &&
|
||||
(void *)context->IP < (void *)&__code_address_end;
|
||||
}
|
||||
|
||||
static inline void internal_fault(const char* errstr,
|
||||
@@ -233,7 +196,8 @@ static inline void internal_fault(const char* errstr,
|
||||
|
||||
static void arithmetic_error_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
{
|
||||
if (is_internal_tid(get_cur_tid()) || context_is_internal(context)) {
|
||||
if (is_internal_tid(get_cur_tid()) || context_is_internal(context) ||
|
||||
DkInPal(context)) {
|
||||
internal_fault("Internal arithmetic fault", arg, context);
|
||||
} else {
|
||||
if (context)
|
||||
@@ -259,7 +223,8 @@ static void memfault_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
goto ret_exception;
|
||||
}
|
||||
|
||||
if (is_internal_tid(get_cur_tid()) || context_is_internal(context)) {
|
||||
if (is_internal_tid(get_cur_tid()) || context_is_internal(context) ||
|
||||
DkInPal(context)) {
|
||||
internal_fault("Internal memory fault", arg, context);
|
||||
goto ret_exception;
|
||||
}
|
||||
@@ -474,21 +439,13 @@ ret_fault:
|
||||
return has_fault;
|
||||
}
|
||||
|
||||
void __attribute__((weak)) syscall_wrapper(void)
|
||||
{
|
||||
/*
|
||||
* work around for link.
|
||||
* syscalldb.S is excluded for libsysdb_debug.so so it fails to link
|
||||
* due to missing syscall_wrapper.
|
||||
*/
|
||||
}
|
||||
|
||||
static void illegal_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
{
|
||||
struct shim_vma_val vma;
|
||||
|
||||
if (!is_internal_tid(get_cur_tid()) &&
|
||||
!context_is_internal(context) &&
|
||||
!DkInPal(context) &&
|
||||
!(lookup_vma((void *) arg, &vma)) &&
|
||||
!(vma.flags & VMA_INTERNAL)) {
|
||||
|
||||
@@ -541,12 +498,87 @@ static void illegal_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
DkExceptionReturn(event);
|
||||
}
|
||||
|
||||
/*
|
||||
* workaround for link syscalldb_debug.so
|
||||
* syscalldb.S is excluded from libsysdb_debug.so so it fails to link
|
||||
* due to missing symbols.
|
||||
*/
|
||||
void* __syscallas_return_begin __attribute__((weak)) = NULL;
|
||||
void* __syscallas_return_before_jmp __attribute__((weak)) = NULL;
|
||||
void* __syscallas_return_end __attribute__((weak)) = NULL;
|
||||
void* __syscalldb_check_sigpending_begin __attribute__((weak)) = NULL;
|
||||
void* __syscalldb_check_sigpending_end __attribute__((weak)) = NULL;
|
||||
|
||||
void __attribute__((weak)) syscall_wrapper(void) {
|
||||
}
|
||||
|
||||
void __attribute__((weak)) syscalldb_check_sigpending(void) {
|
||||
}
|
||||
|
||||
static void syscallas_return_emulate(PAL_CONTEXT* context) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* see syscallas.S
|
||||
* Emulate returning to app.
|
||||
* We've past the last check of signal pending, but still in LibOS.
|
||||
* Emulate last instructions returning to app so that it's in app.
|
||||
* Then we can handle async signal safely.
|
||||
*/
|
||||
void* rip = (void *)context->IP;
|
||||
if (rip == (void*)&__syscallas_return_before_jmp) {
|
||||
// emulate jmp *r11
|
||||
shim_tcb_t* tcb = shim_get_tcb();
|
||||
assert(tcb->context.regs == NULL);
|
||||
context->rip = tcb->tmp_rip;
|
||||
} else if ((void*)&__syscallas_return_begin <= rip &&
|
||||
rip <= (void*)&__syscallas_return_end) {
|
||||
// emulate __syscallas_return_begin to __syscallas_return_end
|
||||
shim_tcb_t* tcb = shim_get_tcb();
|
||||
assert(tcb);
|
||||
|
||||
struct shim_regs* regs = tcb->context.regs;
|
||||
assert(regs);
|
||||
tcb->context.regs = NULL;
|
||||
|
||||
context->r15 = regs->r15;
|
||||
context->r14 = regs->r14;
|
||||
context->r13 = regs->r13;
|
||||
context->r12 = regs->r12;
|
||||
context->r11 = regs->r11;
|
||||
context->r10 = regs->r10;
|
||||
context->r9 = regs->r9;
|
||||
context->r8 = regs->r8;
|
||||
context->rcx = regs->rcx;
|
||||
context->rdx = regs->rdx;
|
||||
context->rsi = regs->rsi;
|
||||
context->rdi = regs->rdi;
|
||||
context->rbx = regs->rbx;
|
||||
context->rbp = regs->rbp;
|
||||
context->efl = regs->rflags;
|
||||
context->rsp = regs->rsp;
|
||||
context->rip = regs->rip;
|
||||
} else if ((void*)&__syscalldb_check_sigpending_begin <= rip &&
|
||||
rip <= (void*)&__syscalldb_check_sigpending_end) {
|
||||
/*
|
||||
* emulate ret instruction.
|
||||
* As we'll deliver signal, sigpending check in
|
||||
* syscalldb_check_sigpending can be safely skipped.
|
||||
*/
|
||||
uint64_t* rsp = (uint64_t*)context->rsp;
|
||||
context->rip = *rsp;
|
||||
rsp++;
|
||||
context->rsp = (uint64_t)rsp;
|
||||
}
|
||||
}
|
||||
|
||||
static void quit_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
{
|
||||
__UNUSED(arg);
|
||||
__UNUSED(context);
|
||||
syscallas_return_emulate(context);
|
||||
if (!is_internal_tid(get_cur_tid())) {
|
||||
deliver_signal(ALLOC_SIGINFO(SIGTERM, SI_USER, si_pid, 0), NULL);
|
||||
deliver_signal(ALLOC_SIGINFO(SIGTERM, SI_USER, si_pid, 0), context);
|
||||
}
|
||||
DkExceptionReturn(event);
|
||||
}
|
||||
@@ -554,9 +586,9 @@ static void quit_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
static void suspend_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
{
|
||||
__UNUSED(arg);
|
||||
__UNUSED(context);
|
||||
syscallas_return_emulate(context);
|
||||
if (!is_internal_tid(get_cur_tid())) {
|
||||
deliver_signal(ALLOC_SIGINFO(SIGINT, SI_USER, si_pid, 0), NULL);
|
||||
deliver_signal(ALLOC_SIGINFO(SIGINT, SI_USER, si_pid, 0), context);
|
||||
}
|
||||
DkExceptionReturn(event);
|
||||
}
|
||||
@@ -564,15 +596,15 @@ static void suspend_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
static void resume_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
|
||||
{
|
||||
__UNUSED(arg);
|
||||
__UNUSED(context);
|
||||
shim_tcb_t * tcb = shim_get_tcb();
|
||||
if (!tcb || !tcb->tp)
|
||||
return;
|
||||
|
||||
syscallas_return_emulate(context);
|
||||
if (!is_internal_tid(get_cur_tid())) {
|
||||
int64_t preempt = __disable_preempt(tcb);
|
||||
if (preempt <= 1)
|
||||
__handle_signal(tcb, 0);
|
||||
__handle_signal(tcb, 0, context);
|
||||
__enable_preempt(tcb);
|
||||
}
|
||||
DkExceptionReturn(event);
|
||||
@@ -618,106 +650,204 @@ __sigset_t * set_sig_mask (struct shim_thread * thread,
|
||||
return &thread->signal_mask;
|
||||
}
|
||||
|
||||
static __rt_sighandler_t __get_sighandler(struct shim_thread* thread, int sig) {
|
||||
struct shim_signal_handle* sighdl = &thread->signal_handles[sig - 1];
|
||||
__rt_sighandler_t handler = NULL;
|
||||
static void __get_sighandler(struct shim_thread* thread, int sig,
|
||||
__rt_sighandler_t* handler, restorer_t* restorer) {
|
||||
*handler = NULL;
|
||||
*restorer = NULL;
|
||||
|
||||
struct shim_signal_handle* sighdl = &thread->signal_handles[sig - 1];
|
||||
if (sighdl->action) {
|
||||
struct __kernel_sigaction * act = sighdl->action;
|
||||
/*
|
||||
* on amd64, sa_handler can be treated as sa_sigaction
|
||||
* because 1-3 arguments are passed by register and
|
||||
* sa_handler simply ignores 2nd and 3rd argument.
|
||||
*/
|
||||
#ifdef __i386__
|
||||
# error "x86-32 support is heavily broken."
|
||||
#endif
|
||||
handler = (void*)act->k_sa_handler;
|
||||
struct __kernel_sigaction* act = sighdl->action;
|
||||
*handler = (void*)act->k_sa_handler;
|
||||
*restorer = act->sa_restorer;
|
||||
if (act->sa_flags & SA_RESETHAND) {
|
||||
sighdl->action = NULL;
|
||||
free(act);
|
||||
}
|
||||
}
|
||||
|
||||
if ((void*)handler == SIG_IGN)
|
||||
return NULL;
|
||||
|
||||
return handler ? : default_sighandler[sig - 1];
|
||||
if ((void*)*handler == SIG_IGN) {
|
||||
*handler = NULL;
|
||||
} else if (!*handler) {
|
||||
*handler = default_sighandler[sig - 1];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__handle_one_signal(shim_tcb_t* tcb, int sig, struct shim_signal* signal) {
|
||||
struct shim_thread* thread = (struct shim_thread*)tcb->tp;
|
||||
__rt_sighandler_t handler = NULL;
|
||||
static void get_sighandler(struct shim_thread * thread, int sig,
|
||||
__rt_sighandler_t* handler, restorer_t* restorer) {
|
||||
lock(&thread->lock);
|
||||
__get_sighandler(thread, sig, handler, restorer);
|
||||
unlock(&thread->lock);
|
||||
}
|
||||
|
||||
static unsigned int fpstate_size_get(const struct _libc_fpstate* fpstate) {
|
||||
if (fpstate == NULL)
|
||||
return 0;
|
||||
|
||||
const struct _fpx_sw_bytes* sw = &fpstate->sw_reserved;
|
||||
if (sw->magic1 == FP_XSTATE_MAGIC1 &&
|
||||
sw->xstate_size < sw->extended_size &&
|
||||
*((__typeof__(FP_XSTATE_MAGIC2)*)((void*)fpstate + sw->xstate_size)) ==
|
||||
FP_XSTATE_MAGIC2)
|
||||
return sw->extended_size;
|
||||
|
||||
return sizeof(struct swregs_state);
|
||||
}
|
||||
|
||||
static void direct_call_if_default_handler(
|
||||
int sig, siginfo_t* info, __rt_sighandler_t handler);
|
||||
|
||||
static void* __get_signal_stack(struct shim_thread* thread, void* current_stack) {
|
||||
const stack_t* ss = &thread->signal_altstack;
|
||||
if (ss->ss_flags & SS_DISABLE)
|
||||
return current_stack - RED_ZONE_SIZE;
|
||||
if (ss->ss_sp < current_stack && current_stack <= ss->ss_sp + ss->ss_size)
|
||||
return current_stack - RED_ZONE_SIZE;
|
||||
|
||||
return ss->ss_sp + ss->ss_size;
|
||||
}
|
||||
|
||||
static void* aligndown_sigframe(void* sp) {
|
||||
return ALIGN_DOWN_PTR(sp, 16UL) - 8;
|
||||
}
|
||||
|
||||
static void __setup_sig_frame(
|
||||
shim_tcb_t* tcb, int sig, struct shim_signal* signal, PAL_CONTEXT* context,
|
||||
__rt_sighandler_t handler, restorer_t restorer) {
|
||||
__UNUSED(tcb);
|
||||
direct_call_if_default_handler(sig, &signal->info, handler);
|
||||
|
||||
struct _libc_xregs_state* xregs_state =
|
||||
(struct _libc_xregs_state* )context->fpregs;
|
||||
struct _libc_fpstate* fpstate = &xregs_state->fpstate;
|
||||
unsigned int fpstate_size = fpstate_size_get(fpstate);
|
||||
|
||||
void* sp = __get_signal_stack(tcb->tp, (void*)context->rsp);
|
||||
fpregset_t user_fp = ALIGN_DOWN_PTR(sp - fpstate_size, 64UL);
|
||||
struct sigframe* user_sigframe =
|
||||
aligndown_sigframe((void*)user_fp - sizeof(struct sigframe));
|
||||
assert(&user_sigframe->uc == ALIGN_UP_PTR(&user_sigframe->uc, 16UL));
|
||||
user_sigframe->restorer = restorer;
|
||||
user_sigframe->uc.uc_flags = UC_SIGCONTEXT_SS | UC_STRICT_RESTORE_SS;
|
||||
user_sigframe->uc.uc_link = NULL;
|
||||
/* the layout of PAL_CONTEXT is same to gregs */
|
||||
memcpy(&user_sigframe->uc.uc_mcontext.gregs, context,
|
||||
sizeof(user_sigframe->uc.uc_mcontext.gregs));
|
||||
|
||||
stack_t* stack = &user_sigframe->uc.uc_stack;
|
||||
*stack = tcb->tp->signal_altstack;
|
||||
|
||||
memcpy(&user_sigframe->info, &signal->info, sizeof(signal->info));
|
||||
if (fpstate_size > 0) {
|
||||
user_sigframe->uc.uc_flags |= UC_FP_XSTATE;
|
||||
memcpy(user_fp, fpstate, fpstate_size);
|
||||
user_sigframe->uc.uc_mcontext.fpregs = user_fp;
|
||||
} else {
|
||||
user_sigframe->uc.uc_flags &= ~UC_FP_XSTATE;
|
||||
user_sigframe->uc.uc_mcontext.fpregs = NULL;
|
||||
}
|
||||
|
||||
context->rsp = (long)user_sigframe;
|
||||
context->rip = (long)handler;
|
||||
context->rdi = (long)signal->info.si_signo;
|
||||
context->rsi = (long)&user_sigframe->info;
|
||||
context->rdx = (long)&user_sigframe->uc;
|
||||
context->rax = 0;
|
||||
context->fpregs = NULL;
|
||||
|
||||
debug("deliver signal handler to user stack %p (%d, %p) sigframe: %p uc: %p fpstate %p\n",
|
||||
handler, sig, &signal->info,
|
||||
user_sigframe, &user_sigframe->uc,
|
||||
user_sigframe->uc.uc_mcontext.fpregs);
|
||||
}
|
||||
|
||||
static void __handle_signal(shim_tcb_t* tcb, int sig, PAL_CONTEXT* context) {
|
||||
/*
|
||||
* check if we're in LibOS or Pal before get_sighandler() which
|
||||
* acquires thread->lock. It may cause deadlock if we tries to lock
|
||||
* from host signal handler.
|
||||
*/
|
||||
if (context == NULL || context_is_internal(context) || DkInPal(context)) {
|
||||
/*
|
||||
* host signal handler is called during PAL or LibOS.
|
||||
* It means thread is in systeam call emulation. actual signal
|
||||
* delivery is done by deliver_signal_on_sysret()
|
||||
*/
|
||||
set_bit(SHIM_FLAG_SIGPENDING, &tcb->flags);
|
||||
return;
|
||||
}
|
||||
|
||||
struct shim_thread* thread = tcb->tp;
|
||||
assert(thread);
|
||||
if (!atomic_read(&thread->has_signal))
|
||||
return;
|
||||
|
||||
int begin_sig = 1;
|
||||
int end_sig = NUM_KNOWN_SIGS;
|
||||
if (sig) {
|
||||
begin_sig = sig;
|
||||
end_sig = sig + 1;
|
||||
}
|
||||
|
||||
struct shim_signal* signal = NULL;
|
||||
for (sig = begin_sig; sig < end_sig; sig++) {
|
||||
if (!__sigismember(&thread->signal_mask, sig) &&
|
||||
(signal = fetch_signal_log(thread, sig)))
|
||||
break;
|
||||
}
|
||||
if (!signal)
|
||||
return;
|
||||
|
||||
if (signal->info.si_signo == SIGCP) {
|
||||
join_checkpoint(thread, SI_CP_SESSION(&signal->info));
|
||||
return;
|
||||
} else {
|
||||
/*
|
||||
* host signal arrived while application is running.
|
||||
* setup signal frame on app stack and return back to app signal handler
|
||||
* thourgh host sigreturn.
|
||||
*/
|
||||
__rt_sighandler_t handler;
|
||||
restorer_t restorer;
|
||||
get_sighandler(thread, sig, &handler, &restorer);
|
||||
if (handler) {
|
||||
debug("%s handled\n", signal_name(sig));
|
||||
__setup_sig_frame(tcb, sig, signal, context, handler, restorer);
|
||||
}
|
||||
}
|
||||
|
||||
lock(&thread->lock);
|
||||
handler = __get_sighandler(thread, sig);
|
||||
unlock(&thread->lock);
|
||||
|
||||
if (!handler)
|
||||
return;
|
||||
|
||||
debug("%s handled\n", signal_name(sig));
|
||||
|
||||
// If the context is never stored in the signal, it means the signal is handled during
|
||||
// system calls, and before the thread is resumed.
|
||||
if (!signal->context_stored)
|
||||
__store_context(tcb, NULL, signal);
|
||||
|
||||
struct shim_context * context = NULL;
|
||||
|
||||
if (tcb->context.regs && tcb->context.regs->orig_rax) {
|
||||
context = __alloca(sizeof(struct shim_context));
|
||||
memcpy(context, &tcb->context, sizeof(struct shim_context));
|
||||
tcb->context.regs->orig_rax = 0;
|
||||
tcb->context.next = context;
|
||||
}
|
||||
|
||||
debug("run signal handler %p (%d, %p, %p)\n", handler, sig, &signal->info,
|
||||
&signal->context);
|
||||
|
||||
(*handler) (sig, &signal->info, &signal->context);
|
||||
|
||||
if (context)
|
||||
memcpy(&tcb->context, context, sizeof(struct shim_context));
|
||||
|
||||
if (signal->pal_context)
|
||||
memcpy(signal->pal_context, signal->context.uc_mcontext.gregs, sizeof(PAL_CONTEXT));
|
||||
free(signal);
|
||||
}
|
||||
|
||||
void __handle_signal (shim_tcb_t * tcb, int sig)
|
||||
{
|
||||
struct shim_thread * thread = tcb->tp;
|
||||
void handle_exit_signal(void) {
|
||||
struct shim_thread* thread = get_cur_thread();
|
||||
assert(thread);
|
||||
int begin_sig = 1, end_sig = NUM_KNOWN_SIGS;
|
||||
|
||||
if (sig)
|
||||
end_sig = (begin_sig = sig) + 1;
|
||||
|
||||
sig = begin_sig;
|
||||
|
||||
while (atomic_read(&thread->has_signal)) {
|
||||
struct shim_signal * signal = NULL;
|
||||
for (int sig = 1; sig < NUM_KNOWN_SIGS; sig++) {
|
||||
while (true) {
|
||||
struct shim_signal* signal = fetch_signal_log(thread, sig);
|
||||
if (!signal)
|
||||
break;
|
||||
|
||||
for ( ; sig < end_sig ; sig++)
|
||||
if (!__sigismember(&thread->signal_mask, sig) &&
|
||||
(signal = fetch_signal_log(thread, sig)))
|
||||
break;
|
||||
if (!__sigismember(&thread->signal_mask, sig)) {
|
||||
__rt_sighandler_t handler;
|
||||
restorer_t restorer;
|
||||
get_sighandler(thread, sig, &handler, &restorer);
|
||||
direct_call_if_default_handler(sig, &signal->info, handler);
|
||||
}
|
||||
free(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!signal)
|
||||
break;
|
||||
void handle_sysret_signal(void) {
|
||||
shim_tcb_t* tcb = shim_get_tcb();
|
||||
struct shim_thread* thread = (struct shim_thread*)tcb->tp;
|
||||
|
||||
if (!signal->context_stored)
|
||||
__store_context(tcb, NULL, signal);
|
||||
|
||||
__handle_one_signal(tcb, sig, signal);
|
||||
free(signal);
|
||||
DkThreadYieldExecution();
|
||||
clear_bit(SHIM_FLAG_SIGPENDING, &tcb->flags);
|
||||
/* This doesn't take user signal mask into account.
|
||||
peek_signal_log would be needed. not fetch_signal_log */
|
||||
if (atomic_read(&thread->has_signal)) {
|
||||
set_bit(SHIM_FLAG_SIGPENDING, &tcb->flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,15 +867,209 @@ void handle_signal (void)
|
||||
if (preempt > 1)
|
||||
debug("signal delayed (%ld)\n", preempt);
|
||||
else
|
||||
__handle_signal(tcb, 0);
|
||||
__handle_signal(tcb, 0, NULL);
|
||||
|
||||
__enable_preempt(tcb);
|
||||
debug("__enable_preempt: %s:%d\n", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
struct sig_deliver {
|
||||
int sig;
|
||||
struct shim_signal* signal;
|
||||
__rt_sighandler_t handler;
|
||||
restorer_t restorer;
|
||||
};
|
||||
|
||||
static bool __get_signal_to_deliver(struct sig_deliver* deliver) {
|
||||
deliver->signal = NULL;
|
||||
struct shim_thread* thread = get_cur_thread();
|
||||
|
||||
while (atomic_read(&thread->has_signal)) {
|
||||
struct shim_signal* signal = NULL;
|
||||
/* signul number starts from 1 */
|
||||
int sig;
|
||||
for (sig = 1 ; sig < NUM_KNOWN_SIGS ; sig++)
|
||||
if (!__sigismember(&thread->signal_mask, sig) &&
|
||||
(signal = fetch_signal_log(thread, sig)))
|
||||
break;
|
||||
|
||||
if (!signal)
|
||||
break;
|
||||
|
||||
__rt_sighandler_t handler;
|
||||
restorer_t restorer;
|
||||
get_sighandler(thread, sig, &handler, &restorer);
|
||||
if (!handler)
|
||||
continue;
|
||||
|
||||
deliver->sig = sig;
|
||||
deliver->signal = signal;
|
||||
deliver->handler = handler;
|
||||
deliver->restorer = restorer;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* sigreturn uses this.
|
||||
* If other signal are pending still, deliver it instead of return back
|
||||
* to app. The existing sigframe can be reused.
|
||||
*/
|
||||
int handle_next_signal(ucontext_t* user_uc) {
|
||||
struct sig_deliver deliver;
|
||||
if (!__get_signal_to_deliver(&deliver))
|
||||
return 0;
|
||||
|
||||
struct shim_regs* regs = shim_get_tcb()->context.regs;
|
||||
struct sigframe* user_sigframe = (struct sigframe*)(((void*)user_uc) - 8);
|
||||
|
||||
user_sigframe->restorer = deliver.restorer;
|
||||
regs->rsp = (uint64_t)user_sigframe;
|
||||
regs->rip = (uint64_t)deliver.handler;
|
||||
regs->rdi = (uint64_t)deliver.sig;
|
||||
regs->rsi = (uint64_t)&user_sigframe->info;
|
||||
regs->rdx = (uint64_t)&user_sigframe->uc;
|
||||
|
||||
// TODO signal mask
|
||||
|
||||
free(deliver.signal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 16-byte alignment on ucontext_t on signal frame
|
||||
* align struct shim_regs to 8 (mod 16) bytes
|
||||
* => align sigframe->us to 16 bytes
|
||||
*/
|
||||
static_assert(
|
||||
(((8 + sizeof(struct shim_regs)) + offsetof(struct sigframe, uc)) % 16) == 0,
|
||||
"signal stack frame isn't aligned to 16 byte on calling deliver_signal_on_sysret");
|
||||
|
||||
/*
|
||||
* host signal arrived while LibOS or PAL was running. So the emulated
|
||||
* signal was queued.
|
||||
* Now we're returning back to app.
|
||||
* setup signal frame and return from system call to signal handler.
|
||||
*/
|
||||
attribute_nofp uint64_t deliver_signal_on_sysret(uint64_t syscall_ret) {
|
||||
shim_tcb_t* tcb = shim_get_tcb();
|
||||
struct shim_regs* regs = tcb->context.regs;
|
||||
void* stack = (void*)regs->rsp;
|
||||
|
||||
struct sig_deliver deliver;
|
||||
debug("regs: %p sp: %08lx ip: %08lx stack: %p &tcb %p tcb %p\n",
|
||||
regs, regs->rsp, regs->rip, stack, &tcb, tcb);
|
||||
|
||||
clear_bit(SHIM_FLAG_SIGPENDING, &tcb->flags);
|
||||
/* FIXME: sigsuspend, sigwait, sigwaitinfo, pselect, ppoll are
|
||||
* broken because signal mask was changed when blocking and
|
||||
* is restored on returning from system call.
|
||||
* So we miss the signal which is masked in user space and
|
||||
* unmasked during blocking.
|
||||
*/
|
||||
if (!__get_signal_to_deliver(&deliver)) {
|
||||
debug("no deliverable signal\n");
|
||||
return syscall_ret;
|
||||
}
|
||||
|
||||
int sig = deliver.sig;
|
||||
struct shim_signal* signal = deliver.signal;
|
||||
__rt_sighandler_t handler = deliver.handler;
|
||||
restorer_t restorer = deliver.restorer;
|
||||
direct_call_if_default_handler(sig, &signal->info, handler);
|
||||
|
||||
void* sp = __get_signal_stack(tcb->tp, stack);
|
||||
/*
|
||||
* FIXME:
|
||||
* For now we can't distinguish how system call is invoked
|
||||
* i.e. through calling syscalldb or jumping into syscall_wrapper.
|
||||
* If syscall is invoked as
|
||||
* subq $RED_ZONE_SIZE, %rsp
|
||||
* callq syscalldb
|
||||
* addq $RED_ZONE_SIZE, %rsp
|
||||
* red zone is avoided twice unnecessarily for signal handler.
|
||||
*/
|
||||
|
||||
/* allocate signal frame */
|
||||
sp -= sizeof(struct sigframe) + FP_XSTATE_MAGIC2_SIZE + 64;
|
||||
sp -= fpu_xstate_size;
|
||||
sp = aligndown_sigframe(sp);
|
||||
stack = sp;
|
||||
struct sigframe* user_sigframe = stack;
|
||||
|
||||
assert(&user_sigframe->uc == ALIGN_UP_PTR(&user_sigframe->uc, 16UL));
|
||||
stack += sizeof(*user_sigframe);
|
||||
stack = ALIGN_UP_PTR(stack, 64UL);
|
||||
struct _libc_fpstate* user_fpstate = stack;
|
||||
|
||||
debug("regs: %p sigframe: %p uc: %p fpstate: %p\n",
|
||||
regs, user_sigframe, &user_sigframe->uc, user_fpstate);
|
||||
|
||||
/* setup sigframe */
|
||||
user_sigframe->restorer = restorer;
|
||||
|
||||
ucontext_t* user_uc = &user_sigframe->uc;
|
||||
user_uc->uc_flags = UC_FP_XSTATE;
|
||||
user_uc->uc_link = NULL;
|
||||
user_uc->uc_stack = tcb->tp->signal_altstack;
|
||||
|
||||
gregset_t * gregs = &user_uc->uc_mcontext.gregs;
|
||||
(*gregs)[REG_R8] = regs->r8;
|
||||
(*gregs)[REG_R9] = regs->r9;
|
||||
(*gregs)[REG_R10] = regs->r10;
|
||||
(*gregs)[REG_R11] = regs->r11;
|
||||
(*gregs)[REG_R12] = regs->r12;
|
||||
(*gregs)[REG_R13] = regs->r13;
|
||||
(*gregs)[REG_R14] = regs->r14;
|
||||
(*gregs)[REG_R15] = regs->r15;
|
||||
(*gregs)[REG_RDI] = regs->rdi;
|
||||
(*gregs)[REG_RSI] = regs->rsi;
|
||||
(*gregs)[REG_RBP] = regs->rbp;
|
||||
(*gregs)[REG_RBX] = regs->rbx;
|
||||
(*gregs)[REG_RDX] = regs->rdx;
|
||||
(*gregs)[REG_RAX] = syscall_ret;
|
||||
(*gregs)[REG_RCX] = regs->rcx;
|
||||
(*gregs)[REG_RSP] = regs->rsp;
|
||||
(*gregs)[REG_RIP] = regs->rip;
|
||||
(*gregs)[REG_EFL] = regs->rflags;
|
||||
union csgsfs sr = {
|
||||
.cs = 0x33, // __USER_CS(5) | 0(GDT) | 3(RPL)
|
||||
.fs = 0,
|
||||
.gs = 0,
|
||||
.ss = 0x2b, // __USER_DS(6) | 0(GDT) | 3(RPL)
|
||||
};
|
||||
(*gregs)[REG_CSGSFS] = sr.csgsfs;
|
||||
|
||||
(*gregs)[REG_ERR] = signal->info.si_errno;
|
||||
(*gregs)[REG_TRAPNO] = signal->info.si_code;
|
||||
(*gregs)[REG_OLDMASK] = 0;
|
||||
(*gregs)[REG_CR2] = (long)signal->info.si_addr;
|
||||
|
||||
user_uc->uc_mcontext.fpregs = user_fpstate;
|
||||
memset(user_fpstate, 0, fpu_xstate_size);
|
||||
fpstate_save(user_fpstate);
|
||||
fpstate_reset();
|
||||
|
||||
// TODO: get current sigmask and mask signal
|
||||
__sigemptyset(&user_uc->uc_sigmask);
|
||||
|
||||
free(signal);
|
||||
|
||||
// setup to return to signal handler
|
||||
regs->rsp = (uint64_t)user_sigframe;
|
||||
regs->rip = (unsigned long)handler;
|
||||
regs->rdi = (unsigned long)sig;
|
||||
regs->rsi = (unsigned long)&user_sigframe->info;
|
||||
regs->rdx = (unsigned long)&user_sigframe->uc;
|
||||
return /*rax=*/0;
|
||||
}
|
||||
|
||||
// Need to hold thread->lock when calling this function
|
||||
void append_signal(struct shim_thread* thread, int sig, siginfo_t* info, bool need_interrupt) {
|
||||
__rt_sighandler_t handler = __get_sighandler(thread, sig);
|
||||
__rt_sighandler_t handler;
|
||||
restorer_t restorer;
|
||||
__get_sighandler(thread, sig, &handler, &restorer);
|
||||
|
||||
if (!handler) {
|
||||
// SIGSTOP and SIGKILL cannot be ignored
|
||||
@@ -774,7 +1098,6 @@ void append_signal(struct shim_thread* thread, int sig, siginfo_t* info, bool ne
|
||||
/* save in signal */
|
||||
if (info) {
|
||||
__store_info(info, signal);
|
||||
signal->context_stored = false;
|
||||
} else {
|
||||
memset(signal, 0, sizeof(struct shim_signal));
|
||||
}
|
||||
@@ -840,6 +1163,17 @@ static void sighandler_core (int sig, siginfo_t * info, void * ucontext)
|
||||
sighandler_kill(sig, info, ucontext);
|
||||
}
|
||||
|
||||
static void direct_call_if_default_handler(
|
||||
int sig, siginfo_t* info, __rt_sighandler_t handler) {
|
||||
/* we know sighandler_kill only kill the thread
|
||||
* without using info and context */
|
||||
if (handler == &sighandler_kill || handler == &sighandler_core) {
|
||||
debug("direct calling sighandler_kill\n");
|
||||
// this thread exits.
|
||||
handler(sig, info, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static __rt_sighandler_t default_sighandler[NUM_SIGS] = {
|
||||
/* SIGHUP */ &sighandler_kill,
|
||||
/* SIGINT */ &sighandler_kill,
|
||||
|
||||
@@ -813,10 +813,15 @@ BEGIN_CP_FUNC(running_thread)
|
||||
new_thread->shim_tcb = (void *)(base + toff);
|
||||
struct shim_tcb* new_tcb = new_thread->shim_tcb;
|
||||
memcpy(new_tcb, thread->shim_tcb, sizeof(*new_tcb));
|
||||
struct shim_regs* regs = thread->shim_tcb->context.regs;
|
||||
if (regs) {
|
||||
ptr_t roff = ADD_CP_OFFSET(sizeof(*regs));
|
||||
new_tcb->context.regs = (struct shim_regs*)(base + roff);
|
||||
memcpy(new_tcb->context.regs, regs, sizeof(*regs));
|
||||
}
|
||||
/* don't export stale pointers */
|
||||
new_tcb->self = NULL;
|
||||
new_tcb->tp = NULL;
|
||||
new_tcb->context.next = NULL;
|
||||
new_tcb->debug_buf = NULL;
|
||||
}
|
||||
}
|
||||
@@ -861,8 +866,12 @@ BEGIN_RS_FUNC(running_thread)
|
||||
|
||||
thread->vmid = cur_process.vmid;
|
||||
|
||||
if (thread->shim_tcb)
|
||||
if (thread->shim_tcb) {
|
||||
CP_REBASE(thread->shim_tcb);
|
||||
if (thread->shim_tcb->context.regs) {
|
||||
CP_REBASE(thread->shim_tcb->context.regs);
|
||||
}
|
||||
}
|
||||
|
||||
if (thread->set_child_tid) {
|
||||
/* CLONE_CHILD_SETTID */
|
||||
|
||||
@@ -1392,6 +1392,7 @@ static ElfW(Addr)* __vdso_shim_clock_gettime __attribute_migratable = NULL;
|
||||
static ElfW(Addr)* __vdso_shim_gettimeofday __attribute_migratable = NULL;
|
||||
static ElfW(Addr)* __vdso_shim_time __attribute_migratable = NULL;
|
||||
static ElfW(Addr)* __vdso_shim_getcpu __attribute_migratable = NULL;
|
||||
static ElfW(Addr)* __vdso_shim_check_sigpending __attribute_migratable = NULL;
|
||||
|
||||
static const struct {
|
||||
const char* name;
|
||||
@@ -1416,7 +1417,13 @@ static const struct {
|
||||
.name = "__vdso_shim_getcpu",
|
||||
.value = (ElfW(Addr))&__shim_getcpu,
|
||||
.func = &__vdso_shim_getcpu,
|
||||
}};
|
||||
},
|
||||
{
|
||||
.name = "__vdso_shim_check_sigpending",
|
||||
.value = (ElfW(Addr))&syscalldb_check_sigpending,
|
||||
.func = &__vdso_shim_check_sigpending,
|
||||
}
|
||||
};
|
||||
|
||||
static int vdso_map_init(void) {
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <generated-offsets-build.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <shim_internal.h>
|
||||
#include <shim_tcb.h>
|
||||
@@ -9,14 +11,18 @@ void dummy(void)
|
||||
{
|
||||
OFFSET_T(SHIM_TCB_OFFSET, PAL_TCB, libos_tcb);
|
||||
OFFSET_T(TCB_REGS, shim_tcb_t, context.regs);
|
||||
OFFSET_T(SHIM_TCB_FLAGS, shim_tcb_t, flags);
|
||||
OFFSET_T(SHIM_TCB_TMP_RIP, shim_tcb_t, tmp_rip);
|
||||
OFFSET_T(SHIM_TCB_SYSCALL_STACK_LOW, shim_tcb_t, syscall_stack_low);
|
||||
OFFSET_T(SHIM_TCB_SYSCALL_STACK_HIGH, shim_tcb_t, syscall_stack_high);
|
||||
OFFSET(SHIM_REGS_RSP, shim_regs, rsp);
|
||||
OFFSET(SHIM_REGS_R15, shim_regs, r15);
|
||||
OFFSET(SHIM_REGS_R11, shim_regs, r11);
|
||||
OFFSET(SHIM_REGS_RIP, shim_regs, rip);
|
||||
OFFSET(SHIM_REGS_RSP, shim_regs, rsp);
|
||||
DEFINE(SHIM_REGS_SIZE, sizeof(struct shim_regs));
|
||||
|
||||
/* definitions */
|
||||
DEFINE(RED_ZONE_SIZE, RED_ZONE_SIZE);
|
||||
DEFINE(SHIM_FLAG_SIGPENDING, SHIM_FLAG_SIGPENDING);
|
||||
DEFINE(__NR_gettid, __NR_gettid);
|
||||
}
|
||||
|
||||
|
||||
@@ -1059,10 +1059,6 @@ void restore_context (struct shim_context * context)
|
||||
struct shim_regs regs = *context->regs;
|
||||
debug("restore context: SP = 0x%08lx, IP = 0x%08lx\n", regs.rsp, regs.rip);
|
||||
|
||||
/* don't clobber redzone. If sigaltstack is used,
|
||||
* this area won't be clobbered by signal context */
|
||||
*(unsigned long*) (regs.rsp - RED_ZONE_SIZE - 8) = regs.rip;
|
||||
|
||||
/* Ready to resume execution, re-enable preemption. */
|
||||
shim_tcb_t * tcb = shim_get_tcb();
|
||||
assert(get_cur_thread()->syscall_stack);
|
||||
@@ -1075,12 +1071,13 @@ void restore_context (struct shim_context * context)
|
||||
context->fs_base = fs_base;
|
||||
|
||||
__asm__ volatile("movq %0, %%rsp\r\n"
|
||||
"addq $2 * 8, %%rsp\r\n" /* skip orig_rax and rsp */
|
||||
"addq $8, %%rsp\r\n" /* skip orig_rax */
|
||||
"movq $0, %%rax\r\n"
|
||||
"popq %%r15\r\n"
|
||||
"popq %%r14\r\n"
|
||||
"popq %%r13\r\n"
|
||||
"popq %%r12\r\n"
|
||||
"popq %%r11\r\n"
|
||||
"addq $8, %%rsp\r\n" /* skip %r11 as it's used below */
|
||||
"popq %%r10\r\n"
|
||||
"popq %%r9\r\n"
|
||||
"popq %%r8\r\n"
|
||||
@@ -1091,8 +1088,14 @@ void restore_context (struct shim_context * context)
|
||||
"popq %%rbx\r\n"
|
||||
"popq %%rbp\r\n"
|
||||
"popfq\r\n"
|
||||
"movq "XSTRINGIFY(SHIM_REGS_RSP)" - "XSTRINGIFY(SHIM_REGS_RIP)"(%%rsp), %%rsp\r\n"
|
||||
"movq $0, %%rax\r\n"
|
||||
"jmp *-"XSTRINGIFY(RED_ZONE_SIZE)"-8(%%rsp)\r\n"
|
||||
:: "g"(®s) : "memory");
|
||||
"popq %%r11\r\n"
|
||||
"movq %%r11, %%gs:%c1\r\n"
|
||||
"movq %c2(%%rsp), %%r11\r\n"
|
||||
"popq %%rsp\r\n"
|
||||
"jmp *%%gs:%c1\r\n"
|
||||
:: "g"(®s),
|
||||
"i"(offsetof(PAL_TCB, libos_tcb) +
|
||||
offsetof(shim_tcb_t, tmp_rip)),
|
||||
"i"(offsetof(struct shim_regs, r11) - offsetof(struct shim_regs, rsp))
|
||||
: "memory");
|
||||
}
|
||||
|
||||
@@ -38,35 +38,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <asm/prctl.h>
|
||||
|
||||
void __attribute__((weak)) syscall_wrapper_after_syscalldb(void)
|
||||
{
|
||||
/*
|
||||
* workaround for linking.
|
||||
* syscalldb.S is excluded for libsysdb_debug.so so it fails to link
|
||||
* due to missing syscall_wrapper_after_syscalldb.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* See syscall_wrapper @ syscalldb.S and illegal_upcall() @ shim_signal.c
|
||||
* for details.
|
||||
* child thread can _not_ use parent stack. So return right after syscall
|
||||
* instruction as if syscall_wrapper is executed.
|
||||
*/
|
||||
static void fixup_child_context(struct shim_regs * regs)
|
||||
{
|
||||
if (regs->rip == (unsigned long)&syscall_wrapper_after_syscalldb) {
|
||||
/*
|
||||
* we don't need to emulate stack pointer change because %rsp is
|
||||
* initialized to new child user stack passed to clone() system call.
|
||||
* See the caller of fixup_child_context().
|
||||
*/
|
||||
/* regs->rsp += RED_ZONE_SIZE; */
|
||||
regs->rflags = regs->r11;
|
||||
regs->rip = regs->rcx;
|
||||
}
|
||||
}
|
||||
|
||||
/* from **sysdeps/unix/sysv/linux/x86_64/clone.S:
|
||||
The userland implementation is:
|
||||
int clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg),
|
||||
@@ -154,16 +125,13 @@ static int clone_implementation_wrapper(struct shim_clone_args * arg)
|
||||
DkEventSet(arg->initialize_event);
|
||||
|
||||
/***** From here down, we are switching to the user-provided stack ****/
|
||||
|
||||
//user_stack_addr[0] ==> user provided function address
|
||||
//user_stack_addr[1] ==> arguments to user provided function.
|
||||
|
||||
debug("child swapping stack to %p return 0x%lx: %d\n",
|
||||
stack, regs.rip, my_thread->tid);
|
||||
|
||||
tcb->context.regs = ®s;
|
||||
fixup_child_context(tcb->context.regs);
|
||||
tcb->context.regs->rsp = (unsigned long)stack;
|
||||
assert((void*)regs.rip < (void*)&__code_address ||
|
||||
(void*)&__code_address_end <= (void*)regs.rip);
|
||||
|
||||
put_thread(my_thread);
|
||||
|
||||
|
||||
@@ -180,8 +180,10 @@ noreturn int shim_do_exit_group (int error_code)
|
||||
* out of this loop.*/
|
||||
static struct atomic_int first = ATOMIC_INIT(0);
|
||||
if (atomic_cmpxchg(&first, 0, 1) == 1) {
|
||||
while (1)
|
||||
while (true) {
|
||||
handle_exit_signal();
|
||||
DkThreadYieldExecution();
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_handle)
|
||||
|
||||
@@ -263,8 +263,6 @@ int shim_do_checkpoint(const char* filename) {
|
||||
|
||||
shim_tcb_t* tcb = shim_get_tcb();
|
||||
assert(tcb && tcb->tp);
|
||||
struct shim_signal signal;
|
||||
__store_context(tcb, NULL, &signal);
|
||||
|
||||
ret = create_checkpoint(filename, &session);
|
||||
if (ret < 0) {
|
||||
|
||||
@@ -87,8 +87,48 @@ out:
|
||||
|
||||
attribute_nofp int shim_do_sigreturn(int __unused) {
|
||||
__UNUSED(__unused);
|
||||
/* do nothing */
|
||||
return 0;
|
||||
shim_tcb_t* tcb = shim_get_tcb();
|
||||
struct shim_regs* regs = tcb->context.regs;
|
||||
ucontext_t* user_uc = (ucontext_t*)regs->rsp;
|
||||
|
||||
debug("sigreturn thread %d regs: %p sp: %08lx "
|
||||
"user_uc: %p gregs.rsp: %08lx gregs.rip: %08lx fpstate %p\n",
|
||||
tcb->tp->tid, regs, regs->rsp, user_uc,
|
||||
user_uc->uc_mcontext.gregs[REG_RSP],
|
||||
user_uc->uc_mcontext.gregs[REG_RIP],
|
||||
user_uc->uc_mcontext.fpregs);
|
||||
|
||||
if (handle_next_signal(user_uc)) {
|
||||
fpstate_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no more signals pending. return back */
|
||||
gregset_t* gregs = &user_uc->uc_mcontext.gregs;
|
||||
/* RAX: will be restored as return value of sigreturn. see below */
|
||||
regs->r15 = (*gregs)[REG_R15];
|
||||
regs->r14 = (*gregs)[REG_R14];
|
||||
regs->r13 = (*gregs)[REG_R13];
|
||||
regs->r12 = (*gregs)[REG_R12];
|
||||
regs->r11 = (*gregs)[REG_R11];
|
||||
regs->r10 = (*gregs)[REG_R10];
|
||||
regs->r9 = (*gregs)[REG_R9];
|
||||
regs->r8 = (*gregs)[REG_R8];
|
||||
regs->rcx = (*gregs)[REG_RCX];
|
||||
regs->rdx = (*gregs)[REG_RDX];
|
||||
regs->rsi = (*gregs)[REG_RSI];
|
||||
regs->rdi = (*gregs)[REG_RDI];
|
||||
regs->rbx = (*gregs)[REG_RBX];
|
||||
regs->rbp = (*gregs)[REG_RBP];
|
||||
regs->rflags = (*gregs)[REG_EFL];
|
||||
regs->rip = (*gregs)[REG_RIP];
|
||||
regs->rsp = (*gregs)[REG_RSP];
|
||||
|
||||
struct _libc_fpstate * user_fpstate = user_uc->uc_mcontext.fpregs;
|
||||
fpstate_restore(user_fpstate);
|
||||
|
||||
/* syscalldb_return doesn't restore %rax. but %rax is return value */
|
||||
return (*gregs)[REG_RAX];
|
||||
}
|
||||
|
||||
int shim_do_sigprocmask(int how, const __sigset_t* set, __sigset_t* oldset) {
|
||||
|
||||
+198
-51
@@ -30,14 +30,55 @@
|
||||
.extern shim_table, debug_unsupp
|
||||
.global syscall_wrapper
|
||||
.type syscall_wrapper, @function
|
||||
.global syscall_wrapper_after_syscalldb
|
||||
.type syscall_wrapper_after_syscalldb, @function
|
||||
|
||||
.global __syscallas_return_begin
|
||||
.global __syscallas_return_before_jmp
|
||||
.global __syscallas_return_end
|
||||
.type __syscallas_return_begin, @function
|
||||
.type __syscallas_return_before_jmp, @function
|
||||
.type __syscallas_return_end, @function
|
||||
|
||||
.global syscalldb_check_sigpending
|
||||
.type syscalldb_check_sigpending, @function
|
||||
|
||||
.global __syscalldb_check_sigpending_begin
|
||||
.global __syscalldb_check_sigpending_end
|
||||
.type __syscalldb_check_sigpending_begin, @function
|
||||
.type __syscalldb_check_sigpending_end, @function
|
||||
|
||||
.macro CHECK_SIGNAL_PENDING_BIT
|
||||
lock btrq $SHIM_FLAG_SIGPENDING, %gs:(SHIM_TCB_OFFSET + SHIM_TCB_FLAGS)
|
||||
.endm
|
||||
|
||||
syscalldb:
|
||||
.cfi_startproc
|
||||
.cfi_def_cfa %rsp, 8
|
||||
|
||||
movq %rsp, %r11
|
||||
.cfi_def_cfa_register %r11
|
||||
.cfi_register %rsp, %r11
|
||||
.cfi_undefined %r11
|
||||
|
||||
# switch stack
|
||||
movq %gs:(SHIM_TCB_OFFSET + SHIM_TCB_SYSCALL_STACK_HIGH), %rsp
|
||||
|
||||
# Create shim_regs struct on the stack.
|
||||
pushq %r11 # save old %rsp
|
||||
# saved rsp has to point to the top of the application stack
|
||||
# (before call to this stub) so we move it past saved return address
|
||||
addq $8, (%rsp)
|
||||
.cfi_def_cfa %rsp, 0
|
||||
.cfi_offset %rsp, 0
|
||||
|
||||
pushq (%r11) # copy return address from original stack
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_offset %rip, -8
|
||||
|
||||
|
||||
.Lsyscalldb_from_wrapper:
|
||||
pushfq
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rflags, 0
|
||||
|
||||
# Under GDB, single-stepping sets Trap Flag (TP) of EFLAGS,
|
||||
# thus TP=1 is stored on pushfq above. Upon consequent popfq,
|
||||
@@ -46,31 +87,58 @@ syscalldb:
|
||||
|
||||
cld
|
||||
pushq %rbp
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rbp, 0
|
||||
pushq %rbx
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rbx, 0
|
||||
pushq %rdi
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rdi, 0
|
||||
pushq %rsi
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rsi, 0
|
||||
pushq %rdx
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rdx, 0
|
||||
pushq %rcx
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rcx, 0
|
||||
pushq %r8
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r8, 0
|
||||
pushq %r9
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r9, 0
|
||||
pushq %r10
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r10, 0
|
||||
pushq %r11
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r11, 0
|
||||
pushq %r12
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r12, 0
|
||||
pushq %r13
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r13, 0
|
||||
pushq %r14
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r14, 0
|
||||
pushq %r15
|
||||
leaq SHIM_REGS_SIZE - SHIM_REGS_R15(%rsp), %rbx
|
||||
pushq %rbx
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %r15, 0
|
||||
pushq %rax
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset %rax, 0
|
||||
# shim_regs struct ends here.
|
||||
|
||||
movq %rsp, %rbp
|
||||
.cfi_def_cfa_offset SHIM_REGS_SIZE
|
||||
.cfi_offset %rbp, -3 * 8 # saved_rbp is at CFA-24 (saved_rflags + saved_rbp)
|
||||
.cfi_def_cfa_register %rbp # %rbp
|
||||
.cfi_def_cfa_register %rbp
|
||||
|
||||
cmp $LIBOS_SYSCALL_BOUND, %rax
|
||||
jae isundef
|
||||
.cfi_remember_state
|
||||
|
||||
movq shim_table@GOTPCREL(%rip), %rbx
|
||||
movq (%rbx,%rax,8), %rbx
|
||||
@@ -84,39 +152,95 @@ syscalldb:
|
||||
movq %r10, %rcx
|
||||
andq $~0xF, %rsp # Required by System V AMD64 ABI.
|
||||
call *%rbx
|
||||
jmp __syscallas_return_begin
|
||||
|
||||
.Ldeliver_signal:
|
||||
movq %rax, %rdi # to preverve returning value
|
||||
call *deliver_signal_on_sysret@GOTPCREL(%rip)
|
||||
|
||||
__syscallas_return_begin:
|
||||
CHECK_SIGNAL_PENDING_BIT
|
||||
jc .Ldeliver_signal
|
||||
|
||||
movq %rbp, %rsp
|
||||
.cfi_def_cfa_register %rsp
|
||||
addq $8, %rsp # skip orig_rax
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %r15
|
||||
.cfi_restore %r15
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %r14
|
||||
.cfi_restore %r14
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %r13
|
||||
.cfi_restore %r13
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %r12
|
||||
.cfi_restore %r12
|
||||
.cfi_adjust_cfa_offset -8
|
||||
addq $8, %rsp # skip %r11 as it's used below
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %r10
|
||||
.cfi_restore %r10
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %r9
|
||||
.cfi_restore %r9
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %r8
|
||||
.cfi_restore %r8
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %rcx
|
||||
.cfi_restore %rcx
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %rdx
|
||||
.cfi_restore %rdx
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %rsi
|
||||
.cfi_restore %rsi
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %rdi
|
||||
.cfi_restore %rdi
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %rbx
|
||||
.cfi_restore %rbx
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popq %rbp
|
||||
.cfi_restore %rbp
|
||||
.cfi_adjust_cfa_offset -8
|
||||
popfq
|
||||
.cfi_restore %rflags
|
||||
.cfi_adjust_cfa_offset -8
|
||||
|
||||
popq %r11
|
||||
.cfi_register %rip, %r11
|
||||
.cfi_adjust_cfa_offset -8
|
||||
movq %r11, %gs:(SHIM_TCB_OFFSET + SHIM_TCB_TMP_RIP)
|
||||
movq SHIM_REGS_R11 - SHIM_REGS_RSP(%rsp), %r11
|
||||
.cfi_restore %r11
|
||||
|
||||
# how to specify
|
||||
# previous %rip = %gs:(SHIM_TCB_OFFSET + SHIM_TCB_TMP_RIP)?
|
||||
.cfi_undefined %rip
|
||||
|
||||
# restore application stack
|
||||
popq %rsp
|
||||
.cfi_restore %rsp
|
||||
|
||||
movq $0, %gs:(SHIM_TCB_OFFSET + TCB_REGS)
|
||||
|
||||
ret:
|
||||
movq %rbp, %rsp
|
||||
addq $2 * 8, %rsp # skip orig_rax and rsp
|
||||
popq %r15
|
||||
popq %r14
|
||||
popq %r13
|
||||
popq %r12
|
||||
popq %r11
|
||||
popq %r10
|
||||
popq %r9
|
||||
popq %r8
|
||||
popq %rcx
|
||||
popq %rdx
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbx
|
||||
popq %rbp
|
||||
.cfi_def_cfa %rsp, 2 * 8 # +8 for ret_addr, +8 for saved_rflags
|
||||
popfq
|
||||
.cfi_def_cfa_offset 8 # +8 for ret_addr
|
||||
retq
|
||||
__syscallas_return_before_jmp:
|
||||
jmp *%gs:(SHIM_TCB_OFFSET + SHIM_TCB_TMP_RIP)
|
||||
__syscallas_return_end:
|
||||
|
||||
isundef:
|
||||
.cfi_restore_state
|
||||
#ifdef DEBUG
|
||||
mov %rax, %rdi
|
||||
andq $~0xF, %rsp # Required by System V AMD64 ABI.
|
||||
call *debug_unsupp@GOTPCREL(%rip)
|
||||
#endif
|
||||
movq $-38, %rax # ENOSYS
|
||||
jmp ret
|
||||
jmp __syscallas_return_begin
|
||||
|
||||
.cfi_endproc
|
||||
.size syscalldb, .-syscalldb
|
||||
@@ -124,38 +248,61 @@ isundef:
|
||||
/*
|
||||
* syscall_wrapper: emulate syscall instruction
|
||||
* prohibited in e.g. Linux-SGX PAL which raises a SIGILL exception
|
||||
* See illegal_upcall() @ shim_signal.c and
|
||||
* fixup_child_context() @ shim_clone.c
|
||||
* This switches stack to dedicated one allocated by LibOS to avoid
|
||||
* small application stack. e.g. as goroutine.
|
||||
* See illegal_upcall() @ shim_signal.c
|
||||
*
|
||||
* input:
|
||||
* argument:
|
||||
* %rcx: Instruction address to continue app execution after trapped
|
||||
* syscall instruction
|
||||
* %r11: rflags on entering syscall
|
||||
* %r11: rflags on entering syscall: unused as %rflags is preserved
|
||||
*
|
||||
* also this function can be used to invoke systemcall without
|
||||
* consuming application stack. The code snippet looks like
|
||||
* leaq 1f(%rip), %rcx
|
||||
* jmp *syscall_wrapper(%rip)
|
||||
* 1:
|
||||
*/
|
||||
syscall_wrapper:
|
||||
.cfi_startproc
|
||||
.cfi_def_cfa %rsp, 0
|
||||
.cfi_def_cfa %rsp, 0 # we don't have %rip saved on stack.
|
||||
# %rcx is used as input for returning %rip
|
||||
.cfi_register %rip, %rcx
|
||||
# %r11 is used as input to keep %rflags
|
||||
.cfi_register %rflags, %r11
|
||||
subq $RED_ZONE_SIZE, %rsp
|
||||
.cfi_adjust_cfa_offset RED_ZONE_SIZE
|
||||
callq *syscalldb@GOTPCREL(%rip)
|
||||
syscall_wrapper_after_syscalldb:
|
||||
addq $RED_ZONE_SIZE, %rsp
|
||||
.cfi_adjust_cfa_offset -RED_ZONE_SIZE
|
||||
# restore %rflags for syscall abi compatibility.
|
||||
# This must be done after "addq $RED_ZONE_SIZE, %rsp" above
|
||||
# which destroys %rflags
|
||||
xchg %r11, (%rsp)
|
||||
.cfi_offset %rflags, 0
|
||||
popfq
|
||||
.cfi_adjust_cfa_offset -8
|
||||
.cfi_same_value %rflags
|
||||
|
||||
# %r11(saved %rflags) isn't needed,
|
||||
# so re-use %r11 to store application stack
|
||||
movq %rsp, %r11
|
||||
.cfi_def_cfa_register %r11
|
||||
.cfi_register %rsp, %r11
|
||||
.cfi_undefined %r11
|
||||
|
||||
# switch stack to the one allocated by LibOS
|
||||
movq %gs:(SHIM_TCB_OFFSET + SHIM_TCB_SYSCALL_STACK_HIGH), %rsp
|
||||
pushq %r11
|
||||
.cfi_def_cfa %rsp, 0
|
||||
.cfi_offset %rsp, 0
|
||||
pushq %rcx
|
||||
.cfi_adjust_cfa_offset 8
|
||||
jmp *%rcx
|
||||
.cfi_offset %rip, -8
|
||||
jmp .Lsyscalldb_from_wrapper
|
||||
|
||||
.cfi_endproc
|
||||
.size syscall_wrapper, .-syscall_wrapper
|
||||
|
||||
syscalldb_check_sigpending:
|
||||
.cfi_startproc
|
||||
__syscalldb_check_sigpending_begin:
|
||||
CHECK_SIGNAL_PENDING_BIT
|
||||
jc .Lsigpending
|
||||
ret
|
||||
|
||||
.Lsigpending:
|
||||
# call nop syscall to trigger signal delivery.
|
||||
# Any syscall with no side effect is okay.
|
||||
# (Or dedicated syscall can be introduced in shim_table.c)
|
||||
movq $__NR_gettid, %rax
|
||||
jmp syscalldb
|
||||
|
||||
__syscalldb_check_sigpending_end:
|
||||
.cfi_endproc
|
||||
.size syscalldb_check_sigpending, .-syscalldb_check_sigpending
|
||||
|
||||
@@ -37,33 +37,48 @@ EXPORT_SYMBOL(shim_gettimeofday);
|
||||
EXPORT_SYMBOL(shim_time);
|
||||
EXPORT_SYMBOL(shim_getcpu);
|
||||
|
||||
static void (*shim_check_sigpending)(void) = NULL;
|
||||
EXPORT_SYMBOL(shim_check_sigpending);
|
||||
|
||||
#define EXPORT_WEAK_SYMBOL(name) \
|
||||
__typeof__(__vdso_##name) name __attribute__((weak, alias("__vdso_" #name)))
|
||||
|
||||
int __vdso_clock_gettime(clockid_t clock, struct timespec* t) {
|
||||
if (shim_clock_gettime)
|
||||
return (*shim_clock_gettime)(clock, t);
|
||||
if (shim_clock_gettime && shim_check_sigpending) {
|
||||
int ret = (*shim_clock_gettime)(clock, t);
|
||||
(*shim_check_sigpending)();
|
||||
return ret;
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_WEAK_SYMBOL(clock_gettime);
|
||||
|
||||
int __vdso_gettimeofday(struct timeval* tv, struct timezone* tz) {
|
||||
if (shim_gettimeofday)
|
||||
return (*shim_gettimeofday)(tv, tz);
|
||||
if (shim_gettimeofday && shim_check_sigpending) {
|
||||
int ret = (*shim_gettimeofday)(tv, tz);
|
||||
(*shim_check_sigpending)();
|
||||
return ret;
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_WEAK_SYMBOL(gettimeofday);
|
||||
|
||||
time_t __vdso_time(time_t* t) {
|
||||
if (shim_time)
|
||||
return (*shim_time)(t);
|
||||
if (shim_time && shim_check_sigpending) {
|
||||
time_t ret = (*shim_time)(t);
|
||||
(*shim_check_sigpending)();
|
||||
return ret;
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_WEAK_SYMBOL(time);
|
||||
|
||||
long __vdso_getcpu(unsigned* cpu, struct getcpu_cache* unused) {
|
||||
if (shim_getcpu)
|
||||
return (*shim_getcpu)(cpu, unused);
|
||||
if (shim_getcpu && shim_check_sigpending) {
|
||||
long ret = (*shim_getcpu)(cpu, unused);
|
||||
(*shim_check_sigpending)();
|
||||
return ret;
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_WEAK_SYMBOL(getcpu);
|
||||
|
||||
@@ -77,6 +77,7 @@ VERSION {
|
||||
__vdso_shim_gettimeofday;
|
||||
__vdso_shim_getcpu;
|
||||
__vdso_shim_time;
|
||||
__vdso_shim_check_sigpending;
|
||||
local: *;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user