|
|
|
@ -46,15 +46,7 @@ struct target_sigcontext { |
|
|
|
abi_ulong fault_address; |
|
|
|
}; |
|
|
|
|
|
|
|
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 { |
|
|
|
struct target_ucontext { |
|
|
|
abi_ulong tuc_flags; |
|
|
|
abi_ulong tuc_link; |
|
|
|
target_stack_t tuc_stack; |
|
|
|
@ -98,68 +90,30 @@ struct target_iwmmxt_sigframe { |
|
|
|
#define TARGET_VFP_MAGIC 0x56465001 |
|
|
|
#define TARGET_IWMMXT_MAGIC 0x12ef842a |
|
|
|
|
|
|
|
struct sigframe_v1 |
|
|
|
{ |
|
|
|
struct target_sigcontext sc; |
|
|
|
abi_ulong extramask[TARGET_NSIG_WORDS-1]; |
|
|
|
abi_ulong retcode[4]; |
|
|
|
}; |
|
|
|
|
|
|
|
struct sigframe_v2 |
|
|
|
{ |
|
|
|
struct target_ucontext_v2 uc; |
|
|
|
abi_ulong retcode[4]; |
|
|
|
}; |
|
|
|
|
|
|
|
struct rt_sigframe_v1 |
|
|
|
struct sigframe |
|
|
|
{ |
|
|
|
abi_ulong pinfo; |
|
|
|
abi_ulong puc; |
|
|
|
struct target_siginfo info; |
|
|
|
struct target_ucontext_v1 uc; |
|
|
|
struct target_ucontext uc; |
|
|
|
abi_ulong retcode[4]; |
|
|
|
}; |
|
|
|
|
|
|
|
struct rt_sigframe_v2 |
|
|
|
struct rt_sigframe |
|
|
|
{ |
|
|
|
struct target_siginfo info; |
|
|
|
struct target_ucontext_v2 uc; |
|
|
|
abi_ulong retcode[4]; |
|
|
|
struct sigframe sig; |
|
|
|
}; |
|
|
|
|
|
|
|
/*
|
|
|
|
* For ARM syscalls, we encode the syscall number into the instruction. |
|
|
|
*/ |
|
|
|
#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE)) |
|
|
|
#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE)) |
|
|
|
static abi_ptr sigreturn_fdpic_tramp; |
|
|
|
|
|
|
|
/*
|
|
|
|
* For Thumb syscalls, we pass the syscall number via r7. We therefore |
|
|
|
* need two 16-bit instructions. |
|
|
|
*/ |
|
|
|
#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn)) |
|
|
|
#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn)) |
|
|
|
|
|
|
|
static const abi_ulong retcodes[4] = { |
|
|
|
SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, |
|
|
|
SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN |
|
|
|
}; |
|
|
|
|
|
|
|
/*
|
|
|
|
* Stub needed to make sure the FD register (r9) contains the right |
|
|
|
* value. |
|
|
|
* Up to 3 words of 'retcode' in the sigframe are code, |
|
|
|
* with retcode[3] being used by fdpic for the function descriptor. |
|
|
|
* This code is not actually executed, but is retained for ABI compat. |
|
|
|
* |
|
|
|
* We will create a table of 8 retcode variants in the sigtramp page. |
|
|
|
* Let each table entry use 3 words. |
|
|
|
*/ |
|
|
|
static const unsigned long sigreturn_fdpic_codes[3] = { |
|
|
|
0xe59fc004, /* ldr r12, [pc, #4] to read function descriptor */ |
|
|
|
0xe59c9004, /* ldr r9, [r12, #4] to setup GOT */ |
|
|
|
0xe59cf000 /* ldr pc, [r12] to jump into restorer */ |
|
|
|
}; |
|
|
|
|
|
|
|
static const unsigned long sigreturn_fdpic_thumb_codes[3] = { |
|
|
|
0xc008f8df, /* ldr r12, [pc, #8] to read function descriptor */ |
|
|
|
0x9004f8dc, /* ldr r9, [r12, #4] to setup GOT */ |
|
|
|
0xf000f8dc /* ldr pc, [r12] to jump into restorer */ |
|
|
|
}; |
|
|
|
#define RETCODE_WORDS 3 |
|
|
|
#define RETCODE_BYTES (RETCODE_WORDS * 4) |
|
|
|
|
|
|
|
static inline int valid_user_regs(CPUARMState *regs) |
|
|
|
{ |
|
|
|
@ -207,15 +161,15 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) |
|
|
|
} |
|
|
|
|
|
|
|
static int |
|
|
|
setup_return(CPUARMState *env, struct target_sigaction *ka, |
|
|
|
abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) |
|
|
|
setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, |
|
|
|
struct sigframe *frame, abi_ulong sp_addr) |
|
|
|
{ |
|
|
|
abi_ulong handler = 0; |
|
|
|
abi_ulong handler_fdpic_GOT = 0; |
|
|
|
abi_ulong retcode; |
|
|
|
|
|
|
|
int thumb; |
|
|
|
int thumb, retcode_idx; |
|
|
|
int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); |
|
|
|
bool copy_retcode; |
|
|
|
|
|
|
|
if (is_fdpic) { |
|
|
|
/* In FDPIC mode, ka->_sa_handler points to a function
|
|
|
|
@ -232,6 +186,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, |
|
|
|
} |
|
|
|
|
|
|
|
thumb = handler & 1; |
|
|
|
retcode_idx = thumb + (ka->sa_flags & TARGET_SA_SIGINFO ? 2 : 0); |
|
|
|
|
|
|
|
uint32_t cpsr = cpsr_read(env); |
|
|
|
|
|
|
|
@ -249,44 +204,29 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, |
|
|
|
|
|
|
|
if (ka->sa_flags & TARGET_SA_RESTORER) { |
|
|
|
if (is_fdpic) { |
|
|
|
/* For FDPIC we ensure that the restorer is called with a
|
|
|
|
* correct r9 value. For that we need to write code on |
|
|
|
* the stack that sets r9 and jumps back to restorer |
|
|
|
* value. |
|
|
|
*/ |
|
|
|
if (thumb) { |
|
|
|
__put_user(sigreturn_fdpic_thumb_codes[0], rc); |
|
|
|
__put_user(sigreturn_fdpic_thumb_codes[1], rc + 1); |
|
|
|
__put_user(sigreturn_fdpic_thumb_codes[2], rc + 2); |
|
|
|
__put_user((abi_ulong)ka->sa_restorer, rc + 3); |
|
|
|
} else { |
|
|
|
__put_user(sigreturn_fdpic_codes[0], rc); |
|
|
|
__put_user(sigreturn_fdpic_codes[1], rc + 1); |
|
|
|
__put_user(sigreturn_fdpic_codes[2], rc + 2); |
|
|
|
__put_user((abi_ulong)ka->sa_restorer, rc + 3); |
|
|
|
} |
|
|
|
|
|
|
|
retcode = rc_addr + thumb; |
|
|
|
__put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]); |
|
|
|
retcode = (sigreturn_fdpic_tramp + |
|
|
|
retcode_idx * RETCODE_BYTES + thumb); |
|
|
|
copy_retcode = true; |
|
|
|
} else { |
|
|
|
retcode = ka->sa_restorer; |
|
|
|
copy_retcode = false; |
|
|
|
} |
|
|
|
} else { |
|
|
|
unsigned int idx = thumb; |
|
|
|
|
|
|
|
if (ka->sa_flags & TARGET_SA_SIGINFO) { |
|
|
|
idx += 2; |
|
|
|
} |
|
|
|
|
|
|
|
__put_user(retcodes[idx], rc); |
|
|
|
retcode = default_sigreturn + retcode_idx * RETCODE_BYTES + thumb; |
|
|
|
copy_retcode = true; |
|
|
|
} |
|
|
|
|
|
|
|
retcode = rc_addr + thumb; |
|
|
|
/* Copy the code to the stack slot for ABI compatibility. */ |
|
|
|
if (copy_retcode) { |
|
|
|
memcpy(frame->retcode, g2h_untagged(retcode & ~1), RETCODE_BYTES); |
|
|
|
} |
|
|
|
|
|
|
|
env->regs[0] = usig; |
|
|
|
if (is_fdpic) { |
|
|
|
env->regs[9] = handler_fdpic_GOT; |
|
|
|
} |
|
|
|
env->regs[13] = frame_addr; |
|
|
|
env->regs[13] = sp_addr; |
|
|
|
env->regs[14] = retcode; |
|
|
|
env->regs[15] = handler & (thumb ? ~1 : ~3); |
|
|
|
cpsr_write(env, cpsr, CPSR_IT | CPSR_T | CPSR_E, CPSRWriteByInstr); |
|
|
|
@ -294,7 +234,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) |
|
|
|
static abi_ulong *setup_sigframe_vfp(abi_ulong *regspace, CPUARMState *env) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
struct target_vfp_sigframe *vfpframe; |
|
|
|
@ -311,8 +251,7 @@ static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) |
|
|
|
return (abi_ulong*)(vfpframe+1); |
|
|
|
} |
|
|
|
|
|
|
|
static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, |
|
|
|
CPUARMState *env) |
|
|
|
static abi_ulong *setup_sigframe_iwmmxt(abi_ulong *regspace, CPUARMState *env) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
struct target_iwmmxt_sigframe *iwmmxtframe; |
|
|
|
@ -331,15 +270,15 @@ static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, |
|
|
|
return (abi_ulong*)(iwmmxtframe+1); |
|
|
|
} |
|
|
|
|
|
|
|
static void setup_sigframe_v2(struct target_ucontext_v2 *uc, |
|
|
|
target_sigset_t *set, CPUARMState *env) |
|
|
|
static void setup_sigframe(struct target_ucontext *uc, |
|
|
|
target_sigset_t *set, CPUARMState *env) |
|
|
|
{ |
|
|
|
struct target_sigaltstack stack; |
|
|
|
int i; |
|
|
|
abi_ulong *regspace; |
|
|
|
|
|
|
|
/* Clear all the bits of the ucontext we don't use. */ |
|
|
|
memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); |
|
|
|
memset(uc, 0, offsetof(struct target_ucontext, tuc_mcontext)); |
|
|
|
|
|
|
|
memset(&stack, 0, sizeof(stack)); |
|
|
|
target_save_altstack(&stack, env); |
|
|
|
@ -349,10 +288,10 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, |
|
|
|
/* Save coprocessor signal frame. */ |
|
|
|
regspace = uc->tuc_regspace; |
|
|
|
if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { |
|
|
|
regspace = setup_sigframe_v2_vfp(regspace, env); |
|
|
|
regspace = setup_sigframe_vfp(regspace, env); |
|
|
|
} |
|
|
|
if (arm_feature(env, ARM_FEATURE_IWMMXT)) { |
|
|
|
regspace = setup_sigframe_v2_iwmmxt(regspace, env); |
|
|
|
regspace = setup_sigframe_iwmmxt(regspace, env); |
|
|
|
} |
|
|
|
|
|
|
|
/* Write terminating magic word */ |
|
|
|
@ -363,114 +302,23 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* compare linux/arch/arm/kernel/signal.c:setup_frame() */ |
|
|
|
static void setup_frame_v1(int usig, struct target_sigaction *ka, |
|
|
|
target_sigset_t *set, CPUARMState *regs) |
|
|
|
{ |
|
|
|
struct sigframe_v1 *frame; |
|
|
|
abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); |
|
|
|
int i; |
|
|
|
|
|
|
|
trace_user_setup_frame(regs, frame_addr); |
|
|
|
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
setup_sigcontext(&frame->sc, regs, set->sig[0]); |
|
|
|
|
|
|
|
for(i = 1; i < TARGET_NSIG_WORDS; i++) { |
|
|
|
__put_user(set->sig[i], &frame->extramask[i - 1]); |
|
|
|
} |
|
|
|
|
|
|
|
if (setup_return(regs, ka, frame->retcode, frame_addr, usig, |
|
|
|
frame_addr + offsetof(struct sigframe_v1, retcode))) { |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
unlock_user_struct(frame, frame_addr, 1); |
|
|
|
return; |
|
|
|
sigsegv: |
|
|
|
unlock_user_struct(frame, frame_addr, 1); |
|
|
|
force_sigsegv(usig); |
|
|
|
} |
|
|
|
|
|
|
|
static void setup_frame_v2(int usig, struct target_sigaction *ka, |
|
|
|
target_sigset_t *set, CPUARMState *regs) |
|
|
|
{ |
|
|
|
struct sigframe_v2 *frame; |
|
|
|
abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); |
|
|
|
|
|
|
|
trace_user_setup_frame(regs, frame_addr); |
|
|
|
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
setup_sigframe_v2(&frame->uc, set, regs); |
|
|
|
|
|
|
|
if (setup_return(regs, ka, frame->retcode, frame_addr, usig, |
|
|
|
frame_addr + offsetof(struct sigframe_v2, retcode))) { |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
unlock_user_struct(frame, frame_addr, 1); |
|
|
|
return; |
|
|
|
sigsegv: |
|
|
|
unlock_user_struct(frame, frame_addr, 1); |
|
|
|
force_sigsegv(usig); |
|
|
|
} |
|
|
|
|
|
|
|
void setup_frame(int usig, struct target_sigaction *ka, |
|
|
|
target_sigset_t *set, CPUARMState *regs) |
|
|
|
{ |
|
|
|
if (get_osversion() >= 0x020612) { |
|
|
|
setup_frame_v2(usig, ka, set, regs); |
|
|
|
} else { |
|
|
|
setup_frame_v1(usig, ka, set, regs); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */ |
|
|
|
static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, |
|
|
|
target_siginfo_t *info, |
|
|
|
target_sigset_t *set, CPUARMState *env) |
|
|
|
{ |
|
|
|
struct rt_sigframe_v1 *frame; |
|
|
|
abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); |
|
|
|
struct target_sigaltstack stack; |
|
|
|
int i; |
|
|
|
abi_ulong info_addr, uc_addr; |
|
|
|
struct sigframe *frame; |
|
|
|
abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); |
|
|
|
|
|
|
|
trace_user_setup_rt_frame(env, frame_addr); |
|
|
|
trace_user_setup_frame(regs, frame_addr); |
|
|
|
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); |
|
|
|
__put_user(info_addr, &frame->pinfo); |
|
|
|
uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); |
|
|
|
__put_user(uc_addr, &frame->puc); |
|
|
|
tswap_siginfo(&frame->info, info); |
|
|
|
|
|
|
|
/* Clear all the bits of the ucontext we don't use. */ |
|
|
|
memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); |
|
|
|
setup_sigframe(&frame->uc, set, regs); |
|
|
|
|
|
|
|
memset(&stack, 0, sizeof(stack)); |
|
|
|
target_save_altstack(&stack, env); |
|
|
|
memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); |
|
|
|
|
|
|
|
setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]); |
|
|
|
for(i = 0; i < TARGET_NSIG_WORDS; i++) { |
|
|
|
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); |
|
|
|
} |
|
|
|
|
|
|
|
if (setup_return(env, ka, frame->retcode, frame_addr, usig, |
|
|
|
frame_addr + offsetof(struct rt_sigframe_v1, retcode))) { |
|
|
|
if (setup_return(regs, ka, usig, frame, frame_addr)) { |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
env->regs[1] = info_addr; |
|
|
|
env->regs[2] = uc_addr; |
|
|
|
|
|
|
|
unlock_user_struct(frame, frame_addr, 1); |
|
|
|
return; |
|
|
|
sigsegv: |
|
|
|
@ -478,11 +326,11 @@ sigsegv: |
|
|
|
force_sigsegv(usig); |
|
|
|
} |
|
|
|
|
|
|
|
static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, |
|
|
|
target_siginfo_t *info, |
|
|
|
target_sigset_t *set, CPUARMState *env) |
|
|
|
void setup_rt_frame(int usig, struct target_sigaction *ka, |
|
|
|
target_siginfo_t *info, |
|
|
|
target_sigset_t *set, CPUARMState *env) |
|
|
|
{ |
|
|
|
struct rt_sigframe_v2 *frame; |
|
|
|
struct rt_sigframe *frame; |
|
|
|
abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); |
|
|
|
abi_ulong info_addr, uc_addr; |
|
|
|
|
|
|
|
@ -491,14 +339,13 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); |
|
|
|
uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); |
|
|
|
info_addr = frame_addr + offsetof(struct rt_sigframe, info); |
|
|
|
uc_addr = frame_addr + offsetof(struct rt_sigframe, sig.uc); |
|
|
|
tswap_siginfo(&frame->info, info); |
|
|
|
|
|
|
|
setup_sigframe_v2(&frame->uc, set, env); |
|
|
|
setup_sigframe(&frame->sig.uc, set, env); |
|
|
|
|
|
|
|
if (setup_return(env, ka, frame->retcode, frame_addr, usig, |
|
|
|
frame_addr + offsetof(struct rt_sigframe_v2, retcode))) { |
|
|
|
if (setup_return(env, ka, usig, &frame->sig, frame_addr)) { |
|
|
|
goto sigsegv; |
|
|
|
} |
|
|
|
|
|
|
|
@ -512,17 +359,6 @@ sigsegv: |
|
|
|
force_sigsegv(usig); |
|
|
|
} |
|
|
|
|
|
|
|
void setup_rt_frame(int usig, struct target_sigaction *ka, |
|
|
|
target_siginfo_t *info, |
|
|
|
target_sigset_t *set, CPUARMState *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(CPUARMState *env, struct target_sigcontext *sc) |
|
|
|
{ |
|
|
|
@ -553,55 +389,7 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
static long do_sigreturn_v1(CPUARMState *env) |
|
|
|
{ |
|
|
|
abi_ulong frame_addr; |
|
|
|
struct sigframe_v1 *frame = NULL; |
|
|
|
target_sigset_t set; |
|
|
|
sigset_t host_set; |
|
|
|
int i; |
|
|
|
|
|
|
|
/*
|
|
|
|
* 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. |
|
|
|
*/ |
|
|
|
frame_addr = env->regs[13]; |
|
|
|
trace_user_do_sigreturn(env, frame_addr); |
|
|
|
if (frame_addr & 7) { |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
|
|
|
|
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
|
|
|
|
__get_user(set.sig[0], &frame->sc.oldmask); |
|
|
|
for(i = 1; i < TARGET_NSIG_WORDS; i++) { |
|
|
|
__get_user(set.sig[i], &frame->extramask[i - 1]); |
|
|
|
} |
|
|
|
|
|
|
|
target_to_host_sigset_internal(&host_set, &set); |
|
|
|
set_sigmask(&host_set); |
|
|
|
|
|
|
|
if (restore_sigcontext(env, &frame->sc)) { |
|
|
|
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 -TARGET_QEMU_ESIGRETURN; |
|
|
|
|
|
|
|
badframe: |
|
|
|
force_sig(TARGET_SIGSEGV); |
|
|
|
return -TARGET_QEMU_ESIGRETURN; |
|
|
|
} |
|
|
|
|
|
|
|
static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) |
|
|
|
static abi_ulong *restore_sigframe_vfp(CPUARMState *env, abi_ulong *regspace) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
abi_ulong magic, sz; |
|
|
|
@ -631,8 +419,8 @@ static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) |
|
|
|
return (abi_ulong*)(vfpframe + 1); |
|
|
|
} |
|
|
|
|
|
|
|
static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, |
|
|
|
abi_ulong *regspace) |
|
|
|
static abi_ulong *restore_sigframe_iwmmxt(CPUARMState *env, |
|
|
|
abi_ulong *regspace) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
abi_ulong magic, sz; |
|
|
|
@ -656,9 +444,9 @@ static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, |
|
|
|
return (abi_ulong*)(iwmmxtframe + 1); |
|
|
|
} |
|
|
|
|
|
|
|
static int do_sigframe_return_v2(CPUARMState *env, |
|
|
|
target_ulong context_addr, |
|
|
|
struct target_ucontext_v2 *uc) |
|
|
|
static int do_sigframe_return(CPUARMState *env, |
|
|
|
target_ulong context_addr, |
|
|
|
struct target_ucontext *uc) |
|
|
|
{ |
|
|
|
sigset_t host_set; |
|
|
|
abi_ulong *regspace; |
|
|
|
@ -666,19 +454,20 @@ static int do_sigframe_return_v2(CPUARMState *env, |
|
|
|
target_to_host_sigset(&host_set, &uc->tuc_sigmask); |
|
|
|
set_sigmask(&host_set); |
|
|
|
|
|
|
|
if (restore_sigcontext(env, &uc->tuc_mcontext)) |
|
|
|
if (restore_sigcontext(env, &uc->tuc_mcontext)) { |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/* Restore coprocessor signal frame */ |
|
|
|
regspace = uc->tuc_regspace; |
|
|
|
if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { |
|
|
|
regspace = restore_sigframe_v2_vfp(env, regspace); |
|
|
|
regspace = restore_sigframe_vfp(env, regspace); |
|
|
|
if (!regspace) { |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
if (arm_feature(env, ARM_FEATURE_IWMMXT)) { |
|
|
|
regspace = restore_sigframe_v2_iwmmxt(env, regspace); |
|
|
|
regspace = restore_sigframe_iwmmxt(env, regspace); |
|
|
|
if (!regspace) { |
|
|
|
return 1; |
|
|
|
} |
|
|
|
@ -695,10 +484,10 @@ static int do_sigframe_return_v2(CPUARMState *env, |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static long do_sigreturn_v2(CPUARMState *env) |
|
|
|
long do_sigreturn(CPUARMState *env) |
|
|
|
{ |
|
|
|
abi_ulong frame_addr; |
|
|
|
struct sigframe_v2 *frame = NULL; |
|
|
|
struct sigframe *frame = NULL; |
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we stacked the signal on a 64-bit boundary, |
|
|
|
@ -715,10 +504,9 @@ static long do_sigreturn_v2(CPUARMState *env) |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
|
|
|
|
if (do_sigframe_return_v2(env, |
|
|
|
frame_addr |
|
|
|
+ offsetof(struct sigframe_v2, uc), |
|
|
|
&frame->uc)) { |
|
|
|
if (do_sigframe_return(env, |
|
|
|
frame_addr + offsetof(struct sigframe, uc), |
|
|
|
&frame->uc)) { |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
|
|
|
|
@ -731,20 +519,10 @@ badframe: |
|
|
|
return -TARGET_QEMU_ESIGRETURN; |
|
|
|
} |
|
|
|
|
|
|
|
long do_sigreturn(CPUARMState *env) |
|
|
|
{ |
|
|
|
if (get_osversion() >= 0x020612) { |
|
|
|
return do_sigreturn_v2(env); |
|
|
|
} else { |
|
|
|
return do_sigreturn_v1(env); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static long do_rt_sigreturn_v1(CPUARMState *env) |
|
|
|
long do_rt_sigreturn(CPUARMState *env) |
|
|
|
{ |
|
|
|
abi_ulong frame_addr; |
|
|
|
struct rt_sigframe_v1 *frame = NULL; |
|
|
|
sigset_t host_set; |
|
|
|
struct rt_sigframe *frame = NULL; |
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we stacked the signal on a 64-bit boundary, |
|
|
|
@ -761,20 +539,12 @@ static long do_rt_sigreturn_v1(CPUARMState *env) |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
|
|
|
|
target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); |
|
|
|
set_sigmask(&host_set); |
|
|
|
|
|
|
|
if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { |
|
|
|
if (do_sigframe_return(env, |
|
|
|
frame_addr + offsetof(struct rt_sigframe, sig.uc), |
|
|
|
&frame->sig.uc)) { |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
|
|
|
|
target_restore_altstack(&frame->uc.tuc_stack, env); |
|
|
|
|
|
|
|
#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 -TARGET_QEMU_ESIGRETURN; |
|
|
|
|
|
|
|
@ -784,47 +554,77 @@ badframe: |
|
|
|
return -TARGET_QEMU_ESIGRETURN; |
|
|
|
} |
|
|
|
|
|
|
|
static long do_rt_sigreturn_v2(CPUARMState *env) |
|
|
|
{ |
|
|
|
abi_ulong frame_addr; |
|
|
|
struct rt_sigframe_v2 *frame = NULL; |
|
|
|
/*
|
|
|
|
* EABI syscalls pass the number via r7. |
|
|
|
* Note that the kernel still adds the OABI syscall number to the trap, |
|
|
|
* presumably for backward ABI compatibility with unwinders. |
|
|
|
*/ |
|
|
|
#define ARM_MOV_R7_IMM(X) (0xe3a07000 | (X)) |
|
|
|
#define ARM_SWI_SYS(X) (0xef000000 | (X) | ARM_SYSCALL_BASE) |
|
|
|
|
|
|
|
/*
|
|
|
|
* 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. |
|
|
|
*/ |
|
|
|
frame_addr = env->regs[13]; |
|
|
|
trace_user_do_rt_sigreturn(env, frame_addr); |
|
|
|
if (frame_addr & 7) { |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
#define THUMB_MOVS_R7_IMM(X) (0x2700 | (X)) |
|
|
|
#define THUMB_SWI_SYS 0xdf00 |
|
|
|
|
|
|
|
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
static void write_arm_sigreturn(uint32_t *rc, int syscall) |
|
|
|
{ |
|
|
|
__put_user(ARM_MOV_R7_IMM(syscall), rc); |
|
|
|
__put_user(ARM_SWI_SYS(syscall), rc + 1); |
|
|
|
/* Wrote 8 of 12 bytes */ |
|
|
|
} |
|
|
|
|
|
|
|
if (do_sigframe_return_v2(env, |
|
|
|
frame_addr |
|
|
|
+ offsetof(struct rt_sigframe_v2, uc), |
|
|
|
&frame->uc)) { |
|
|
|
goto badframe; |
|
|
|
} |
|
|
|
static void write_thm_sigreturn(uint32_t *rc, int syscall) |
|
|
|
{ |
|
|
|
__put_user(THUMB_SWI_SYS << 16 | THUMB_MOVS_R7_IMM(syscall), rc); |
|
|
|
/* Wrote 4 of 12 bytes */ |
|
|
|
} |
|
|
|
|
|
|
|
unlock_user_struct(frame, frame_addr, 0); |
|
|
|
return -TARGET_QEMU_ESIGRETURN; |
|
|
|
/*
|
|
|
|
* Stub needed to make sure the FD register (r9) contains the right value. |
|
|
|
* Use the same instruction sequence as the kernel. |
|
|
|
*/ |
|
|
|
static void write_arm_fdpic_sigreturn(uint32_t *rc, int ofs) |
|
|
|
{ |
|
|
|
assert(ofs <= 0xfff); |
|
|
|
__put_user(0xe59d3000 | ofs, rc + 0); /* ldr r3, [sp, #ofs] */ |
|
|
|
__put_user(0xe8930908, rc + 1); /* ldm r3, { r3, r9 } */ |
|
|
|
__put_user(0xe12fff13, rc + 2); /* bx r3 */ |
|
|
|
/* Wrote 12 of 12 bytes */ |
|
|
|
} |
|
|
|
|
|
|
|
badframe: |
|
|
|
unlock_user_struct(frame, frame_addr, 0); |
|
|
|
force_sig(TARGET_SIGSEGV); |
|
|
|
return -TARGET_QEMU_ESIGRETURN; |
|
|
|
static void write_thm_fdpic_sigreturn(void *vrc, int ofs) |
|
|
|
{ |
|
|
|
uint16_t *rc = vrc; |
|
|
|
|
|
|
|
assert((ofs & ~0x3fc) == 0); |
|
|
|
__put_user(0x9b00 | (ofs >> 2), rc + 0); /* ldr r3, [sp, #ofs] */ |
|
|
|
__put_user(0xcb0c, rc + 1); /* ldm r3, { r2, r3 } */ |
|
|
|
__put_user(0x4699, rc + 2); /* mov r9, r3 */ |
|
|
|
__put_user(0x4710, rc + 3); /* bx r2 */ |
|
|
|
/* Wrote 8 of 12 bytes */ |
|
|
|
} |
|
|
|
|
|
|
|
long do_rt_sigreturn(CPUARMState *env) |
|
|
|
void setup_sigtramp(abi_ulong sigtramp_page) |
|
|
|
{ |
|
|
|
if (get_osversion() >= 0x020612) { |
|
|
|
return do_rt_sigreturn_v2(env); |
|
|
|
} else { |
|
|
|
return do_rt_sigreturn_v1(env); |
|
|
|
} |
|
|
|
uint32_t total_size = 8 * RETCODE_BYTES; |
|
|
|
uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, total_size, 0); |
|
|
|
|
|
|
|
assert(tramp != NULL); |
|
|
|
|
|
|
|
default_sigreturn = sigtramp_page; |
|
|
|
write_arm_sigreturn(&tramp[0 * RETCODE_WORDS], TARGET_NR_sigreturn); |
|
|
|
write_thm_sigreturn(&tramp[1 * RETCODE_WORDS], TARGET_NR_sigreturn); |
|
|
|
write_arm_sigreturn(&tramp[2 * RETCODE_WORDS], TARGET_NR_rt_sigreturn); |
|
|
|
write_thm_sigreturn(&tramp[3 * RETCODE_WORDS], TARGET_NR_rt_sigreturn); |
|
|
|
|
|
|
|
sigreturn_fdpic_tramp = sigtramp_page + 4 * RETCODE_BYTES; |
|
|
|
write_arm_fdpic_sigreturn(tramp + 4 * RETCODE_WORDS, |
|
|
|
offsetof(struct sigframe, retcode[3])); |
|
|
|
write_thm_fdpic_sigreturn(tramp + 5 * RETCODE_WORDS, |
|
|
|
offsetof(struct sigframe, retcode[3])); |
|
|
|
write_arm_fdpic_sigreturn(tramp + 6 * RETCODE_WORDS, |
|
|
|
offsetof(struct rt_sigframe, sig.retcode[3])); |
|
|
|
write_thm_fdpic_sigreturn(tramp + 7 * RETCODE_WORDS, |
|
|
|
offsetof(struct rt_sigframe, sig.retcode[3])); |
|
|
|
|
|
|
|
unlock_user(tramp, sigtramp_page, total_size); |
|
|
|
} |
|
|
|
|