On 9/24/21 18:59, Richard Henderson wrote:
> Create and record the two signal trampolines.
> Use them when the guest does not use SA_RESTORER.
> Note that x86_64 does not use this code.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
> linux-user/i386/target_signal.h | 2 ++
> linux-user/x86_64/target_signal.h | 3 ++
> linux-user/i386/signal.c | 56 +++++++++++++++++++++----------
> 3 files changed, 43 insertions(+), 18 deletions(-)
>
> diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h
> index 50361af874..64d09f2e75 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/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h
> index 4ea74f20dd..4673c5a886 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/i386/signal.c b/linux-user/i386/signal.c
> index 3b4b55fc0a..94ab6396a0 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, (int *)(tramp + 2));
I know this is mostly code movement, but using uint32_t would
make it easier to read.
> + __put_user(0x80cd, (uint16_t *)(tramp + 6));
> +}
> +
> +static void install_rt_sigtramp(void *tramp)
> +{
> + /* This is movl $syscall,%eax ; int $0x80 */
> + __put_user(0xb8, (char *)(tramp + 0));
Ditto uint8_t,
> + __put_user(TARGET_NR_rt_sigreturn, (int *)(tramp + 1));
and uint32_t.
> + __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 */
> @@ -416,14 +425,9 @@ void setup_rt_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;
> - 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));
> + /* This is no longer used, but is retained for ABI compatibility. */
> + install_rt_sigtramp(frame->retcode);
> + __put_user(default_rt_sigreturn, &frame->pretcode);
> }
> #else
> /* XXX: Would be slightly better to return -EFAULT here if test fails
> @@ -592,3 +596,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);
Shouldn't this be 8 + 7?
> + 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
>