better signal/exception support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@42 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
66fb9763af
commit
9de5e440b9
8
TODO
8
TODO
@ -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)
|
||||
|
13
cpu-i386.h
13
cpu-i386.h
@ -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
|
||||
|
173
exec-i386.c
173
exec-i386.c
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
48
op-i386.c
48
op-i386.c
@ -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;
|
||||
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user