better signal/exception support

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@42 c046a42c-6fe2-441c-8c8c-71466251a162
master
bellard 2003-03-23 16:49:39 +00:00
parent 66fb9763af
commit 9de5e440b9
15 changed files with 644 additions and 208 deletions

8
TODO
View File

@ -1,11 +1,9 @@
- asynchronous signal interrupt / clear synchronous signal handling
- add eflags restore in emulator
- finish signal handing (fp87 state)
- verify thread support (clone() and various locks)
- optimize translated cache chaining (DLL PLT-like system)
- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit issues)
- finish signal handing (fp87 state, more siginfo conversions)
- verify thread support (clone() and various locks)
- vm86 syscall support
- overrides/16bit for string ops
- more syscalls (in particular all 64 bit ones)
- make it self runnable (use same trick as ld.so : include its own relocator and libc)
- improved 16 bit support
- fix FPU exceptions (in particular: gen_op_fpush not before mem load)

View File

@ -68,7 +68,7 @@
#define EXCP11_ALGN 18
#define EXCP12_MCHK 19
#define EXCP_SIGNAL 256 /* async signal */
#define EXCP_INTERRUPT 256 /* async interruption */
enum {
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
@ -170,9 +170,10 @@ typedef struct CPUX86State {
/* various CPU modes */
int vm86;
/* exception handling */
/* exception/interrupt handling */
jmp_buf jmp_env;
int exception_index;
int interrupt_request;
} CPUX86State;
/* all CPU memory access use these macros */
@ -383,11 +384,19 @@ int cpu_x86_inl(int addr);
CPUX86State *cpu_x86_init(void);
int cpu_x86_exec(CPUX86State *s);
void cpu_x86_interrupt(CPUX86State *s);
void cpu_x86_close(CPUX86State *s);
/* needed to load some predefinied segment registers */
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
/* you can call these signal handler from you SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
struct siginfo;
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
void *puc);
/* internal functions */
#define GEN_FLAG_CODE32_SHIFT 0

View File

@ -21,6 +21,7 @@
//#define DEBUG_EXEC
#define DEBUG_FLUSH
//#define DEBUG_SIGNAL
/* main execution loop */
@ -98,7 +99,41 @@ void cpu_unlock(void)
global_cpu_lock = 0;
}
#ifdef DEBUG_EXEC
/* exception support */
/* NOTE: not static to force relocation generation by GCC */
void raise_exception(int exception_index)
{
/* NOTE: the register at this point must be saved by hand because
longjmp restore them */
#ifdef reg_EAX
env->regs[R_EAX] = EAX;
#endif
#ifdef reg_ECX
env->regs[R_ECX] = ECX;
#endif
#ifdef reg_EDX
env->regs[R_EDX] = EDX;
#endif
#ifdef reg_EBX
env->regs[R_EBX] = EBX;
#endif
#ifdef reg_ESP
env->regs[R_ESP] = ESP;
#endif
#ifdef reg_EBP
env->regs[R_EBP] = EBP;
#endif
#ifdef reg_ESI
env->regs[R_ESI] = ESI;
#endif
#ifdef reg_EDI
env->regs[R_EDI] = EDI;
#endif
env->exception_index = exception_index;
longjmp(env->jmp_env, 1);
}
#if defined(DEBUG_EXEC)
static const char *cc_op_str[] = {
"DYNAMIC",
"EFLAGS",
@ -132,15 +167,16 @@ static const char *cc_op_str[] = {
"SARL",
};
static void cpu_x86_dump_state(void)
static void cpu_x86_dump_state(FILE *f)
{
int eflags;
eflags = cc_table[CC_OP].compute_all();
eflags |= (DF & DIRECTION_FLAG);
fprintf(logfile,
fprintf(f,
"EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n"
"CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n",
"CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n"
"EIP=%08x\n",
env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX],
env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP],
env->cc_src, env->cc_dst, cc_op_str[env->cc_op],
@ -150,10 +186,10 @@ static void cpu_x86_dump_state(void)
eflags & CC_Z ? 'Z' : '-',
eflags & CC_A ? 'A' : '-',
eflags & CC_P ? 'P' : '-',
eflags & CC_C ? 'C' : '-'
);
eflags & CC_C ? 'C' : '-',
env->eip);
#if 1
fprintf(logfile, "ST0=%f ST1=%f ST2=%f ST3=%f\n",
fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n",
(double)ST0, (double)ST1, (double)ST(2), (double)ST(3));
#endif
}
@ -185,10 +221,11 @@ static void tb_flush(void)
}
/* find a translation block in the translation cache. If not found,
allocate a new one */
static inline TranslationBlock *tb_find_and_alloc(unsigned long pc,
unsigned long cs_base,
unsigned int flags)
return NULL and the pointer to the last element of the list in pptb */
static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
unsigned long pc,
unsigned long cs_base,
unsigned int flags)
{
TranslationBlock **ptb, *tb;
unsigned int h;
@ -203,16 +240,19 @@ static inline TranslationBlock *tb_find_and_alloc(unsigned long pc,
return tb;
ptb = &tb->hash_next;
}
*pptb = ptb;
return NULL;
}
/* allocate a new translation block. flush the translation buffer if
too many translation blocks or too much generated code */
static inline TranslationBlock *tb_alloc(void)
{
TranslationBlock *tb;
if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
(code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
tb_flush();
tb = &tbs[nb_tbs++];
*ptb = tb;
tb->pc = pc;
tb->cs_base = cs_base;
tb->flags = flags;
tb->tc_ptr = NULL;
tb->hash_next = NULL;
return tb;
}
@ -246,7 +286,7 @@ int cpu_x86_exec(CPUX86State *env1)
#endif
int code_gen_size, ret;
void (*gen_func)(void);
TranslationBlock *tb;
TranslationBlock *tb, **ptb;
uint8_t *tc_ptr, *cs_base, *pc;
unsigned int flags;
@ -289,12 +329,21 @@ int cpu_x86_exec(CPUX86State *env1)
EDI = env->regs[R_EDI];
#endif
/* put eflags in CPU temporary format */
T0 = env->eflags;
op_movl_eflags_T0();
CC_OP = CC_OP_EFLAGS;
env->interrupt_request = 0;
/* prepare setjmp context for exception handling */
if (setjmp(env->jmp_env) == 0) {
for(;;) {
if (env->interrupt_request) {
raise_exception(EXCP_INTERRUPT);
}
#ifdef DEBUG_EXEC
if (loglevel) {
cpu_x86_dump_state();
cpu_x86_dump_state(logfile);
}
#endif
/* we compute the CPU state. We assume it will not
@ -307,28 +356,43 @@ int cpu_x86_exec(CPUX86State *env1)
GEN_FLAG_ADDSEG_SHIFT;
cs_base = env->seg_cache[R_CS].base;
pc = cs_base + env->eip;
tb = tb_find_and_alloc((unsigned long)pc, (unsigned long)cs_base,
flags);
tc_ptr = tb->tc_ptr;
if (!tb->tc_ptr) {
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
flags);
if (!tb) {
/* if no translated code available, then translate it now */
/* XXX: very inefficient: we lock all the cpus when
generating code */
cpu_lock();
tc_ptr = code_gen_ptr;
cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
&code_gen_size, pc, cs_base, flags);
ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
&code_gen_size, pc, cs_base, flags);
/* if invalid instruction, signal it */
if (ret != 0) {
cpu_unlock();
raise_exception(EXCP06_ILLOP);
}
tb = tb_alloc();
*ptb = tb;
tb->pc = (unsigned long)pc;
tb->cs_base = (unsigned long)cs_base;
tb->flags = flags;
tb->tc_ptr = tc_ptr;
tb->hash_next = NULL;
code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
cpu_unlock();
}
/* execute the generated code */
tc_ptr = tb->tc_ptr;
gen_func = (void *)tc_ptr;
gen_func();
}
}
ret = env->exception_index;
/* restore flags in standard format */
op_movl_T0_eflags();
env->eflags = T0;
/* restore global registers */
#ifdef reg_EAX
EAX = saved_EAX;
@ -361,6 +425,12 @@ int cpu_x86_exec(CPUX86State *env1)
return ret;
}
void cpu_x86_interrupt(CPUX86State *s)
{
s->interrupt_request = 1;
}
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
{
CPUX86State *saved_env;
@ -370,3 +440,56 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
load_seg(seg_reg, selector);
env = saved_env;
}
#undef EAX
#undef ECX
#undef EDX
#undef EBX
#undef ESP
#undef EBP
#undef ESI
#undef EDI
#undef EIP
#include <signal.h>
#include <sys/ucontext.h>
static inline int handle_cpu_signal(unsigned long pc,
sigset_t *old_set)
{
#ifdef DEBUG_SIGNAL
printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n",
pc, *(unsigned long *)old_set);
#endif
if (pc >= (unsigned long)code_gen_buffer &&
pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) {
/* the PC is inside the translated code. It means that we have
a virtual CPU fault */
/* we restore the process signal mask as the sigreturn should
do it */
sigprocmask(SIG_SETMASK, old_set, NULL);
/* XXX: need to compute virtual pc position by retranslating
code. The rest of the CPU state should be correct. */
raise_exception(EXCP0D_GPF);
/* never comes here */
return 1;
} else {
return 0;
}
}
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
void *puc)
{
#if defined(__i386__)
struct ucontext *uc = puc;
unsigned long pc;
sigset_t *pold_set;
pc = uc->uc_mcontext.gregs[EIP];
pold_set = &uc->uc_sigmask;
return handle_cpu_signal(pc, pold_set);
#else
#warning No CPU specific signal handler: cannot handle target SIGSEGV events
return 0;
#endif
}

