aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2021-10-04 14:21:39 -0700
committerRichard Henderson <richard.henderson@linaro.org>2021-10-04 14:21:39 -0700
commite7ab6584784dbbd701f0ffe7b29918f44dcd98fd (patch)
treee5ce7b4f7397c9c426440f06d809e0898fd32278
parent30bd1db58b09c12b68c35f041f919014b885482d (diff)
parentefee71c8ca181d4f5b2211736b38a74a2a223375 (diff)
downloadqemu-e7ab6584784dbbd701f0ffe7b29918f44dcd98fd.zip
qemu-e7ab6584784dbbd701f0ffe7b29918f44dcd98fd.tar.gz
qemu-e7ab6584784dbbd701f0ffe7b29918f44dcd98fd.tar.bz2
Merge remote-tracking branch 'remotes/vivier/tags/linux-user-for-6.2-pull-request' into staging
Pull request linux-user 20211004 Move signal trampolines to new page # gpg: Signature made Mon 04 Oct 2021 12:43:53 AM PDT # gpg: using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C # gpg: issuer "laurent@vivier.eu" # gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full] # gpg: aka "Laurent Vivier <laurent@vivier.eu>" [full] # gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full] * remotes/vivier/tags/linux-user-for-6.2-pull-request: (26 commits) tests/tcg/multiarch: Re-enable signals test for most guests linux-user: Remove default for TARGET_ARCH_HAS_SIGTRAMP_PAGE linux-user/xtensa: Implement setup_sigtramp linux-user/sparc: Implement setup_sigtramp linux-user/sh4: Implement setup_sigtramp linux-user/s390x: Implement setup_sigtramp linux-user/riscv: Implement setup_sigtramp linux-user/ppc: Implement setup_sigtramp linux-user/ppc: Simplify encode_trampoline linux-user/openrisc: Implement setup_sigtramp linux-user/nios2: Document non-use of setup_sigtramp linux-user/mips: Implement setup_sigtramp linux-user/mips: Tidy install_sigtramp linux-user/microblaze: Implement setup_sigtramp linux-user/m68k: Implement setup_sigtramp linux-user/x86_64: Raise SIGSEGV if SA_RESTORER not set linux-user/i386: Implement setup_sigtramp linux-user/hppa: Document non-use of setup_sigtramp linux-user/hexagon: Implement setup_sigtramp linux-user/cris: Implement setup_sigtramp ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
-rw-r--r--linux-user/aarch64/signal.c34
-rw-r--r--linux-user/aarch64/target_signal.h2
-rw-r--r--linux-user/alpha/signal.c34
-rw-r--r--linux-user/alpha/target_signal.h1
-rw-r--r--linux-user/arm/signal.c472
-rw-r--r--linux-user/arm/target_signal.h2
-rw-r--r--linux-user/cris/signal.c29
-rw-r--r--linux-user/cris/target_signal.h2
-rw-r--r--linux-user/elfload.c14
-rw-r--r--linux-user/hexagon/signal.c19
-rw-r--r--linux-user/hexagon/target_signal.h2
-rw-r--r--linux-user/hppa/target_signal.h14
-rw-r--r--linux-user/i386/signal.c65
-rw-r--r--linux-user/i386/target_signal.h2
-rw-r--r--linux-user/m68k/signal.c47
-rw-r--r--linux-user/m68k/target_signal.h2
-rw-r--r--linux-user/microblaze/signal.c24
-rw-r--r--linux-user/microblaze/target_signal.h2
-rw-r--r--linux-user/mips/signal.c39
-rw-r--r--linux-user/mips/target_signal.h1
-rw-r--r--linux-user/mips64/target_signal.h2
-rw-r--r--linux-user/nios2/target_signal.h3
-rw-r--r--linux-user/openrisc/signal.c22
-rw-r--r--linux-user/openrisc/target_signal.h2
-rw-r--r--linux-user/ppc/signal.c40
-rw-r--r--linux-user/ppc/target_signal.h2
-rw-r--r--linux-user/riscv/signal.c22
-rw-r--r--linux-user/riscv/target_signal.h2
-rw-r--r--linux-user/s390x/signal.c24
-rw-r--r--linux-user/s390x/target_signal.h2
-rw-r--r--linux-user/sh4/signal.c40
-rw-r--r--linux-user/sh4/target_signal.h2
-rw-r--r--linux-user/signal-common.h6
-rw-r--r--linux-user/signal.c3
-rw-r--r--linux-user/sparc/signal.c40
-rw-r--r--linux-user/sparc/target_signal.h4
-rw-r--r--linux-user/x86_64/target_signal.h3
-rw-r--r--linux-user/xtensa/signal.c56
-rw-r--r--linux-user/xtensa/target_signal.h2
-rw-r--r--tests/tcg/hppa/Makefile.target7
-rw-r--r--tests/tcg/i386/Makefile.target3
-rw-r--r--tests/tcg/multiarch/Makefile.target8
-rw-r--r--tests/tcg/sh4/Makefile.target7
43 files changed, 559 insertions, 550 deletions
diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c
index 4902564..29c52db 100644
--- a/linux-user/aarch64/signal.c
+++ b/linux-user/aarch64/signal.c
@@ -109,7 +109,6 @@ struct target_rt_sigframe {
struct target_rt_frame_record {
uint64_t fp;
uint64_t lr;
- uint32_t tramp[2];
};
static void target_setup_general_frame(struct target_rt_sigframe *sf,
@@ -461,9 +460,9 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
layout.total_size = MAX(layout.total_size,
sizeof(struct target_rt_sigframe));
- /* Reserve space for the return code. On a real system this would
- * be within the VDSO. So, despite the name this is not a "real"
- * record within the frame.
+ /*
+ * Reserve space for the standard frame unwind pair: fp, lr.
+ * Despite the name this is not a "real" record within the frame.
*/
fr_ofs = layout.total_size;
layout.total_size += sizeof(struct target_rt_frame_record);
@@ -496,15 +495,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
if (ka->sa_flags & TARGET_SA_RESTORER) {
return_addr = ka->sa_restorer;
} else {
- /*
- * mov x8,#__NR_rt_sigreturn; svc #0
- * Since these are instructions they need to be put as little-endian
- * regardless of target default or current CPU endianness.
- */
- __put_user_e(0xd2801168, &fr->tramp[0], le);
- __put_user_e(0xd4000001, &fr->tramp[1], le);
- return_addr = frame_addr + fr_ofs
- + offsetof(struct target_rt_frame_record, tramp);
+ return_addr = default_rt_sigreturn;
}
env->xregs[0] = usig;
env->xregs[29] = frame_addr + fr_ofs;
@@ -577,3 +568,20 @@ long do_sigreturn(CPUARMState *env)
{
return do_rt_sigreturn(env);
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
+ assert(tramp != NULL);
+
+ /*
+ * mov x8,#__NR_rt_sigreturn; svc #0
+ * Since these are instructions they need to be put as little-endian
+ * regardless of target default or current CPU endianness.
+ */
+ __put_user_e(0xd2801168, &tramp[0], le);
+ __put_user_e(0xd4000001, &tramp[1], le);
+
+ default_rt_sigreturn = sigtramp_page;
+ unlock_user(tramp, sigtramp_page, 8);
+}
diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h
index 18013e1..7580d99 100644
--- a/linux-user/aarch64/target_signal.h
+++ b/linux-user/aarch64/target_signal.h
@@ -25,4 +25,6 @@ typedef struct target_sigaltstack {
#define TARGET_SEGV_MTESERR 9 /* Synchronous ARM MTE exception */
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* AARCH64_TARGET_SIGNAL_H */
diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c
index 3a820f6..bbe3dd1 100644
--- a/linux-user/alpha/signal.c
+++ b/linux-user/alpha/signal.c
@@ -55,13 +55,11 @@ struct target_ucontext {
struct target_sigframe {
struct target_sigcontext sc;
- unsigned int retcode[3];
};
struct target_rt_sigframe {
target_siginfo_t info;
struct target_ucontext uc;
- unsigned int retcode[3];
};
#define INSN_MOV_R30_R16 0x47fe0410
@@ -142,12 +140,7 @@ void setup_frame(int sig, struct target_sigaction *ka,
if (ka->ka_restorer) {
r26 = ka->ka_restorer;
} else {
- __put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
- __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn,
- &frame->retcode[1]);
- __put_user(INSN_CALLSYS, &frame->retcode[2]);
- /* imb() */
- r26 = frame_addr + offsetof(struct target_sigframe, retcode);
+ r26 = default_sigreturn;
}
unlock_user_struct(frame, frame_addr, 1);
@@ -196,12 +189,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
if (ka->ka_restorer) {
r26 = ka->ka_restorer;
} else {
- __put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
- __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn,
- &frame->retcode[1]);
- __put_user(INSN_CALLSYS, &frame->retcode[2]);
- /* imb(); */
- r26 = frame_addr + offsetof(struct target_rt_sigframe, retcode);
+ r26 = default_rt_sigreturn;
}
if (err) {
@@ -269,3 +257,21 @@ badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6 * 4, 0);
+ assert(tramp != NULL);
+
+ default_sigreturn = sigtramp_page;
+ __put_user(INSN_MOV_R30_R16, &tramp[0]);
+ __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, &tramp[1]);
+ __put_user(INSN_CALLSYS, &tramp[2]);
+
+ default_rt_sigreturn = sigtramp_page + 3 * 4;
+ __put_user(INSN_MOV_R30_R16, &tramp[3]);
+ __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, &tramp[4]);
+ __put_user(INSN_CALLSYS, &tramp[5]);
+
+ unlock_user(tramp, sigtramp_page, 6 * 4);
+}
diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h
index 2506429..0b6a39d 100644
--- a/linux-user/alpha/target_signal.h
+++ b/linux-user/alpha/target_signal.h
@@ -93,6 +93,7 @@ typedef struct target_sigaltstack {
#define TARGET_ARCH_HAS_SETUP_FRAME
#define TARGET_ARCH_HAS_KA_RESTORER
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
/* bit-flags */
#define TARGET_SS_AUTODISARM (1U << 31) /* disable sas during sighandling */
diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c
index ed144f9..df9f8e8 100644
--- a/linux-user/arm/signal.c
+++ b/linux-user/arm/signal.c
@@ -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);
}
diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h
index 0998dd6..1e7fb0c 100644
--- a/linux-user/arm/target_signal.h
+++ b/linux-user/arm/target_signal.h
@@ -22,4 +22,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* ARM_TARGET_SIGNAL_H */
diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c
index 2c39bdf..7f6aca9 100644
--- a/linux-user/cris/signal.c
+++ b/linux-user/cris/signal.c
@@ -97,6 +97,14 @@ static abi_ulong get_sigframe(CPUCRISState *env, int framesize)
return sp - framesize;
}
+static void setup_sigreturn(uint16_t *retcode)
+{
+ /* This is movu.w __NR_sigreturn, r9; break 13; */
+ __put_user(0x9c5f, retcode + 0);
+ __put_user(TARGET_NR_sigreturn, retcode + 1);
+ __put_user(0xe93d, retcode + 2);
+}
+
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUCRISState *env)
{
@@ -112,14 +120,8 @@ void setup_frame(int sig, struct target_sigaction *ka,
/*
* The CRIS signal return trampoline. A real linux/CRIS kernel doesn't
* use this trampoline anymore but it sets it up for GDB.
- * In QEMU, using the trampoline simplifies things a bit so we use it.
- *
- * This is movu.w __NR_sigreturn, r9; break 13;
*/
- __put_user(0x9c5f, frame->retcode+0);
- __put_user(TARGET_NR_sigreturn,
- frame->retcode + 1);
- __put_user(0xe93d, frame->retcode + 2);
+ setup_sigreturn(frame->retcode);
/* Save the mask. */
__put_user(set->sig[0], &frame->sc.oldmask);
@@ -135,7 +137,7 @@ void setup_frame(int sig, struct target_sigaction *ka,
env->regs[10] = sig;
env->pc = (unsigned long) ka->_sa_handler;
/* Link SRP so the guest returns through the trampoline. */
- env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode);
+ env->pregs[PR_SRP] = default_sigreturn;
unlock_user_struct(frame, frame_addr, 1);
return;
@@ -187,3 +189,14 @@ long do_rt_sigreturn(CPUCRISState *env)
qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n");
return -TARGET_ENOSYS;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0);
+ assert(tramp != NULL);
+
+ default_sigreturn = sigtramp_page;
+ setup_sigreturn(tramp);
+
+ unlock_user(tramp, sigtramp_page, 6);
+}
diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h
index 495a142..83a5155 100644
--- a/linux-user/cris/target_signal.h
+++ b/linux-user/cris/target_signal.h
@@ -22,4 +22,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* CRIS_TARGET_SIGNAL_H */
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 5f9e214..2404d48 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -7,6 +7,7 @@
#include "qemu.h"
#include "user-internals.h"
+#include "signal-common.h"
#include "loader.h"
#include "user-mmap.h"
#include "disas/disas.h"
@@ -17,6 +18,7 @@
#include "qemu/units.h"
#include "qemu/selfmap.h"
#include "qapi/error.h"
+#include "target_signal.h"
#ifdef _ARCH_PPC64
#undef ARCH_DLINFO
@@ -3249,6 +3251,18 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
#endif
}
+ /*
+ * TODO: load a vdso, which would also contain the signal trampolines.
+ * Otherwise, allocate a private page to hold them.
+ */
+ if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) {
+ abi_ulong tramp_page = target_mmap(0, TARGET_PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ setup_sigtramp(tramp_page);
+ target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC);
+ }
+
bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
info, (elf_interpreter ? &interp_info : NULL));
info->start_stack = bprm->p;
diff --git a/linux-user/hexagon/signal.c b/linux-user/hexagon/signal.c
index c7f0bf6..74e6173 100644
--- a/linux-user/hexagon/signal.c
+++ b/linux-user/hexagon/signal.c
@@ -162,6 +162,11 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
setup_ucontext(&frame->uc, env, set);
tswap_siginfo(&frame->info, info);
+ /*
+ * The on-stack signal trampoline is no longer executed;
+ * however, the libgcc signal frame unwinding code checks
+ * for the presence of these two numeric magic values.
+ */
install_sigtramp(frame->tramp);
env->gpr[HEX_REG_PC] = ka->_sa_handler;
@@ -171,8 +176,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
frame_addr + offsetof(struct target_rt_sigframe, info);
env->gpr[HEX_REG_R02] =
frame_addr + offsetof(struct target_rt_sigframe, uc);
- env->gpr[HEX_REG_LR] =
- frame_addr + offsetof(struct target_rt_sigframe, tramp);
+ env->gpr[HEX_REG_LR] = default_rt_sigreturn;
return;
@@ -271,3 +275,14 @@ badframe:
force_sig(TARGET_SIGSEGV);
return 0;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 4 * 2, 0);
+ assert(tramp != NULL);
+
+ default_rt_sigreturn = sigtramp_page;
+ install_sigtramp(tramp);
+
+ unlock_user(tramp, sigtramp_page, 4 * 2);
+}
diff --git a/linux-user/hexagon/target_signal.h b/linux-user/hexagon/target_signal.h
index 345cf1c..9e0223d 100644
--- a/linux-user/hexagon/target_signal.h
+++ b/linux-user/hexagon/target_signal.h
@@ -31,4 +31,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h
index 7f52536..d558119 100644
--- a/linux-user/hppa/target_signal.h
+++ b/linux-user/hppa/target_signal.h
@@ -71,4 +71,18 @@ typedef struct target_sigaltstack {
/* mask for all SS_xxx flags */
#define TARGET_SS_FLAG_BITS TARGET_SS_AUTODISARM
+/*
+ * We cannot use a bare sigtramp page for hppa-linux.
+ *
+ * Unlike other guests where we use the instructions at PC to validate
+ * an offset from SP, the hppa libgcc signal frame fallback unwinding uses
+ * the PC address itself to find the frame. This is due to the fact that
+ * the hppa grows the stack upward, and the frame is of unknown size.
+ *
+ * TODO: We should be able to use a VDSO to address this, by providing
+ * proper unwind info for the sigtramp code, at which point the fallback
+ * unwinder will not be used.
+ */
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0
+
#endif /* HPPA_TARGET_SIGNAL_H */
diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c
index 3b4b55f..433efa3 100644
--- a/linux-user/i386/signal.c
+++ b/linux-user/i386/signal.c
@@ -310,6 +310,22 @@ get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size)
}
#ifndef TARGET_X86_64
+static void install_sigtramp(void *tramp)
+{
+ /* This is popl %eax ; movl $syscall,%eax ; int $0x80 */
+ __put_user(0xb858, (uint16_t *)(tramp + 0));
+ __put_user(TARGET_NR_sigreturn, (int32_t *)(tramp + 2));
+ __put_user(0x80cd, (uint16_t *)(tramp + 6));
+}
+
+static void install_rt_sigtramp(void *tramp)
+{
+ /* This is movl $syscall,%eax ; int $0x80 */
+ __put_user(0xb8, (uint8_t *)(tramp + 0));
+ __put_user(TARGET_NR_rt_sigreturn, (int32_t *)(tramp + 1));
+ __put_user(0x80cd, (uint16_t *)(tramp + 5));
+}
+
/* compare linux/arch/i386/kernel/signal.c:setup_frame() */
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUX86State *env)
@@ -338,16 +354,9 @@ void setup_frame(int sig, struct target_sigaction *ka,
if (ka->sa_flags & TARGET_SA_RESTORER) {
__put_user(ka->sa_restorer, &frame->pretcode);
} else {
- uint16_t val16;
- abi_ulong retcode_addr;
- retcode_addr = frame_addr + offsetof(struct sigframe, retcode);
- __put_user(retcode_addr, &frame->pretcode);
- /* This is popl %eax ; movl $,%eax ; int $0x80 */
- val16 = 0xb858;
- __put_user(val16, (uint16_t *)(frame->retcode+0));
- __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2));
- val16 = 0x80cd;
- __put_user(val16, (uint16_t *)(frame->retcode+6));
+ /* This is no longer used, but is retained for ABI compatibility. */
+ install_sigtramp(frame->retcode);
+ __put_user(default_sigreturn, &frame->pretcode);
}
/* Set up registers for signal handler */
@@ -412,24 +421,18 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
-#ifndef TARGET_X86_64
if (ka->sa_flags & TARGET_SA_RESTORER) {
__put_user(ka->sa_restorer, &frame->pretcode);
} else {
- uint16_t val16;
- addr = frame_addr + offsetof(struct rt_sigframe, retcode);
- __put_user(addr, &frame->pretcode);
- /* This is movl $,%eax ; int $0x80 */
- __put_user(0xb8, (char *)(frame->retcode+0));
- __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1));
- val16 = 0x80cd;
- __put_user(val16, (uint16_t *)(frame->retcode+5));
- }
+#ifdef TARGET_X86_64
+ /* For x86_64, SA_RESTORER is required ABI. */
+ goto give_sigsegv;
#else
- /* XXX: Would be slightly better to return -EFAULT here if test fails
- assert(ka->sa_flags & TARGET_SA_RESTORER); */
- __put_user(ka->sa_restorer, &frame->pretcode);
+ /* This is no longer used, but is retained for ABI compatibility. */
+ install_rt_sigtramp(frame->retcode);
+ __put_user(default_rt_sigreturn, &frame->pretcode);
#endif
+ }
/* Set up registers for signal handler */
env->regs[R_ESP] = frame_addr;
@@ -592,3 +595,19 @@ badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
+
+#ifndef TARGET_X86_64
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0);
+ assert(tramp != NULL);
+
+ default_sigreturn = sigtramp_page;
+ install_sigtramp(tramp);
+
+ default_rt_sigreturn = sigtramp_page + 8;
+ install_rt_sigtramp(tramp + 8);
+
+ unlock_user(tramp, sigtramp_page, 2 * 8);
+}
+#endif
diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h
index 50361af..64d09f2 100644
--- a/linux-user/i386/target_signal.h
+++ b/linux-user/i386/target_signal.h
@@ -22,4 +22,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* I386_TARGET_SIGNAL_H */
diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c
index 4f8eb6f..ec33482 100644
--- a/linux-user/m68k/signal.c
+++ b/linux-user/m68k/signal.c
@@ -39,7 +39,6 @@ struct target_sigframe
int sig;
int code;
abi_ulong psc;
- char retcode[8];
abi_ulong extramask[TARGET_NSIG_WORDS-1];
struct target_sigcontext sc;
};
@@ -76,7 +75,6 @@ struct target_rt_sigframe
int sig;
abi_ulong pinfo;
abi_ulong puc;
- char retcode[8];
struct target_siginfo info;
struct target_ucontext uc;
};
@@ -130,7 +128,6 @@ void setup_frame(int sig, struct target_sigaction *ka,
{
struct target_sigframe *frame;
abi_ulong frame_addr;
- abi_ulong retcode_addr;
abi_ulong sc_addr;
int i;
@@ -152,16 +149,7 @@ void setup_frame(int sig, struct target_sigaction *ka,
}
/* Set up to return from userspace. */
-
- retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode);
- __put_user(retcode_addr, &frame->pretcode);
-
- /* moveq #,d0; trap #0 */
-
- __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16),
- (uint32_t *)(frame->retcode));
-
- /* Set up to return from userspace */
+ __put_user(default_sigreturn, &frame->pretcode);
env->aregs[7] = frame_addr;
env->pc = ka->_sa_handler;
@@ -288,7 +276,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
{
struct target_rt_sigframe *frame;
abi_ulong frame_addr;
- abi_ulong retcode_addr;
abi_ulong info_addr;
abi_ulong uc_addr;
int err = 0;
@@ -325,17 +312,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
}
/* Set up to return from userspace. */
-
- retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode);
- __put_user(retcode_addr, &frame->pretcode);
-
- /* moveq #,d0; notb d0; trap #0 */
-
- __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16),
- (uint32_t *)(frame->retcode + 0));
- __put_user(0x4e40, (uint16_t *)(frame->retcode + 4));
-
- /* Set up to return from userspace */
+ __put_user(default_rt_sigreturn, &frame->pretcode);
env->aregs[7] = frame_addr;
env->pc = ka->_sa_handler;
@@ -411,3 +388,23 @@ badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ void *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 4 + 6, 0);
+ assert(tramp != NULL);
+
+ default_sigreturn = sigtramp_page;
+
+ /* moveq #,d0; trap #0 */
+ __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), (uint32_t *)tramp);
+
+ default_rt_sigreturn = sigtramp_page + 4;
+
+ /* moveq #,d0; notb d0; trap #0 */
+ __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16),
+ (uint32_t *)(tramp + 4));
+ __put_user(0x4e40, (uint16_t *)(tramp + 8));
+
+ unlock_user(tramp, sigtramp_page, 4 + 6);
+}
diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h
index d096544..94157bf 100644
--- a/linux-user/m68k/target_signal.h
+++ b/linux-user/m68k/target_signal.h
@@ -22,4 +22,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* M68K_TARGET_SIGNAL_H */
diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c
index b822679..8ebb6a1 100644
--- a/linux-user/microblaze/signal.c
+++ b/linux-user/microblaze/signal.c
@@ -161,17 +161,11 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
/* Kernel does not use SA_RESTORER. */
- /* addi r12, r0, __NR_sigreturn */
- __put_user(0x31800000U | TARGET_NR_rt_sigreturn, frame->tramp + 0);
- /* brki r14, 0x8 */
- __put_user(0xb9cc0008U, frame->tramp + 1);
-
/*
* Return from sighandler will jump to the tramp.
* Negative 8 offset because return is rtsd r15, 8
*/
- env->regs[15] =
- frame_addr + offsetof(struct target_rt_sigframe, tramp) - 8;
+ env->regs[15] = default_rt_sigreturn - 8;
/* Set up registers for signal handler */
env->regs[1] = frame_addr;
@@ -220,3 +214,19 @@ long do_rt_sigreturn(CPUMBState *env)
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
+ assert(tramp != NULL);
+
+ /*
+ * addi r12, r0, __NR_rt_sigreturn
+ * brki r14, 0x8
+ */
+ __put_user(0x31800000U | TARGET_NR_rt_sigreturn, tramp);
+ __put_user(0xb9cc0008U, tramp + 1);
+
+ default_rt_sigreturn = sigtramp_page;
+ unlock_user(tramp, sigtramp_page, 8);
+}
diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h
index 1c32629..e8b510f 100644
--- a/linux-user/microblaze/target_signal.h
+++ b/linux-user/microblaze/target_signal.h
@@ -21,4 +21,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* MICROBLAZE_TARGET_SIGNAL_H */
diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c
index d174b34..8f79e40 100644
--- a/linux-user/mips/signal.c
+++ b/linux-user/mips/signal.c
@@ -87,10 +87,8 @@ struct target_rt_sigframe {
};
/* Install trampoline to jump back from signal handler */
-static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall)
+static void install_sigtramp(uint32_t *tramp, unsigned int syscall)
{
- int err = 0;
-
/*
* Set up the return code ...
*
@@ -100,7 +98,6 @@ static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall)
__put_user(0x24020000 + syscall, tramp + 0);
__put_user(0x0000000c , tramp + 1);
- return err;
}
static inline void setup_sigcontext(CPUMIPSState *regs,
@@ -212,8 +209,6 @@ void setup_frame(int sig, struct target_sigaction * ka,
goto give_sigsegv;
}
- install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
-
setup_sigcontext(regs, &frame->sf_sc);
for(i = 0; i < TARGET_NSIG_WORDS; i++) {
@@ -234,7 +229,7 @@ void setup_frame(int sig, struct target_sigaction * ka,
regs->active_tc.gpr[ 5] = 0;
regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc);
regs->active_tc.gpr[29] = frame_addr;
- regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code);
+ regs->active_tc.gpr[31] = default_sigreturn;
/* The original kernel code sets CP0_EPC to the handler
* since it returns to userland using eret
* we cannot do this here, and we must set PC directly */
@@ -308,8 +303,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
goto give_sigsegv;
}
- install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn);
-
tswap_siginfo(&frame->rs_info, info);
__put_user(0, &frame->rs_uc.tuc_flags);
@@ -338,11 +331,13 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
env->active_tc.gpr[ 6] = frame_addr
+ offsetof(struct target_rt_sigframe, rs_uc);
env->active_tc.gpr[29] = frame_addr;
- env->active_tc.gpr[31] = frame_addr
- + offsetof(struct target_rt_sigframe, rs_code);
- /* The original kernel code sets CP0_EPC to the handler
- * since it returns to userland using eret
- * we cannot do this here, and we must set PC directly */
+ env->active_tc.gpr[31] = default_rt_sigreturn;
+
+ /*
+ * The original kernel code sets CP0_EPC to the handler
+ * since it returns to userland using eret
+ * we cannot do this here, and we must set PC directly
+ */
env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
mips_set_hflags_isa_mode_from_pc(env);
unlock_user_struct(frame, frame_addr, 1);
@@ -382,3 +377,19 @@ badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0);
+ assert(tramp != NULL);
+
+#ifdef TARGET_ARCH_HAS_SETUP_FRAME
+ default_sigreturn = sigtramp_page;
+ install_sigtramp(tramp, TARGET_NR_sigreturn);
+#endif
+
+ default_rt_sigreturn = sigtramp_page + 8;
+ install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn);
+
+ unlock_user(tramp, sigtramp_page, 2 * 8);
+}
diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h
index d521765..780a4dd 100644
--- a/linux-user/mips/target_signal.h
+++ b/linux-user/mips/target_signal.h
@@ -73,6 +73,7 @@ typedef struct target_sigaltstack {
/* compare linux/arch/mips/kernel/signal.c:setup_frame() */
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
/* bit-flags */
#define TARGET_SS_AUTODISARM (1U << 31) /* disable sas during sighandling */
diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h
index d857c55..275e9b7 100644
--- a/linux-user/mips64/target_signal.h
+++ b/linux-user/mips64/target_signal.h
@@ -76,4 +76,6 @@ typedef struct target_sigaltstack {
/* compare linux/arch/mips/kernel/signal.c:setup_frame() */
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* MIPS64_TARGET_SIGNAL_H */
diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h
index aebf749..fe266c4 100644
--- a/linux-user/nios2/target_signal.h
+++ b/linux-user/nios2/target_signal.h
@@ -19,4 +19,7 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
+/* Nios2 uses a fixed address on the kuser page for sigreturn. */
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0
+
#endif /* NIOS2_TARGET_SIGNAL_H */
diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c
index ca2532b..be8b687 100644
--- a/linux-user/openrisc/signal.c
+++ b/linux-user/openrisc/signal.c
@@ -38,7 +38,6 @@ typedef struct target_ucontext {
typedef struct target_rt_sigframe {
struct target_siginfo info;
target_ucontext uc;
- uint32_t retcode[4]; /* trampoline code */
} target_rt_sigframe;
static void restore_sigcontext(CPUOpenRISCState *env, target_sigcontext *sc)
@@ -116,14 +115,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
- /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1; l.nop; l.nop */
- __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, frame->retcode + 0);
- __put_user(0x20000001, frame->retcode + 1);
- __put_user(0x15000000, frame->retcode + 2);
- __put_user(0x15000000, frame->retcode + 3);
-
/* Set up registers for signal handler */
- cpu_set_gpr(env, 9, frame_addr + offsetof(target_rt_sigframe, retcode));
+ cpu_set_gpr(env, 9, default_rt_sigreturn);
cpu_set_gpr(env, 3, sig);
cpu_set_gpr(env, 4, frame_addr + offsetof(target_rt_sigframe, info));
cpu_set_gpr(env, 5, frame_addr + offsetof(target_rt_sigframe, uc));
@@ -169,3 +162,16 @@ long do_rt_sigreturn(CPUOpenRISCState *env)
force_sig(TARGET_SIGSEGV);
return 0;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
+ assert(tramp != NULL);
+
+ /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1 */
+ __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, tramp + 0);
+ __put_user(0x20000001, tramp + 1);
+
+ default_rt_sigreturn = sigtramp_page;
+ unlock_user(tramp, sigtramp_page, 8);
+}
diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h
index 8283eaf..077ec3d 100644
--- a/linux-user/openrisc/target_signal.h
+++ b/linux-user/openrisc/target_signal.h
@@ -26,4 +26,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* OPENRISC_TARGET_SIGNAL_H */
diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c
index e4d0dfa..c37744c 100644
--- a/linux-user/ppc/signal.c
+++ b/linux-user/ppc/signal.c
@@ -203,9 +203,6 @@ struct target_func_ptr {
#endif
-/* We use the mc_pad field for the signal return trampoline. */
-#define tramp mc_pad
-
/* See arch/powerpc/kernel/signal.c. */
static target_ulong get_sigframe(struct target_sigaction *ka,
CPUPPCState *env,
@@ -309,10 +306,8 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame)
static void encode_trampoline(int sigret, uint32_t *tramp)
{
/* Set up the sigreturn trampoline: li r0,sigret; sc. */
- if (sigret) {
- __put_user(0x38000000 | sigret, &tramp[0]);
- __put_user(0x44000002, &tramp[1]);
- }
+ __put_user(0x38000000 | sigret, &tramp[0]);
+ __put_user(0x44000002, &tramp[1]);
}
static void restore_user_regs(CPUPPCState *env,
@@ -438,12 +433,7 @@ void setup_frame(int sig, struct target_sigaction *ka,
/* Save user regs. */
save_user_regs(env, &frame->mctx);
- /* Construct the trampoline code on the stack. */
- encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp);
-
- /* The kernel checks for the presence of a VDSO here. We don't
- emulate a vdso, so use a sigreturn system call. */
- env->lr = (target_ulong) h2g(frame->mctx.tramp);
+ env->lr = default_sigreturn;
/* Turn off all fp exceptions. */
env->fpscr = 0;
@@ -479,7 +469,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUPPCState *env)
{
struct target_rt_sigframe *rt_sf;
- uint32_t *trampptr = 0;
struct target_mcontext *mctx = 0;
target_ulong rt_sf_addr, newsp = 0;
int i, err = 0;
@@ -509,22 +498,17 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
#if defined(TARGET_PPC64)
mctx = &rt_sf->uc.tuc_sigcontext.mcontext;
- trampptr = &rt_sf->trampoline[0];
sc = &rt_sf->uc.tuc_sigcontext;
__put_user(h2g(mctx), &sc->regs);
__put_user(sig, &sc->signal);
#else
mctx = &rt_sf->uc.tuc_mcontext;
- trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp;
#endif
save_user_regs(env, mctx);
- encode_trampoline(TARGET_NR_rt_sigreturn, trampptr);
- /* The kernel checks for the presence of a VDSO here. We don't
- emulate a vdso, so use a sigreturn system call. */
- env->lr = (target_ulong) h2g(trampptr);
+ env->lr = default_rt_sigreturn;
/* Turn off all fp exceptions. */
env->fpscr = 0;
@@ -722,3 +706,19 @@ abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx,
return 0;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0);
+ assert(tramp != NULL);
+
+#ifdef TARGET_ARCH_HAS_SETUP_FRAME
+ default_sigreturn = sigtramp_page;
+ encode_trampoline(TARGET_NR_sigreturn, tramp + 0);
+#endif
+
+ default_rt_sigreturn = sigtramp_page + 8;
+ encode_trampoline(TARGET_NR_rt_sigreturn, tramp + 2);
+
+ unlock_user(tramp, sigtramp_page, 2 * 8);
+}
diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h
index 72fcdd9..82184ab 100644
--- a/linux-user/ppc/target_signal.h
+++ b/linux-user/ppc/target_signal.h
@@ -24,4 +24,6 @@ typedef struct target_sigaltstack {
#if !defined(TARGET_PPC64)
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* PPC_TARGET_SIGNAL_H */
diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
index f7f33bc..a0f9542 100644
--- a/linux-user/riscv/signal.c
+++ b/linux-user/riscv/signal.c
@@ -47,7 +47,6 @@ struct target_ucontext {
};
struct target_rt_sigframe {
- uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */
struct target_siginfo info;
struct target_ucontext uc;
};
@@ -105,12 +104,6 @@ static void setup_ucontext(struct target_ucontext *uc,
setup_sigcontext(&uc->uc_mcontext, env);
}
-static inline void install_sigtramp(uint32_t *tramp)
-{
- __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */
- __put_user(0x00000073, tramp + 1); /* ecall */
-}
-
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPURISCVState *env)
@@ -127,14 +120,13 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
setup_ucontext(&frame->uc, env, set);
tswap_siginfo(&frame->info, info);
- install_sigtramp(frame->tramp);
env->pc = ka->_sa_handler;
env->gpr[xSP] = frame_addr;
env->gpr[xA0] = sig;
env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info);
env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
- env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp);
+ env->gpr[xRA] = default_rt_sigreturn;
return;
@@ -203,3 +195,15 @@ badframe:
force_sig(TARGET_SIGSEGV);
return 0;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
+ assert(tramp != NULL);
+
+ __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */
+ __put_user(0x00000073, tramp + 1); /* ecall */
+
+ default_rt_sigreturn = sigtramp_page;
+ unlock_user(tramp, sigtramp_page, 8);
+}
diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h
index f113ba9..3e36fdd 100644
--- a/linux-user/riscv/target_signal.h
+++ b/linux-user/riscv/target_signal.h
@@ -15,4 +15,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* RISCV_TARGET_SIGNAL_H */
diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c
index 80f3408..676b948 100644
--- a/linux-user/s390x/signal.c
+++ b/linux-user/s390x/signal.c
@@ -68,7 +68,6 @@ typedef struct {
target_sigregs sregs;
int signo;
target_sigregs_ext sregs_ext;
- uint16_t retcode;
} sigframe;
#define TARGET_UC_VXRS 2
@@ -85,7 +84,6 @@ struct target_ucontext {
typedef struct {
uint8_t callee_used_stack[__SIGNAL_FRAMESIZE];
- uint16_t retcode;
struct target_siginfo info;
struct target_ucontext uc;
} rt_sigframe;
@@ -209,9 +207,7 @@ void setup_frame(int sig, struct target_sigaction *ka,
if (ka->sa_flags & TARGET_SA_RESTORER) {
restorer = ka->sa_restorer;
} else {
- restorer = frame_addr + offsetof(sigframe, retcode);
- __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn,
- &frame->retcode);
+ restorer = default_sigreturn;
}
/* Set up registers for signal handler */
@@ -262,9 +258,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
if (ka->sa_flags & TARGET_SA_RESTORER) {
restorer = ka->sa_restorer;
} else {
- restorer = frame_addr + offsetof(typeof(*frame), retcode);
- __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn,
- &frame->retcode);
+ restorer = default_rt_sigreturn;
}
/* Create siginfo on the signal stack. */
@@ -405,3 +399,17 @@ long do_rt_sigreturn(CPUS390XState *env)
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 + 2, 0);
+ assert(tramp != NULL);
+
+ default_sigreturn = sigtramp_page;
+ __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, &tramp[0]);
+
+ default_rt_sigreturn = sigtramp_page + 2;
+ __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, &tramp[1]);
+
+ unlock_user(tramp, sigtramp_page, 2 + 2);
+}
diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h
index bbfc464..64f5f42 100644
--- a/linux-user/s390x/target_signal.h
+++ b/linux-user/s390x/target_signal.h
@@ -19,4 +19,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* S390X_TARGET_SIGNAL_H */
diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c
index d70d744..faa869f 100644
--- a/linux-user/sh4/signal.c
+++ b/linux-user/sh4/signal.c
@@ -52,7 +52,6 @@ struct target_sigframe
{
struct target_sigcontext sc;
target_ulong extramask[TARGET_NSIG_WORDS-1];
- uint16_t retcode[3];
};
@@ -68,7 +67,6 @@ struct target_rt_sigframe
{
struct target_siginfo info;
struct target_ucontext uc;
- uint16_t retcode[3];
};
@@ -190,15 +188,9 @@ void setup_frame(int sig, struct target_sigaction *ka,
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ka->sa_flags & TARGET_SA_RESTORER) {
- regs->pr = (unsigned long) ka->sa_restorer;
+ regs->pr = ka->sa_restorer;
} else {
- /* Generate return code (system call to sigreturn) */
- abi_ulong retcode_addr = frame_addr +
- offsetof(struct target_sigframe, retcode);
- __put_user(MOVW(2), &frame->retcode[0]);
- __put_user(TRAP_NOARG, &frame->retcode[1]);
- __put_user((TARGET_NR_sigreturn), &frame->retcode[2]);
- regs->pr = (unsigned long) retcode_addr;
+ regs->pr = default_sigreturn;
}
/* Set up registers for signal handler */
@@ -248,15 +240,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ka->sa_flags & TARGET_SA_RESTORER) {
- regs->pr = (unsigned long) ka->sa_restorer;
+ regs->pr = ka->sa_restorer;
} else {
- /* Generate return code (system call to sigreturn) */
- abi_ulong retcode_addr = frame_addr +
- offsetof(struct target_rt_sigframe, retcode);
- __put_user(MOVW(2), &frame->retcode[0]);
- __put_user(TRAP_NOARG, &frame->retcode[1]);
- __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]);
- regs->pr = (unsigned long) retcode_addr;
+ regs->pr = default_rt_sigreturn;
}
/* Set up registers for signal handler */
@@ -334,3 +320,21 @@ badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 6, 0);
+ assert(tramp != NULL);
+
+ default_sigreturn = sigtramp_page;
+ __put_user(MOVW(2), &tramp[0]);
+ __put_user(TRAP_NOARG, &tramp[1]);
+ __put_user(TARGET_NR_sigreturn, &tramp[2]);
+
+ default_rt_sigreturn = sigtramp_page + 6;
+ __put_user(MOVW(2), &tramp[3]);
+ __put_user(TRAP_NOARG, &tramp[4]);
+ __put_user(TARGET_NR_rt_sigreturn, &tramp[5]);
+
+ unlock_user(tramp, sigtramp_page, 2 * 6);
+}
diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h
index d7309b7..04069cb 100644
--- a/linux-user/sh4/target_signal.h
+++ b/linux-user/sh4/target_signal.h
@@ -22,4 +22,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif /* SH4_TARGET_SIGNAL_H */
diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h
index 79511be..7457f80 100644
--- a/linux-user/signal-common.h
+++ b/linux-user/signal-common.h
@@ -20,6 +20,12 @@
#ifndef SIGNAL_COMMON_H
#define SIGNAL_COMMON_H
+/* Fallback addresses into sigtramp page. */
+extern abi_ulong default_sigreturn;
+extern abi_ulong default_rt_sigreturn;
+
+void setup_sigtramp(abi_ulong tramp_page);
+
int on_sig_stack(unsigned long sp);
int sas_ss_flags(unsigned long sp);
abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 2038216..14d8fdf 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -35,6 +35,9 @@ static struct target_sigaction sigact_table[TARGET_NSIG];
static void host_signal_handler(int host_signum, siginfo_t *info,
void *puc);
+/* Fallback addresses into sigtramp page. */
+abi_ulong default_sigreturn;
+abi_ulong default_rt_sigreturn;
/*
* System includes define _NSIG as SIGRTMAX + 1,
diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c
index 3bc023d..23e1e76 100644
--- a/linux-user/sparc/signal.c
+++ b/linux-user/sparc/signal.c
@@ -242,6 +242,12 @@ static void restore_fpu(struct target_siginfo_fpu *fpu, CPUSPARCState *env)
}
#ifdef TARGET_ARCH_HAS_SETUP_FRAME
+static void install_sigtramp(uint32_t *tramp, int syscall)
+{
+ __put_user(0x82102000u + syscall, &tramp[0]); /* mov syscall, %g1 */
+ __put_user(0x91d02010u, &tramp[1]); /* t 0x10 */
+}
+
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUSPARCState *env)
{
@@ -291,13 +297,9 @@ void setup_frame(int sig, struct target_sigaction *ka,
if (ka->ka_restorer) {
env->regwptr[WREG_O7] = ka->ka_restorer;
} else {
- env->regwptr[WREG_O7] = sf_addr +
- offsetof(struct target_signal_frame, insns) - 2 * 4;
-
- /* mov __NR_sigreturn, %g1 */
- __put_user(0x821020d8u, &sf->insns[0]);
- /* t 0x10 */
- __put_user(0x91d02010u, &sf->insns[1]);
+ /* Not used, but retain for ABI compatibility. */
+ install_sigtramp(sf->insns, TARGET_NR_sigreturn);
+ env->regwptr[WREG_O7] = default_sigreturn;
}
unlock_user(sf, sf_addr, sf_size);
}
@@ -358,13 +360,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
if (ka->ka_restorer) {
env->regwptr[WREG_O7] = ka->ka_restorer;
} else {
- env->regwptr[WREG_O7] =
- sf_addr + offsetof(struct target_rt_signal_frame, insns) - 2 * 4;
-
- /* mov __NR_rt_sigreturn, %g1 */
- __put_user(0x82102065u, &sf->insns[0]);
- /* t 0x10 */
- __put_user(0x91d02010u, &sf->insns[1]);
+ /* Not used, but retain for ABI compatibility. */
+ install_sigtramp(sf->insns, TARGET_NR_rt_sigreturn);
+ env->regwptr[WREG_O7] = default_rt_sigreturn;
}
#else
env->regwptr[WREG_O7] = ka->ka_restorer;
@@ -775,4 +773,18 @@ do_sigsegv:
unlock_user_struct(ucp, ucp_addr, 1);
force_sig(TARGET_SIGSEGV);
}
+#else
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0);
+ assert(tramp != NULL);
+
+ default_sigreturn = sigtramp_page;
+ install_sigtramp(tramp, TARGET_NR_sigreturn);
+
+ default_rt_sigreturn = sigtramp_page + 8;
+ install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn);
+
+ unlock_user(tramp, sigtramp_page, 2 * 8);
+}
#endif
diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h
index 34f9a12..e661ddd 100644
--- a/linux-user/sparc/target_signal.h
+++ b/linux-user/sparc/target_signal.h
@@ -69,6 +69,10 @@ typedef struct target_sigaltstack {
#ifdef TARGET_ABI32
#define TARGET_ARCH_HAS_SETUP_FRAME
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+#else
+/* For sparc64, use of KA_RESTORER is mandatory. */
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0
#endif
/* bit-flags */
diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h
index 4ea74f2..4673c5a 100644
--- a/linux-user/x86_64/target_signal.h
+++ b/linux-user/x86_64/target_signal.h
@@ -21,4 +21,7 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
+/* For x86_64, use of SA_RESTORER is mandatory. */
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0
+
#endif /* X86_64_TARGET_SIGNAL_H */
diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c
index 7a3bfb9..81572a5 100644
--- a/linux-user/xtensa/signal.c
+++ b/linux-user/xtensa/signal.c
@@ -128,6 +128,29 @@ static int setup_sigcontext(struct target_rt_sigframe *frame,
return 1;
}
+static void install_sigtramp(uint8_t *tramp)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
+ __put_user(0x22, &tramp[0]);
+ __put_user(0x0a, &tramp[1]);
+ __put_user(TARGET_NR_rt_sigreturn, &tramp[2]);
+ /* Generate instruction: SYSCALL */
+ __put_user(0x00, &tramp[3]);
+ __put_user(0x05, &tramp[4]);
+ __put_user(0x00, &tramp[5]);
+#else
+ /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
+ __put_user(0x22, &tramp[0]);
+ __put_user(0xa0, &tramp[1]);
+ __put_user(TARGET_NR_rt_sigreturn, &tramp[2]);
+ /* Generate instruction: SYSCALL */
+ __put_user(0x00, &tramp[3]);
+ __put_user(0x50, &tramp[4]);
+ __put_user(0x00, &tramp[5]);
+#endif
+}
+
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUXtensaState *env)
@@ -164,26 +187,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
if (ka->sa_flags & TARGET_SA_RESTORER) {
ra = ka->sa_restorer;
} else {
- ra = frame_addr + offsetof(struct target_rt_sigframe, retcode);
-#ifdef TARGET_WORDS_BIGENDIAN
- /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
- __put_user(0x22, &frame->retcode[0]);
- __put_user(0x0a, &frame->retcode[1]);
- __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]);
- /* Generate instruction: SYSCALL */
- __put_user(0x00, &frame->retcode[3]);
- __put_user(0x05, &frame->retcode[4]);
- __put_user(0x00, &frame->retcode[5]);
-#else
- /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
- __put_user(0x22, &frame->retcode[0]);
- __put_user(0xa0, &frame->retcode[1]);
- __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]);
- /* Generate instruction: SYSCALL */
- __put_user(0x00, &frame->retcode[3]);
- __put_user(0x50, &frame->retcode[4]);
- __put_user(0x00, &frame->retcode[5]);
-#endif
+ /* Not used, but retain for ABI compatibility. */
+ install_sigtramp(frame->retcode);
+ ra = default_rt_sigreturn;
}
memset(env->regs, 0, sizeof(env->regs));
env->pc = ka->_sa_handler;
@@ -264,3 +270,13 @@ badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
+
+void setup_sigtramp(abi_ulong sigtramp_page)
+{
+ uint8_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0);
+ assert(tramp != NULL);
+
+ default_rt_sigreturn = sigtramp_page;
+ install_sigtramp(tramp);
+ unlock_user(tramp, sigtramp_page, 6);
+}
diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h
index c60bf65..1c7ee73 100644
--- a/linux-user/xtensa/target_signal.h
+++ b/linux-user/xtensa/target_signal.h
@@ -20,4 +20,6 @@ typedef struct target_sigaltstack {
#include "../generic/signal.h"
+#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
+
#endif
diff --git a/tests/tcg/hppa/Makefile.target b/tests/tcg/hppa/Makefile.target
index 473864d..d0d5e0e 100644
--- a/tests/tcg/hppa/Makefile.target
+++ b/tests/tcg/hppa/Makefile.target
@@ -5,3 +5,10 @@
# On parisc Linux supports 4K/16K/64K (but currently only 4k works)
EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-16384 run-test-mmap-65536
+# This triggers failures for hppa-linux about 1% of the time
+# HPPA is the odd target that can't use the sigtramp page;
+# it requires the full vdso with dwarf2 unwind info.
+run-signals: signals
+ $(call skip-test, $<, "BROKEN awaiting vdso support")
+run-plugin-signals-with-%:
+ $(call skip-test, $<, "BROKEN awaiting vdso support")
diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target
index a053ca3..38c1037 100644
--- a/tests/tcg/i386/Makefile.target
+++ b/tests/tcg/i386/Makefile.target
@@ -65,9 +65,6 @@ run-plugin-%-with-libinsn.so:
-d plugin -D $*-with-libinsn.so.pout $*, \
"$* (inline) on $(TARGET_NAME)")
-run-plugin-signals-with-libinsn.so:
- $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups and vdso support")
-
# Update TESTS
I386_TESTS:=$(filter-out $(SKIP_I386_TESTS), $(ALL_X86_TESTS))
TESTS=$(MULTIARCH_TESTS) $(I386_TESTS)
diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index 85a6fb7..3f283ea 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -32,14 +32,6 @@ threadcount: LDFLAGS+=-lpthread
signals: LDFLAGS+=-lrt -lpthread
-# This triggers failures on s390x hosts about 4% of the time
-# This triggers failures for hppa-linux about 1% of the time
-run-signals: signals
- $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups and vdso support")
-
-run-plugin-signals-with-%:
- $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups and vdso support")
-
# We define the runner for test-mmap after the individual
# architectures have defined their supported pages sizes. If no
# additional page sizes are defined we only run the default test.
diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target
index 9d18d44..47c39a4 100644
--- a/tests/tcg/sh4/Makefile.target
+++ b/tests/tcg/sh4/Makefile.target
@@ -5,3 +5,10 @@
# On sh Linux supports 4k, 8k, 16k and 64k pages (but only 4k currently works)
EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 run-test-mmap-16384 run-test-mmap-65536
+
+# This triggers failures for sh4-linux about 10% of the time.
+# Random SIGSEGV at unpredictable guest address, cause unknown.
+run-signals: signals
+ $(call skip-test, $<, "BROKEN")
+run-plugin-signals-with-%:
+ $(call skip-test, $<, "BROKEN")