diff options
-rw-r--r-- | linux-user/qemu.h | 1 | ||||
-rw-r--r-- | linux-user/signal.c | 154 | ||||
-rw-r--r-- | linux-user/syscall.c | 31 |
3 files changed, 172 insertions, 14 deletions
diff --git a/linux-user/qemu.h b/linux-user/qemu.h index b33ad8942f..5106d4526d 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -169,6 +169,7 @@ void cpu_loop(CPUState *env); void init_paths(const char *prefix); const char *path(const char *pathname); char *target_strerror(int err); +int get_osversion(void); extern int loglevel; extern FILE *logfile; diff --git a/linux-user/signal.c b/linux-user/signal.c index 3aa8590c68..1e5fb20bc9 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1020,12 +1020,22 @@ struct target_sigcontext { abi_ulong fault_address; }; -struct target_ucontext { +struct target_ucontext_v1 { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_ucontext_v2 { abi_ulong tuc_flags; abi_ulong tuc_link; target_stack_t tuc_stack; struct target_sigcontext tuc_mcontext; target_sigset_t tuc_sigmask; /* mask last for extensibility */ + char __unused[128 - sizeof(sigset_t)]; + abi_ulong tuc_regspace[128] __attribute__((__aligned__(8))); }; struct sigframe @@ -1035,12 +1045,19 @@ struct sigframe abi_ulong retcode; }; -struct rt_sigframe +struct rt_sigframe_v1 { abi_ulong pinfo; abi_ulong puc; struct target_siginfo info; - struct target_ucontext uc; + struct target_ucontext_v1 uc; + abi_ulong retcode; +}; + +struct rt_sigframe_v2 +{ + struct target_siginfo info; + struct target_ucontext_v2 uc; abi_ulong retcode; }; @@ -1191,11 +1208,11 @@ end: } /* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */ -static void setup_rt_frame(int usig, struct emulated_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUState *env) +static void setup_rt_frame_v1(int usig, struct emulated_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) { - struct rt_sigframe *frame; + struct rt_sigframe_v1 *frame; abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); struct target_sigaltstack stack; int i, err = 0; @@ -1204,14 +1221,14 @@ static void setup_rt_frame(int usig, struct emulated_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) return /* 1 */; - info_addr = frame_addr + offsetof(struct rt_sigframe, info); + info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); __put_user_error(info_addr, &frame->pinfo, err); - uc_addr = frame_addr + offsetof(struct rt_sigframe, uc); + uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); __put_user_error(uc_addr, &frame->puc, err); err |= copy_siginfo_to_user(&frame->info, info); /* Clear all the bits of the ucontext we don't use. */ - memset(&frame->uc, 0, offsetof(struct target_ucontext, tuc_mcontext)); + memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); memset(&stack, 0, sizeof(stack)); __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); @@ -1228,7 +1245,55 @@ static void setup_rt_frame(int usig, struct emulated_sigaction *ka, if (err == 0) err = setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe, retcode)); + frame_addr + offsetof(struct rt_sigframe_v1, retcode)); + + if (err == 0) { + env->regs[1] = info_addr; + env->regs[2] = uc_addr; + } + +end: + unlock_user_struct(frame, frame_addr, 1); + + // return err; +} + +static void setup_rt_frame_v2(int usig, struct emulated_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) +{ + struct rt_sigframe_v2 *frame; + abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); + struct target_sigaltstack stack; + int i, err = 0; + abi_ulong info_addr, uc_addr; + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + return /* 1 */; + + info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); + uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); + err |= copy_siginfo_to_user(&frame->info, info); + + /* Clear all the bits of the ucontext we don't use. */ + memset(&frame->uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); + + memset(&stack, 0, sizeof(stack)); + __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); + __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); + memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); + + err |= setup_sigcontext(&frame->uc.tuc_mcontext, /*&frame->fpstate,*/ + env, set->sig[0]); + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + if (__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i])) + goto end; + } + + if (err == 0) + err = setup_return(env, ka, &frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe_v2, retcode)); if (err == 0) { /* @@ -1246,6 +1311,17 @@ end: // return err; } +static void setup_rt_frame(int usig, struct emulated_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) +{ + if (get_osversion() >= 0x020612) { + setup_rt_frame_v2(usig, ka, info, set, env); + } else { + setup_rt_frame_v1(usig, ka, info, set, env); + } +} + static int restore_sigcontext(CPUState *env, struct target_sigcontext *sc) { @@ -1325,10 +1401,51 @@ badframe: return 0; } -long do_rt_sigreturn(CPUState *env) +long do_rt_sigreturn_v1(CPUState *env) { abi_ulong frame_addr; - struct rt_sigframe *frame; + struct rt_sigframe_v1 *frame; + sigset_t host_set; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (env->regs[13] & 7) + goto badframe; + + frame_addr = env->regs[13]; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); + sigprocmask(SIG_SETMASK, &host_set, NULL); + + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) + goto badframe; + + if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; + +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt(current)) + send_sig(SIGTRAP, current, 1); +#endif + unlock_user_struct(frame, frame_addr, 0); + return env->regs[0]; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(SIGSEGV /* , current */); + return 0; +} + +long do_rt_sigreturn_v2(CPUState *env) +{ + abi_ulong frame_addr; + struct rt_sigframe_v2 *frame; sigset_t host_set; /* @@ -1349,7 +1466,7 @@ long do_rt_sigreturn(CPUState *env) if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) goto badframe; - if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) + if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v2, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) goto badframe; #if 0 @@ -1366,6 +1483,15 @@ badframe: return 0; } +long do_rt_sigreturn(CPUState *env) +{ + if (get_osversion() >= 0x020612) { + return do_rt_sigreturn_v2(env); + } else { + return do_rt_sigreturn_v1(env); + } +} + #elif defined(TARGET_SPARC) #define __SUNOS_MAXWIN 31 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index a62bd9bd6b..0e506cc137 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3054,6 +3054,37 @@ static inline abi_long host_to_target_timespec(abi_ulong target_addr, unlock_user_struct(target_ts, target_addr, 1); } +int get_osversion(void) +{ + static int osversion; + struct new_utsname buf; + const char *s; + int i, n, tmp; + if (osversion) + return osversion; + if (qemu_uname_release && *qemu_uname_release) { + s = qemu_uname_release; + } else { + if (sys_uname(&buf)) + return 0; + s = buf.release; + } + tmp = 0; + for (i = 0; i < 3; i++) { + n = 0; + while (*s >= '0' && *s <= '9') { + n *= 10; + n += *s - '0'; + s++; + } + tmp = (tmp << 8) + n; + if (*s == '.') + s++; + } + osversion = tmp; + return osversion; +} + /* do_syscall() should always have a single exit point at the end so that actions, such as logging of syscall results, can be performed. All errnos that do_syscall() returns must be -TARGET_<errcode>. */ |