From 4f5a466765092feea4a2224ec16896ec2ef392ff Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 13 Jun 2016 17:50:26 +0100 Subject: linux-user: Implement safe_syscall for i386 Implement the host assembly fragment needed for safe_syscall for 32-bit x86. Signed-off-by: Peter Maydell --- linux-user/host/i386/hostdep.h | 28 +++++++ linux-user/host/i386/safe-syscall.inc.S | 134 ++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 linux-user/host/i386/safe-syscall.inc.S (limited to 'linux-user/host/i386') diff --git a/linux-user/host/i386/hostdep.h b/linux-user/host/i386/hostdep.h index 7609bf5cd7..ee6a810be5 100644 --- a/linux-user/host/i386/hostdep.h +++ b/linux-user/host/i386/hostdep.h @@ -12,4 +12,32 @@ #ifndef QEMU_HOSTDEP_H #define QEMU_HOSTDEP_H +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#ifndef __ASSEMBLER__ + +/* These are defined by the safe-syscall.inc.S file */ +extern char safe_syscall_start[]; +extern char safe_syscall_end[]; + +/* For glibc 2.1 */ +#ifndef REG_EIP +#define REG_EIP EIP +#endif + +/* Adjust the signal context to rewind out of safe-syscall if we're in it */ +static inline void rewind_if_in_safe_syscall(void *puc) +{ + struct ucontext *uc = puc; + greg_t *pcreg = &uc->uc_mcontext.gregs[REG_EIP]; + + if (*pcreg > (uintptr_t)safe_syscall_start + && *pcreg < (uintptr_t)safe_syscall_end) { + *pcreg = (uintptr_t)safe_syscall_start; + } +} + +#endif /* __ASSEMBLER__ */ + #endif diff --git a/linux-user/host/i386/safe-syscall.inc.S b/linux-user/host/i386/safe-syscall.inc.S new file mode 100644 index 0000000000..9d98332148 --- /dev/null +++ b/linux-user/host/i386/safe-syscall.inc.S @@ -0,0 +1,134 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * * Written by Peter Maydell + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .cfi_startproc + /* Save ebx esi edi ebp, as the calling convention requires */ + pushl %ebp + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ebp, 0 + pushl %edi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edi, 0 + pushl %esi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset esi, 0 + pushl %ebx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ebx, 0 + + /* The syscall calling convention isn't the same as the + * C one: + * we enter with all arguments on the stack: + * *signal_pending + * syscall number + * syscall arguments + * and return the result in eax + * and the syscall instruction needs + * eax == syscall number + * ebx, ecx, edx, esi, edi, ebp == syscall arguments + * and returns the result in eax + * Shuffle everything around appropriately. + * + * At this point the stack layout is: + * 48(%esp) syscall arg 6 + * 44(%esp) syscall arg 5 + * 40(%esp) syscall arg 4 + * 36(%esp) syscall arg 3 + * 32(%esp) syscall arg 2 + * 28(%esp) syscall arg 1 + * 24(%esp) syscall number + * 20(%esp) *signal_pending + * 16(%esp) return address + * 12(%esp) saved EBP + * 8(%esp) saved EDI + * 4(%esp) saved ESI + * (%esp) saved EBX + */ + /* syscall arguments */ + movl 48(%esp), %ebp + movl 44(%esp), %edi + movl 40(%esp), %esi + movl 36(%esp), %edx + movl 32(%esp), %ecx + movl 28(%esp), %ebx + + /* This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + * For i386 we have absolutely no spare registers at the point of + * the syscall, so we must load *signal_pending and the syscall + * number off the stack inside the start/end section. + */ +safe_syscall_start: + /* *signal_pending */ + movl 20(%esp), %eax + /* if signal_pending is non-zero, don't do the call */ + testl $1, (%eax) + jnz return_ERESTARTSYS + movl 24(%esp), %eax + int $0x80 +safe_syscall_end: + /* code path for having successfully executed the syscall */ + .cfi_remember_state + popl %ebx + .cfi_adjust_cfa_offset -4 + .cfi_restore ebx + popl %esi + .cfi_adjust_cfa_offset -4 + .cfi_restore esi + popl %edi + .cfi_adjust_cfa_offset -4 + .cfi_restore edi + popl %ebp + .cfi_adjust_cfa_offset -4 + .cfi_restore ebp + ret + +return_ERESTARTSYS: + /* code path when we didn't execute the syscall */ + .cfi_restore_state + mov $-TARGET_ERESTARTSYS, %eax + popl %ebx + .cfi_adjust_cfa_offset -4 + .cfi_restore ebx + popl %esi + .cfi_adjust_cfa_offset -4 + .cfi_restore esi + popl %edi + .cfi_adjust_cfa_offset -4 + .cfi_restore edi + popl %ebp + .cfi_adjust_cfa_offset -4 + .cfi_restore ebp + ret + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base -- cgit v1.2.3