View File

@ -141,3 +141,7 @@ extern CCTable cc_table[];
void load_seg(int seg_reg, int selector);
void cpu_lock(void);
void cpu_unlock(void);
void raise_exception(int exception_index);
void OPPROTO op_movl_eflags_T0(void);
void OPPROTO op_movl_T0_eflags(void);

View File

@ -261,6 +261,9 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm,
/* Create enough stack to hold everything. If we don't use
* it for args, we'll use it for something else...
*/
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
we allocate a bigger stack. Need a better solution, for example
by remapping the process stack directly at the right place */
if(x86_stack_size > MAX_ARG_PAGES*X86_PAGE_SIZE) {
if((long)mmap4k((void *)(X86_STACK_TOP-x86_stack_size), x86_stack_size + X86_PAGE_SIZE,
PROT_READ | PROT_WRITE,

View File

@ -1,7 +1,7 @@
/* emulated ioctl list */
IOCTL(TCGETS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCGETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCSETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCSETSF, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCSETSW, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TIOCGWINSZ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_winsize)))
@ -199,8 +199,12 @@
IOCTL(SNDCTL_TMR_METRONOME, IOC_W, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_TMR_SELECT, IOC_W, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_TMR_SOURCE, IOC_RW, MK_PTR(TYPE_INT))
#if 0
/* we invalidate these defines because they have a same number as
termios ioctls */
IOCTL(SNDCTL_TMR_START, 0, TYPE_NULL)
IOCTL(SNDCTL_TMR_STOP, 0, TYPE_NULL)
#endif
IOCTL(SNDCTL_TMR_TEMPO, IOC_RW, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_TMR_TIMEBASE, IOC_RW, MK_PTR(TYPE_INT))

View File

@ -33,7 +33,10 @@
FILE *logfile = NULL;
int loglevel;
unsigned long x86_stack_size;
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
we allocate a bigger stack. Need a better solution, for example
by remapping the process stack directly at the right place */
unsigned long x86_stack_size = 512 * 1024;
unsigned long stktop;
void gemu_log(const char *fmt, ...)
@ -102,10 +105,11 @@ uint64_t gdt_table[6];
void cpu_loop(struct CPUX86State *env)
{
int err;
uint8_t *pc;
target_siginfo_t info;
for(;;) {
int err;
uint8_t *pc;
err = cpu_x86_exec(env);
pc = env->seg_cache[R_CS].base + env->eip;
switch(err) {
@ -122,12 +126,42 @@ void cpu_loop(struct CPUX86State *env)
env->regs[R_EDI],
env->regs[R_EBP]);
} else {
goto trap_error;
/* XXX: more precise info */
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = 0;
info._sifields._sigfault._addr = 0;
queue_signal(info.si_signo, &info);
}
break;
case EXCP00_DIVZ:
/* division by zero */
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = TARGET_FPE_INTDIV;
info._sifields._sigfault._addr = env->eip;
queue_signal(info.si_signo, &info);
break;
case EXCP04_INTO:
case EXCP05_BOUND:
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = 0;
info._sifields._sigfault._addr = 0;
queue_signal(info.si_signo, &info);
break;
case EXCP06_ILLOP:
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->eip;
queue_signal(info.si_signo, &info);
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
default:
trap_error:
fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n",
fprintf(stderr, "0x%08lx: Unknown exception CPU %d, aborting\n",
(long)pc, err);
abort();
}
@ -144,6 +178,9 @@ void usage(void)
exit(1);
}
/* XXX: currently only used for async signals (see signal.c) */
CPUX86State *global_env;
int main(int argc, char **argv)
{
const char *filename;
@ -199,6 +236,7 @@ int main(int argc, char **argv)
signal_init();
env = cpu_x86_init();
global_env = env;
/* linux register setup */
env->regs[R_EAX] = regs->eax;

View File

@ -3,30 +3,12 @@
#include "thunk.h"
#include <signal.h>
#include "syscall_defs.h"
#ifdef TARGET_I386
/* default linux values for the selectors */
#define __USER_CS (0x23)
#define __USER_DS (0x2B)
struct target_pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
long orig_eax;
long eip;
int xcs;
long eflags;
long esp;
int xss;
};
#include "cpu-i386.h"
#include "syscall-i386.h"
#endif
/* This struct is used to hold certain information about the image.
@ -59,9 +41,10 @@ void syscall_init(void);
long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
long arg4, long arg5, long arg6);
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
struct CPUX86State;
void cpu_loop(struct CPUX86State *env);
extern CPUX86State *global_env;
void cpu_loop(CPUX86State *env);
void process_pending_signals(void *cpu_env);
void signal_init(void);
int queue_signal(int sig, target_siginfo_t *info);
#endif

View File

@ -27,13 +27,6 @@
#include "gemu.h"
#include "syscall_defs.h"
#ifdef TARGET_I386
#include "cpu-i386.h"
#include "syscall-i386.h"
#endif
/* signal handling inspired from em86. */
//#define DEBUG_SIGNAL
@ -42,7 +35,7 @@
struct sigqueue {
struct sigqueue *next;
siginfo_t info;
target_siginfo_t info;
};
struct emulated_sigaction {
@ -101,20 +94,66 @@ void target_to_host_old_sigset(sigset_t *sigset,
*(unsigned long *)sigset = tswapl(*old_sigset);
}
/* XXX: finish it */
void host_to_target_siginfo(target_siginfo_t *tinfo, siginfo_t *info)
/* siginfo conversion */
static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
const siginfo_t *info)
{
tinfo->si_signo = tswap32(info->si_signo);
tinfo->si_errno = tswap32(info->si_errno);
tinfo->si_code = tswap32(info->si_code);
int sig;
sig = host_to_target_signal(info->si_signo);
tinfo->si_signo = sig;
tinfo->si_errno = 0;
tinfo->si_code = 0;
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) {
/* should never come here, but who knows. The information for
the target is irrelevant */
tinfo->_sifields._sigfault._addr = 0;
} else if (sig >= TARGET_SIGRTMIN) {
tinfo->_sifields._rt._pid = info->si_pid;
tinfo->_sifields._rt._uid = info->si_uid;
/* XXX: potential problem if 64 bit */
tinfo->_sifields._rt._sigval.sival_ptr =
(target_ulong)info->si_value.sival_ptr;
}
}
/* XXX: finish it */
void target_to_host_siginfo(siginfo_t *info, target_siginfo_t *tinfo)
static void tswap_siginfo(target_siginfo_t *tinfo,
const target_siginfo_t *info)
{
int sig;
sig = info->si_signo;
tinfo->si_signo = tswap32(sig);
tinfo->si_errno = tswap32(info->si_errno);
tinfo->si_code = tswap32(info->si_code);
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) {
tinfo->_sifields._sigfault._addr =
tswapl(info->_sifields._sigfault._addr);
} else if (sig >= TARGET_SIGRTMIN) {
tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid);
tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid);
tinfo->_sifields._rt._sigval.sival_ptr =
tswapl(info->_sifields._rt._sigval.sival_ptr);
}
}
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
{
host_to_target_siginfo_noswap(tinfo, info);
tswap_siginfo(tinfo, tinfo);
}
/* XXX: we support only POSIX RT signals are used. */
/* XXX: find a solution for 64 bit (additionnal malloced data is needed) */
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
{
info->si_signo = tswap32(tinfo->si_signo);
info->si_errno = tswap32(tinfo->si_errno);
info->si_code = tswap32(tinfo->si_code);
info->si_pid = tswap32(tinfo->_sifields._rt._pid);
info->si_uid = tswap32(tinfo->_sifields._rt._uid);
info->si_value.sival_ptr =
(void *)tswapl(tinfo->_sifields._rt._sigval.sival_ptr);
}
void signal_init(void)
@ -122,8 +161,9 @@ void signal_init(void)
struct sigaction act;
int i;
/* set all host signal handlers */
sigemptyset(&act.sa_mask);
/* set all host signal handlers. ALL signals are blocked during
the handlers to serialize them. */
sigfillset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = host_signal_handler;
for(i = 1; i < NSIG; i++) {
@ -155,56 +195,40 @@ static inline void free_sigqueue(struct sigqueue *q)
first_free = q;
}
static int queue_signal(struct emulated_sigaction *k, int sig, siginfo_t *info)
{
struct sigqueue *q, **pq;
pq = &k->first;
if (!k->pending || sig < TARGET_SIGRTMIN) {
/* first signal or non real time signal */
q = &k->info;
} else {
q = alloc_sigqueue();
if (!q)
return -EAGAIN;
while (*pq != NULL)
pq = &(*pq)->next;
}
*pq = q;
q->info = *info;
q->next = NULL;
k->pending = 1;
/* signal that a new signal is pending */
signal_pending = 1;
return 0;
}
void force_sig(int sig)
/* abort execution with signal */
void __attribute((noreturn)) force_sig(int sig)
{
int host_sig;
/* abort execution with signal */
host_sig = target_to_host_signal(sig);
fprintf(stderr, "gemu: uncaught target signal %d (%s) - exiting\n",
sig, strsignal(host_sig));
#if 1
_exit(-host_sig);
#else
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = SIG_DFL;
sigaction(SIGABRT, &act, NULL);
abort();
}
#endif
}
static void host_signal_handler(int host_signum, siginfo_t *info,
void *puc)
/* queue a signal so that it will be send to the virtual CPU as soon
as possible */
int queue_signal(int sig, target_siginfo_t *info)
{
struct emulated_sigaction *k;
int sig;
struct sigqueue *q, **pq;
target_ulong handler;
/* get target signal number */
sig = host_to_target_signal(host_signum);
if (sig < 1 || sig > TARGET_NSIG)
return;
k = &sigact_table[sig - 1];
#ifdef DEBUG_SIGNAL
fprintf(stderr, "gemu: got signal %d\n", sig);
#if defined(DEBUG_SIGNAL)
fprintf(stderr, "queue_sigal: sig=%d\n",
sig);
#endif
k = &sigact_table[sig - 1];
handler = k->sa._sa_handler;
if (handler == TARGET_SIG_DFL) {
/* default handler : ignore some signal. The other are fatal */
@ -212,13 +236,96 @@ static void host_signal_handler(int host_signum, siginfo_t *info,
sig != TARGET_SIGURG &&
sig != TARGET_SIGWINCH) {
force_sig(sig);
} else {
return 0; /* indicate ignored */
}
} else if (handler == TARGET_SIG_IGN) {
/* ignore signal */
return 0;
} else if (handler == TARGET_SIG_ERR) {
force_sig(sig);
} else {
queue_signal(k, sig, info);
pq = &k->first;
if (sig < TARGET_SIGRTMIN) {
/* if non real time signal, we queue exactly one signal */
if (!k->pending)
q = &k->info;
else
return 0;
} else {
if (!k->pending) {
/* first signal */
q = &k->info;
} else {
q = alloc_sigqueue();
if (!q)
return -EAGAIN;
while (*pq != NULL)
pq = &(*pq)->next;
}
}
*pq = q;
q->info = *info;
q->next = NULL;
k->pending = 1;
/* signal that a new signal is pending */
signal_pending = 1;
return 1; /* indicates that the signal was queued */
}
}
#if defined(DEBUG_SIGNAL)
#ifdef __i386__
static void dump_regs(struct ucontext *uc)
{
fprintf(stderr,
"EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
"EFL=%08x EIP=%08x\n",
uc->uc_mcontext.gregs[EAX],
uc->uc_mcontext.gregs[EBX],
uc->uc_mcontext.gregs[ECX],
uc->uc_mcontext.gregs[EDX],
uc->uc_mcontext.gregs[ESI],
uc->uc_mcontext.gregs[EDI],
uc->uc_mcontext.gregs[EBP],
uc->uc_mcontext.gregs[ESP],
uc->uc_mcontext.gregs[EFL],
uc->uc_mcontext.gregs[EIP]);
}
#else
static void dump_regs(struct ucontext *uc)
{
}
#endif
#endif
static void host_signal_handler(int host_signum, siginfo_t *info,
void *puc)
{
int sig;
target_siginfo_t tinfo;
/* the CPU emulator uses some host signals to detect exceptions,
we we forward to it some signals */
if (host_signum == SIGSEGV || host_signum == SIGBUS) {
if (cpu_x86_signal_handler(host_signum, info, puc))
return;
}
/* get target signal number */
sig = host_to_target_signal(host_signum);
if (sig < 1 || sig > TARGET_NSIG)
return;
#if defined(DEBUG_SIGNAL)
fprintf(stderr, "gemu: got signal %d\n", sig);
dump_regs(puc);
#endif
host_to_target_siginfo_noswap(&tinfo, info);
if (queue_signal(sig, &tinfo) == 1) {
/* interrupt the virtual CPU as soon as possible */
cpu_x86_interrupt(global_env);
}
}
@ -388,9 +495,10 @@ struct rt_sigframe
0;\
})
static inline int copy_siginfo_to_user(target_siginfo_t *tinfo, siginfo_t *info)
static inline int copy_siginfo_to_user(target_siginfo_t *tinfo,
const target_siginfo_t *info)
{
host_to_target_siginfo(tinfo, info);
tswap_siginfo(tinfo, info);
return 0;
}
@ -531,7 +639,8 @@ give_sigsegv:
force_sig(TARGET_SIGSEGV /* , current */);
}
static void setup_rt_frame(int sig, struct emulated_sigaction *ka, siginfo_t *info,
static void setup_rt_frame(int sig, struct emulated_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUX86State *env)
{
struct rt_sigframe *frame;
@ -734,7 +843,8 @@ void process_pending_signals(void *cpu_env)
{
int sig;
target_ulong handler;
target_sigset_t set;
sigset_t set, old_set;
target_sigset_t target_old_set;
struct emulated_sigaction *k;
struct sigqueue *q;
@ -774,12 +884,24 @@ void process_pending_signals(void *cpu_env)
} else if (handler == TARGET_SIG_ERR) {
force_sig(sig);
} else {
set = k->sa.sa_mask;
/* send the signal to the CPU */
/* compute the blocked signals during the handler execution */
target_to_host_sigset(&set, &k->sa.sa_mask);
/* SA_NODEFER indicates that the current signal should not be
blocked during the handler */
if (!(k->sa.sa_flags & TARGET_SA_NODEFER))
sigaddset(&set, target_to_host_signal(sig));
/* block signals in the handler using Linux */
sigprocmask(SIG_BLOCK, &set, &old_set);
/* save the previous blocked signal state to restore it at the
end of the signal execution (see do_sigreturn) */
host_to_target_sigset(&target_old_set, &old_set);
/* prepare the stack frame of the virtual CPU */
if (k->sa.sa_flags & TARGET_SA_SIGINFO)
setup_rt_frame(sig, k, &q->info, &set, cpu_env);
setup_rt_frame(sig, k, &q->info, &target_old_set, cpu_env);
else
setup_frame(sig, k, &set, cpu_env);
setup_frame(sig, k, &target_old_set, cpu_env);
if (k->sa.sa_flags & TARGET_SA_RESETHAND)
k->sa._sa_handler = TARGET_SIG_DFL;
}

View File

@ -38,6 +38,7 @@
#include <sched.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/poll.h>
//#include <sys/user.h>
#define termios host_termios
@ -68,15 +69,8 @@
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2])
#include "syscall_defs.h"
#ifdef TARGET_I386
#include "cpu-i386.h"
#include "syscall-i386.h"
#endif
void host_to_target_siginfo(target_siginfo_t *tinfo, siginfo_t *info);
void target_to_host_siginfo(siginfo_t *info, target_siginfo_t *tinfo);
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
long do_sigreturn(CPUX86State *env);
long do_rt_sigreturn(CPUX86State *env);
@ -106,6 +100,9 @@ _syscall2(int,sys_fstatfs,int,fd,struct kernel_statfs *,buf)
_syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo)
extern int personality(int);
extern int flock(int, int);
extern int setfsuid(int);
extern int setfsgid(int);
static inline long get_errno(long ret)
{
@ -437,7 +434,7 @@ static long do_ioctl(long fd, long cmd, long arg)
ie++;
}
arg_type = ie->arg_type;
#ifdef DEBUG
#if defined(DEBUG)
gemu_log("ioctl: cmd=0x%04lx (%s)\n", cmd, ie->name);
#endif
switch(arg_type[0]) {
@ -1244,9 +1241,30 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
ret = get_errno(sethostname((const char *)arg1, arg2));
break;
case TARGET_NR_setrlimit:
goto unimplemented;
{
/* XXX: convert resource ? */
int resource = arg1;
struct target_rlimit *target_rlim = (void *)arg2;
struct rlimit rlim;
rlim.rlim_cur = tswapl(target_rlim->rlim_cur);
rlim.rlim_max = tswapl(target_rlim->rlim_max);
ret = get_errno(setrlimit(resource, &rlim));
}
break;
case TARGET_NR_getrlimit:
goto unimplemented;
{
/* XXX: convert resource ? */
int resource = arg1;
struct target_rlimit *target_rlim = (void *)arg2;
struct rlimit rlim;
ret = get_errno(getrlimit(resource, &rlim));
if (!is_error(ret)) {
target_rlim->rlim_cur = tswapl(rlim.rlim_cur);
target_rlim->rlim_max = tswapl(rlim.rlim_max);
}
}
break;
case TARGET_NR_getrusage:
goto unimplemented;
case TARGET_NR_gettimeofday:
@ -1317,6 +1335,27 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
case TARGET_NR_munmap:
ret = get_errno(munmap((void *)arg1, arg2));
break;
case TARGET_NR_mprotect:
ret = get_errno(mprotect((void *)arg1, arg2, arg3));
break;
case TARGET_NR_mremap:
ret = get_errno((long)mremap((void *)arg1, arg2, arg3, arg4));
break;
case TARGET_NR_msync:
ret = get_errno(msync((void *)arg1, arg2, arg3));
break;
case TARGET_NR_mlock:
ret = get_errno(mlock((void *)arg1, arg2));
break;
case TARGET_NR_munlock:
ret = get_errno(munlock((void *)arg1, arg2));
break;
case TARGET_NR_mlockall:
ret = get_errno(mlockall(arg1));
break;
case TARGET_NR_munlockall:
ret = get_errno(munlockall());
break;
case TARGET_NR_truncate:
ret = get_errno(truncate((const char *)arg1, arg2));
break;
@ -1506,9 +1545,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
#endif
case TARGET_NR_adjtimex:
goto unimplemented;
case TARGET_NR_mprotect:
ret = get_errno(mprotect((void *)arg1, arg2, arg3));
break;
case TARGET_NR_create_module:
case TARGET_NR_init_module:
case TARGET_NR_delete_module:
@ -1532,9 +1568,11 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
case TARGET_NR_afs_syscall:
goto unimplemented;
case TARGET_NR_setfsuid:
goto unimplemented;
ret = get_errno(setfsuid(arg1));
break;
case TARGET_NR_setfsgid:
goto unimplemented;
ret = get_errno(setfsgid(arg1));
break;
case TARGET_NR__llseek:
{
int64_t res;
@ -1596,10 +1634,31 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
ret = do_select(arg1, (void *)arg2, (void *)arg3, (void *)arg4,
(void *)arg5);
break;
case TARGET_NR_poll:
{
struct target_pollfd *target_pfd = (void *)arg1;
unsigned int nfds = arg2;
int timeout = arg3;
struct pollfd *pfd;
int i;
pfd = alloca(sizeof(struct pollfd) * nfds);
for(i = 0; i < nfds; i++) {
pfd->fd = tswap32(target_pfd->fd);
pfd->events = tswap16(target_pfd->events);
}
ret = get_errno(poll(pfd, nfds, timeout));
if (!is_error(ret)) {
for(i = 0; i < nfds; i++) {
target_pfd->revents = tswap16(pfd->revents);
}
}
}
break;
case TARGET_NR_flock:
goto unimplemented;
case TARGET_NR_msync:
ret = get_errno(msync((void *)arg1, arg2, arg3));
/* NOTE: the flock constant seems to be the same for every
Linux platform */
ret = get_errno(flock(arg1, arg2));
break;
case TARGET_NR_readv:
{
@ -1638,18 +1697,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
goto unimplemented;
case TARGET_NR__sysctl:
goto unimplemented;
case TARGET_NR_mlock:
ret = get_errno(mlock((void *)arg1, arg2));
break;
case TARGET_NR_munlock:
ret = get_errno(munlock((void *)arg1, arg2));
break;
case TARGET_NR_mlockall:
ret = get_errno(mlockall(arg1));
break;
case TARGET_NR_munlockall:
ret = get_errno(munlockall());
break;
case TARGET_NR_sched_setparam:
goto unimplemented;
case TARGET_NR_sched_getparam:
@ -1681,12 +1728,10 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
}
break;
case TARGET_NR_mremap:
case TARGET_NR_setresuid:
case TARGET_NR_getresuid:
case TARGET_NR_vm86:
case TARGET_NR_query_module:
case TARGET_NR_poll:
case TARGET_NR_nfsservctl:
case TARGET_NR_setresgid:
case TARGET_NR_getresgid:
@ -1800,7 +1845,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
goto unimplemented;
default:
unimplemented:
gemu_log("Unsupported syscall: %d\n", num);
gemu_log("gemu: Unsupported syscall: %d\n", num);
ret = -ENOSYS;
break;
}

View File

@ -150,6 +150,17 @@ struct target_sigaction;
int do_sigaction(int sig, const struct target_sigaction *act,
struct target_sigaction *oact);
struct target_rlimit {
target_ulong rlim_cur;
target_ulong rlim_max;
};
struct target_pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
/* Networking ioctls */
#define TARGET_SIOCADDRT 0x890B /* add routing table entry */
#define TARGET_SIOCDELRT 0x890C /* delete routing table entry */

View File

@ -119,40 +119,6 @@ static inline int lshift(int x, int n)
return x >> (-n);
}
/* exception support */
/* NOTE: not static to force relocation generation by GCC */
void raise_exception(int exception_index)
{
/* NOTE: the register at this point must be saved by hand because
longjmp restore them */
#ifdef reg_EAX
env->regs[R_EAX] = EAX;
#endif
#ifdef reg_ECX
env->regs[R_ECX] = ECX;
#endif
#ifdef reg_EDX
env->regs[R_EDX] = EDX;
#endif
#ifdef reg_EBX
env->regs[R_EBX] = EBX;
#endif
#ifdef reg_ESP
env->regs[R_ESP] = ESP;
#endif
#ifdef reg_EBP
env->regs[R_EBP] = EBP;
#endif
#ifdef reg_ESI
env->regs[R_ESI] = ESI;
#endif
#ifdef reg_EDI
env->regs[R_EDI] = EDI;
#endif
env->exception_index = exception_index;
longjmp(env->jmp_env, 1);
}
/* we define the various pieces of code used by the JIT */
#define REG EAX
@ -391,13 +357,15 @@ void OPPROTO op_imull_T0_T1(void)
}
/* division, flags are undefined */
/* XXX: add exceptions for overflow & div by zero */
/* XXX: add exceptions for overflow */
void OPPROTO op_divb_AL_T0(void)
{
unsigned int num, den, q, r;
num = (EAX & 0xffff);
den = (T0 & 0xff);
if (den == 0)
raise_exception(EXCP00_DIVZ);
q = (num / den) & 0xff;
r = (num % den) & 0xff;
EAX = (EAX & 0xffff0000) | (r << 8) | q;
@ -409,6 +377,8 @@ void OPPROTO op_idivb_AL_T0(void)
num = (int16_t)EAX;
den = (int8_t)T0;
if (den == 0)
raise_exception(EXCP00_DIVZ);
q = (num / den) & 0xff;
r = (num % den) & 0xff;
EAX = (EAX & 0xffff0000) | (r << 8) | q;
@ -420,6 +390,8 @@ void OPPROTO op_divw_AX_T0(void)
num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
den = (T0 & 0xffff);
if (den == 0)
raise_exception(EXCP00_DIVZ);
q = (num / den) & 0xffff;
r = (num % den) & 0xffff;
EAX = (EAX & 0xffff0000) | q;
@ -432,6 +404,8 @@ void OPPROTO op_idivw_AX_T0(void)
num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
den = (int16_t)T0;
if (den == 0)
raise_exception(EXCP00_DIVZ);
q = (num / den) & 0xffff;
r = (num % den) & 0xffff;
EAX = (EAX & 0xffff0000) | q;
@ -445,6 +419,8 @@ void OPPROTO op_divl_EAX_T0(void)
num = EAX | ((uint64_t)EDX << 32);
den = T0;
if (den == 0)
raise_exception(EXCP00_DIVZ);
q = (num / den);
r = (num % den);
EAX = q;
@ -458,6 +434,8 @@ void OPPROTO op_idivl_EAX_T0(void)
num = EAX | ((uint64_t)EDX << 32);
den = T0;
if (den == 0)
raise_exception(EXCP00_DIVZ);
q = (num / den);
r = (num % den);
EAX = q;

View File

@ -359,7 +359,7 @@ struct target_sigaction {
typedef union target_sigval {
int sival_int;
void *sival_ptr;
target_ulong sival_ptr;
} target_sigval_t;
#define TARGET_SI_MAX_SIZE 128
@ -389,7 +389,7 @@ typedef struct target_siginfo {
struct {
pid_t _pid; /* sender's pid */
uid_t _uid; /* sender's uid */
sigval_t _sigval;
target_sigval_t _sigval;
} _rt;
/* SIGCHLD */
@ -403,7 +403,7 @@ typedef struct target_siginfo {
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
struct {
void *_addr; /* faulting insn/memory ref. */
target_ulong _addr; /* faulting insn/memory ref. */
} _sigfault;
/* SIGPOLL */
@ -414,6 +414,46 @@ typedef struct target_siginfo {
} _sifields;
} target_siginfo_t;
/*
* SIGILL si_codes
*/
#define TARGET_ILL_ILLOPN (2) /* illegal operand */
/*
* SIGFPE si_codes
*/
#define TARGET_FPE_INTDIV (1) /* integer divide by zero */
#define TARGET_FPE_INTOVF (2) /* integer overflow */
#define TARGET_FPE_FLTDIV (3) /* floating point divide by zero */
#define TARGET_FPE_FLTOVF (4) /* floating point overflow */
#define TARGET_FPE_FLTUND (5) /* floating point underflow */
#define TARGET_FPE_FLTRES (6) /* floating point inexact result */
#define TARGET_FPE_FLTINV (7) /* floating point invalid operation */
#define TARGET_FPE_FLTSUB (8) /* subscript out of range */
#define TARGET_NSIGFPE 8
/* default linux values for the selectors */
#define __USER_CS (0x23)
#define __USER_DS (0x2B)
struct target_pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
long orig_eax;
long eip;
int xcs;
long eflags;
long esp;
int xss;
};
/* ioctls */
/*

View File

@ -1,7 +1,13 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
#include <sys/ucontext.h>
jmp_buf jmp_env;
void alarm_handler(int sig)
{
@ -9,15 +15,82 @@ void alarm_handler(int sig)
alarm(1);
}
void dump_regs(struct ucontext *uc)
{
printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
"EFL=%08x EIP=%08x\n",
uc->uc_mcontext.gregs[EAX],
uc->uc_mcontext.gregs[EBX],
uc->uc_mcontext.gregs[ECX],
uc->uc_mcontext.gregs[EDX],
uc->uc_mcontext.gregs[ESI],
uc->uc_mcontext.gregs[EDI],
uc->uc_mcontext.gregs[EBP],
uc->uc_mcontext.gregs[ESP],
uc->uc_mcontext.gregs[EFL],
uc->uc_mcontext.gregs[EIP]);
}
void sig_handler(int sig, siginfo_t *info, void *puc)
{
struct ucontext *uc = puc;
printf("%s: si_signo=%d si_errno=%d si_code=%d si_addr=0x%08lx\n",
strsignal(info->si_signo),
info->si_signo, info->si_errno, info->si_code,
(unsigned long)info->si_addr);
dump_regs(uc);
longjmp(jmp_env, 1);
}
int v1;
int main(int argc, char **argv)
{
struct sigaction act;
int i;
/* test division by zero reporting */
if (setjmp(jmp_env) == 0) {
act.sa_sigaction = sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_ONESHOT;
sigaction(SIGFPE, &act, NULL);
/* now divide by zero */
v1 = 0;
v1 = 2 / v1;
}
/* test illegal instruction reporting */
if (setjmp(jmp_env) == 0) {
act.sa_sigaction = sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_ONESHOT;
sigaction(SIGILL, &act, NULL);
/* now execute an invalid instruction */
asm volatile("ud2");
}
/* test SEGV reporting */
if (setjmp(jmp_env) == 0) {
act.sa_sigaction = sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_ONESHOT;
sigaction(SIGSEGV, &act, NULL);
/* now store in an invalid address */
*(char *)0x1234 = 1;
}
act.sa_handler = alarm_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM, &act, NULL);
alarm(1);
for(;;) {
for(i = 0;i < 2; i++) {
sleep(1);
}
return 0;

View File

@ -22,6 +22,7 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <signal.h>
#include <assert.h>
#define DEBUG_DISAS
@ -3487,7 +3488,8 @@ static void dump_ops(const uint16_t *opc_buf)
static uint16_t gen_opc_buf[OPC_BUF_SIZE];
static uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE];
/* return the next pc */
/* return non zero if the very first instruction is invalid so that
the virtual CPU can trigger an exception. */
int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
int *gen_code_size_ptr,
uint8_t *pc_start, uint8_t *cs_base, int flags)
@ -3519,9 +3521,13 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
do {
ret = disas_insn(dc, pc_ptr);
if (ret == -1) {
fprintf(stderr, "unknown instruction at PC=0x%08lx B=%02x %02x %02x",
(long)pc_ptr, pc_ptr[0], pc_ptr[1], pc_ptr[2]);
abort();
/* we trigger an illegal instruction operation only if it
is the first instruction. Otherwise, we simply stop
generating the code just before it */
if (pc_ptr == pc_start)
return -1;
else
break;
}
pc_ptr = (void *)ret;
} while (!dc->is_jmp && gen_opc_ptr < gen_opc_end);
@ -3640,8 +3646,7 @@ CPUX86State *cpu_x86_init(void)
env->fptags[i] = 1;
env->fpuc = 0x37f;
/* flags setup */
env->cc_op = CC_OP_EFLAGS;
env->df = 1;
env->eflags = 0;
/* init various static tables */
if (!inited) {