[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:
Isaku Yamahata
2020-01-30 17:34:58 -08:00
parent 8759cb8e08
commit a90a6180ce
16 changed files with 845 additions and 290 deletions
+10 -9
View File
@@ -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);
-3
View File
@@ -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
+4 -2
View File
@@ -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;
+25
View File
@@ -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 */
+497 -163
View File
@@ -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,
+11 -2
View File
@@ -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 */
+8 -1
View File
@@ -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) {
/*
+8 -2
View File
@@ -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);
}
+13 -10
View File
@@ -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"(&regs) : "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"(&regs),
"i"(offsetof(PAL_TCB, libos_tcb) +
offsetof(shim_tcb_t, tmp_rip)),
"i"(offsetof(struct shim_regs, r11) - offsetof(struct shim_regs, rsp))
: "memory");
}
+2 -34
View File
@@ -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 = &regs;
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);
+3 -1
View File
@@ -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)
-2
View File
@@ -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) {
+42 -2
View File
@@ -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
View File
@@ -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
+23 -8
View File
@@ -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);
+1
View File
@@ -77,6 +77,7 @@ VERSION {
__vdso_shim_gettimeofday;
__vdso_shim_getcpu;
__vdso_shim_time;
__vdso_shim_check_sigpending;
local: *;
};
}