Commit 9de5e440b9f6a6c6305c0b81d1df4ddcc5a4b966
1 parent
66fb9763
better signal/exception support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@42 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
15 changed files
with
642 additions
and
206 deletions
TODO
1 | -- asynchronous signal interrupt / clear synchronous signal handling | |
2 | -- add eflags restore in emulator | |
3 | -- finish signal handing (fp87 state) | |
4 | -- verify thread support (clone() and various locks) | |
5 | 1 | - optimize translated cache chaining (DLL PLT-like system) |
2 | +- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit issues) | |
3 | +- finish signal handing (fp87 state, more siginfo conversions) | |
4 | +- verify thread support (clone() and various locks) | |
6 | 5 | - vm86 syscall support |
7 | 6 | - overrides/16bit for string ops |
8 | -- more syscalls (in particular all 64 bit ones) | |
9 | 7 | - make it self runnable (use same trick as ld.so : include its own relocator and libc) |
10 | 8 | - improved 16 bit support |
11 | 9 | - fix FPU exceptions (in particular: gen_op_fpush not before mem load) | ... | ... |
cpu-i386.h
... | ... | @@ -68,7 +68,7 @@ |
68 | 68 | #define EXCP11_ALGN 18 |
69 | 69 | #define EXCP12_MCHK 19 |
70 | 70 | |
71 | -#define EXCP_SIGNAL 256 /* async signal */ | |
71 | +#define EXCP_INTERRUPT 256 /* async interruption */ | |
72 | 72 | |
73 | 73 | enum { |
74 | 74 | CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ |
... | ... | @@ -170,9 +170,10 @@ typedef struct CPUX86State { |
170 | 170 | /* various CPU modes */ |
171 | 171 | int vm86; |
172 | 172 | |
173 | - /* exception handling */ | |
173 | + /* exception/interrupt handling */ | |
174 | 174 | jmp_buf jmp_env; |
175 | 175 | int exception_index; |
176 | + int interrupt_request; | |
176 | 177 | } CPUX86State; |
177 | 178 | |
178 | 179 | /* all CPU memory access use these macros */ |
... | ... | @@ -383,11 +384,19 @@ int cpu_x86_inl(int addr); |
383 | 384 | |
384 | 385 | CPUX86State *cpu_x86_init(void); |
385 | 386 | int cpu_x86_exec(CPUX86State *s); |
387 | +void cpu_x86_interrupt(CPUX86State *s); | |
386 | 388 | void cpu_x86_close(CPUX86State *s); |
387 | 389 | |
388 | 390 | /* needed to load some predefinied segment registers */ |
389 | 391 | void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector); |
390 | 392 | |
393 | +/* you can call these signal handler from you SIGBUS and SIGSEGV | |
394 | + signal handlers to inform the virtual CPU of exceptions. non zero | |
395 | + is returned if the signal was handled by the virtual CPU. */ | |
396 | +struct siginfo; | |
397 | +int cpu_x86_signal_handler(int host_signum, struct siginfo *info, | |
398 | + void *puc); | |
399 | + | |
391 | 400 | /* internal functions */ |
392 | 401 | |
393 | 402 | #define GEN_FLAG_CODE32_SHIFT 0 | ... | ... |
exec-i386.c
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | |
22 | 22 | //#define DEBUG_EXEC |
23 | 23 | #define DEBUG_FLUSH |
24 | +//#define DEBUG_SIGNAL | |
24 | 25 | |
25 | 26 | /* main execution loop */ |
26 | 27 | |
... | ... | @@ -98,7 +99,41 @@ void cpu_unlock(void) |
98 | 99 | global_cpu_lock = 0; |
99 | 100 | } |
100 | 101 | |
101 | -#ifdef DEBUG_EXEC | |
102 | +/* exception support */ | |
103 | +/* NOTE: not static to force relocation generation by GCC */ | |
104 | +void raise_exception(int exception_index) | |
105 | +{ | |
106 | + /* NOTE: the register at this point must be saved by hand because | |
107 | + longjmp restore them */ | |
108 | +#ifdef reg_EAX | |
109 | + env->regs[R_EAX] = EAX; | |
110 | +#endif | |
111 | +#ifdef reg_ECX | |
112 | + env->regs[R_ECX] = ECX; | |
113 | +#endif | |
114 | +#ifdef reg_EDX | |
115 | + env->regs[R_EDX] = EDX; | |
116 | +#endif | |
117 | +#ifdef reg_EBX | |
118 | + env->regs[R_EBX] = EBX; | |
119 | +#endif | |
120 | +#ifdef reg_ESP | |
121 | + env->regs[R_ESP] = ESP; | |
122 | +#endif | |
123 | +#ifdef reg_EBP | |
124 | + env->regs[R_EBP] = EBP; | |
125 | +#endif | |
126 | +#ifdef reg_ESI | |
127 | + env->regs[R_ESI] = ESI; | |
128 | +#endif | |
129 | +#ifdef reg_EDI | |
130 | + env->regs[R_EDI] = EDI; | |
131 | +#endif | |
132 | + env->exception_index = exception_index; | |
133 | + longjmp(env->jmp_env, 1); | |
134 | +} | |
135 | + | |
136 | +#if defined(DEBUG_EXEC) | |
102 | 137 | static const char *cc_op_str[] = { |
103 | 138 | "DYNAMIC", |
104 | 139 | "EFLAGS", |
... | ... | @@ -132,15 +167,16 @@ static const char *cc_op_str[] = { |
132 | 167 | "SARL", |
133 | 168 | }; |
134 | 169 | |
135 | -static void cpu_x86_dump_state(void) | |
170 | +static void cpu_x86_dump_state(FILE *f) | |
136 | 171 | { |
137 | 172 | int eflags; |
138 | 173 | eflags = cc_table[CC_OP].compute_all(); |
139 | 174 | eflags |= (DF & DIRECTION_FLAG); |
140 | - fprintf(logfile, | |
175 | + fprintf(f, | |
141 | 176 | "EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n" |
142 | 177 | "ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n" |
143 | - "CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n", | |
178 | + "CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n" | |
179 | + "EIP=%08x\n", | |
144 | 180 | env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX], |
145 | 181 | env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP], |
146 | 182 | env->cc_src, env->cc_dst, cc_op_str[env->cc_op], |
... | ... | @@ -150,10 +186,10 @@ static void cpu_x86_dump_state(void) |
150 | 186 | eflags & CC_Z ? 'Z' : '-', |
151 | 187 | eflags & CC_A ? 'A' : '-', |
152 | 188 | eflags & CC_P ? 'P' : '-', |
153 | - eflags & CC_C ? 'C' : '-' | |
154 | - ); | |
189 | + eflags & CC_C ? 'C' : '-', | |
190 | + env->eip); | |
155 | 191 | #if 1 |
156 | - fprintf(logfile, "ST0=%f ST1=%f ST2=%f ST3=%f\n", | |
192 | + fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n", | |
157 | 193 | (double)ST0, (double)ST1, (double)ST(2), (double)ST(3)); |
158 | 194 | #endif |
159 | 195 | } |
... | ... | @@ -185,10 +221,11 @@ static void tb_flush(void) |
185 | 221 | } |
186 | 222 | |
187 | 223 | /* find a translation block in the translation cache. If not found, |
188 | - allocate a new one */ | |
189 | -static inline TranslationBlock *tb_find_and_alloc(unsigned long pc, | |
190 | - unsigned long cs_base, | |
191 | - unsigned int flags) | |
224 | + return NULL and the pointer to the last element of the list in pptb */ | |
225 | +static inline TranslationBlock *tb_find(TranslationBlock ***pptb, | |
226 | + unsigned long pc, | |
227 | + unsigned long cs_base, | |
228 | + unsigned int flags) | |
192 | 229 | { |
193 | 230 | TranslationBlock **ptb, *tb; |
194 | 231 | unsigned int h; |
... | ... | @@ -203,16 +240,19 @@ static inline TranslationBlock *tb_find_and_alloc(unsigned long pc, |
203 | 240 | return tb; |
204 | 241 | ptb = &tb->hash_next; |
205 | 242 | } |
243 | + *pptb = ptb; | |
244 | + return NULL; | |
245 | +} | |
246 | + | |
247 | +/* allocate a new translation block. flush the translation buffer if | |
248 | + too many translation blocks or too much generated code */ | |
249 | +static inline TranslationBlock *tb_alloc(void) | |
250 | +{ | |
251 | + TranslationBlock *tb; | |
206 | 252 | if (nb_tbs >= CODE_GEN_MAX_BLOCKS || |
207 | 253 | (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) |
208 | 254 | tb_flush(); |
209 | 255 | tb = &tbs[nb_tbs++]; |
210 | - *ptb = tb; | |
211 | - tb->pc = pc; | |
212 | - tb->cs_base = cs_base; | |
213 | - tb->flags = flags; | |
214 | - tb->tc_ptr = NULL; | |
215 | - tb->hash_next = NULL; | |
216 | 256 | return tb; |
217 | 257 | } |
218 | 258 | |
... | ... | @@ -246,7 +286,7 @@ int cpu_x86_exec(CPUX86State *env1) |
246 | 286 | #endif |
247 | 287 | int code_gen_size, ret; |
248 | 288 | void (*gen_func)(void); |
249 | - TranslationBlock *tb; | |
289 | + TranslationBlock *tb, **ptb; | |
250 | 290 | uint8_t *tc_ptr, *cs_base, *pc; |
251 | 291 | unsigned int flags; |
252 | 292 | |
... | ... | @@ -289,12 +329,21 @@ int cpu_x86_exec(CPUX86State *env1) |
289 | 329 | EDI = env->regs[R_EDI]; |
290 | 330 | #endif |
291 | 331 | |
332 | + /* put eflags in CPU temporary format */ | |
333 | + T0 = env->eflags; | |
334 | + op_movl_eflags_T0(); | |
335 | + CC_OP = CC_OP_EFLAGS; | |
336 | + env->interrupt_request = 0; | |
337 | + | |
292 | 338 | /* prepare setjmp context for exception handling */ |
293 | 339 | if (setjmp(env->jmp_env) == 0) { |
294 | 340 | for(;;) { |
341 | + if (env->interrupt_request) { | |
342 | + raise_exception(EXCP_INTERRUPT); | |
343 | + } | |
295 | 344 | #ifdef DEBUG_EXEC |
296 | 345 | if (loglevel) { |
297 | - cpu_x86_dump_state(); | |
346 | + cpu_x86_dump_state(logfile); | |
298 | 347 | } |
299 | 348 | #endif |
300 | 349 | /* we compute the CPU state. We assume it will not |
... | ... | @@ -307,28 +356,43 @@ int cpu_x86_exec(CPUX86State *env1) |
307 | 356 | GEN_FLAG_ADDSEG_SHIFT; |
308 | 357 | cs_base = env->seg_cache[R_CS].base; |
309 | 358 | pc = cs_base + env->eip; |
310 | - tb = tb_find_and_alloc((unsigned long)pc, (unsigned long)cs_base, | |
311 | - flags); | |
312 | - tc_ptr = tb->tc_ptr; | |
313 | - if (!tb->tc_ptr) { | |
359 | + tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base, | |
360 | + flags); | |
361 | + if (!tb) { | |
314 | 362 | /* if no translated code available, then translate it now */ |
315 | 363 | /* XXX: very inefficient: we lock all the cpus when |
316 | 364 | generating code */ |
317 | 365 | cpu_lock(); |
318 | 366 | tc_ptr = code_gen_ptr; |
319 | - cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, | |
320 | - &code_gen_size, pc, cs_base, flags); | |
367 | + ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, | |
368 | + &code_gen_size, pc, cs_base, flags); | |
369 | + /* if invalid instruction, signal it */ | |
370 | + if (ret != 0) { | |
371 | + cpu_unlock(); | |
372 | + raise_exception(EXCP06_ILLOP); | |
373 | + } | |
374 | + tb = tb_alloc(); | |
375 | + *ptb = tb; | |
376 | + tb->pc = (unsigned long)pc; | |
377 | + tb->cs_base = (unsigned long)cs_base; | |
378 | + tb->flags = flags; | |
321 | 379 | tb->tc_ptr = tc_ptr; |
380 | + tb->hash_next = NULL; | |
322 | 381 | code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); |
323 | 382 | cpu_unlock(); |
324 | 383 | } |
325 | 384 | /* execute the generated code */ |
385 | + tc_ptr = tb->tc_ptr; | |
326 | 386 | gen_func = (void *)tc_ptr; |
327 | 387 | gen_func(); |
328 | 388 | } |
329 | 389 | } |
330 | 390 | ret = env->exception_index; |
331 | 391 | |
392 | + /* restore flags in standard format */ | |
393 | + op_movl_T0_eflags(); | |
394 | + env->eflags = T0; | |
395 | + | |
332 | 396 | /* restore global registers */ |
333 | 397 | #ifdef reg_EAX |
334 | 398 | EAX = saved_EAX; |
... | ... | @@ -361,6 +425,12 @@ int cpu_x86_exec(CPUX86State *env1) |
361 | 425 | return ret; |
362 | 426 | } |
363 | 427 | |
428 | +void cpu_x86_interrupt(CPUX86State *s) | |
429 | +{ | |
430 | + s->interrupt_request = 1; | |
431 | +} | |
432 | + | |
433 | + | |
364 | 434 | void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) |
365 | 435 | { |
366 | 436 | CPUX86State *saved_env; |
... | ... | @@ -370,3 +440,56 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) |
370 | 440 | load_seg(seg_reg, selector); |
371 | 441 | env = saved_env; |
372 | 442 | } |
443 | + | |
444 | +#undef EAX | |
445 | +#undef ECX | |
446 | +#undef EDX | |
447 | +#undef EBX | |
448 | +#undef ESP | |
449 | +#undef EBP | |
450 | +#undef ESI | |
451 | +#undef EDI | |
452 | +#undef EIP | |
453 | +#include <signal.h> | |
454 | +#include <sys/ucontext.h> | |
455 | + | |
456 | +static inline int handle_cpu_signal(unsigned long pc, | |
457 | + sigset_t *old_set) | |
458 | +{ | |
459 | +#ifdef DEBUG_SIGNAL | |
460 | + printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n", | |
461 | + pc, *(unsigned long *)old_set); | |
462 | +#endif | |
463 | + if (pc >= (unsigned long)code_gen_buffer && | |
464 | + pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) { | |
465 | + /* the PC is inside the translated code. It means that we have | |
466 | + a virtual CPU fault */ | |
467 | + /* we restore the process signal mask as the sigreturn should | |
468 | + do it */ | |
469 | + sigprocmask(SIG_SETMASK, old_set, NULL); | |
470 | + /* XXX: need to compute virtual pc position by retranslating | |
471 | + code. The rest of the CPU state should be correct. */ | |
472 | + raise_exception(EXCP0D_GPF); | |
473 | + /* never comes here */ | |
474 | + return 1; | |
475 | + } else { | |
476 | + return 0; | |
477 | + } | |
478 | +} | |
479 | + | |
480 | +int cpu_x86_signal_handler(int host_signum, struct siginfo *info, | |
481 | + void *puc) | |
482 | +{ | |
483 | +#if defined(__i386__) | |
484 | + struct ucontext *uc = puc; | |
485 | + unsigned long pc; | |
486 | + sigset_t *pold_set; | |
487 | + | |
488 | + pc = uc->uc_mcontext.gregs[EIP]; | |
489 | + pold_set = &uc->uc_sigmask; | |
490 | + return handle_cpu_signal(pc, pold_set); | |
491 | +#else | |
492 | +#warning No CPU specific signal handler: cannot handle target SIGSEGV events | |
493 | + return 0; | |
494 | +#endif | |
495 | +} | ... | ... |
exec-i386.h
... | ... | @@ -141,3 +141,7 @@ extern CCTable cc_table[]; |
141 | 141 | void load_seg(int seg_reg, int selector); |
142 | 142 | void cpu_lock(void); |
143 | 143 | void cpu_unlock(void); |
144 | +void raise_exception(int exception_index); | |
145 | + | |
146 | +void OPPROTO op_movl_eflags_T0(void); | |
147 | +void OPPROTO op_movl_T0_eflags(void); | ... | ... |
linux-user/elfload.c
... | ... | @@ -261,6 +261,9 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm, |
261 | 261 | /* Create enough stack to hold everything. If we don't use |
262 | 262 | * it for args, we'll use it for something else... |
263 | 263 | */ |
264 | + /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so | |
265 | + we allocate a bigger stack. Need a better solution, for example | |
266 | + by remapping the process stack directly at the right place */ | |
264 | 267 | if(x86_stack_size > MAX_ARG_PAGES*X86_PAGE_SIZE) { |
265 | 268 | if((long)mmap4k((void *)(X86_STACK_TOP-x86_stack_size), x86_stack_size + X86_PAGE_SIZE, |
266 | 269 | PROT_READ | PROT_WRITE, | ... | ... |
linux-user/ioctls.h
1 | 1 | /* emulated ioctl list */ |
2 | 2 | |
3 | 3 | IOCTL(TCGETS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios))) |
4 | - IOCTL(TCGETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios))) | |
4 | + IOCTL(TCSETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios))) | |
5 | 5 | IOCTL(TCSETSF, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios))) |
6 | 6 | IOCTL(TCSETSW, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios))) |
7 | 7 | IOCTL(TIOCGWINSZ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_winsize))) |
... | ... | @@ -199,8 +199,12 @@ |
199 | 199 | IOCTL(SNDCTL_TMR_METRONOME, IOC_W, MK_PTR(TYPE_INT)) |
200 | 200 | IOCTL(SNDCTL_TMR_SELECT, IOC_W, MK_PTR(TYPE_INT)) |
201 | 201 | IOCTL(SNDCTL_TMR_SOURCE, IOC_RW, MK_PTR(TYPE_INT)) |
202 | +#if 0 | |
203 | + /* we invalidate these defines because they have a same number as | |
204 | + termios ioctls */ | |
202 | 205 | IOCTL(SNDCTL_TMR_START, 0, TYPE_NULL) |
203 | 206 | IOCTL(SNDCTL_TMR_STOP, 0, TYPE_NULL) |
207 | +#endif | |
204 | 208 | IOCTL(SNDCTL_TMR_TEMPO, IOC_RW, MK_PTR(TYPE_INT)) |
205 | 209 | IOCTL(SNDCTL_TMR_TIMEBASE, IOC_RW, MK_PTR(TYPE_INT)) |
206 | 210 | ... | ... |
linux-user/main.c
... | ... | @@ -33,7 +33,10 @@ |
33 | 33 | FILE *logfile = NULL; |
34 | 34 | int loglevel; |
35 | 35 | |
36 | -unsigned long x86_stack_size; | |
36 | +/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so | |
37 | + we allocate a bigger stack. Need a better solution, for example | |
38 | + by remapping the process stack directly at the right place */ | |
39 | +unsigned long x86_stack_size = 512 * 1024; | |
37 | 40 | unsigned long stktop; |
38 | 41 | |
39 | 42 | void gemu_log(const char *fmt, ...) |
... | ... | @@ -102,10 +105,11 @@ uint64_t gdt_table[6]; |
102 | 105 | |
103 | 106 | void cpu_loop(struct CPUX86State *env) |
104 | 107 | { |
108 | + int err; | |
109 | + uint8_t *pc; | |
110 | + target_siginfo_t info; | |
111 | + | |
105 | 112 | for(;;) { |
106 | - int err; | |
107 | - uint8_t *pc; | |
108 | - | |
109 | 113 | err = cpu_x86_exec(env); |
110 | 114 | pc = env->seg_cache[R_CS].base + env->eip; |
111 | 115 | switch(err) { |
... | ... | @@ -122,12 +126,42 @@ void cpu_loop(struct CPUX86State *env) |
122 | 126 | env->regs[R_EDI], |
123 | 127 | env->regs[R_EBP]); |
124 | 128 | } else { |
125 | - goto trap_error; | |
129 | + /* XXX: more precise info */ | |
130 | + info.si_signo = SIGSEGV; | |
131 | + info.si_errno = 0; | |
132 | + info.si_code = 0; | |
133 | + info._sifields._sigfault._addr = 0; | |
134 | + queue_signal(info.si_signo, &info); | |
126 | 135 | } |
127 | 136 | break; |
137 | + case EXCP00_DIVZ: | |
138 | + /* division by zero */ | |
139 | + info.si_signo = SIGFPE; | |
140 | + info.si_errno = 0; | |
141 | + info.si_code = TARGET_FPE_INTDIV; | |
142 | + info._sifields._sigfault._addr = env->eip; | |
143 | + queue_signal(info.si_signo, &info); | |
144 | + break; | |
145 | + case EXCP04_INTO: | |
146 | + case EXCP05_BOUND: | |
147 | + info.si_signo = SIGSEGV; | |
148 | + info.si_errno = 0; | |
149 | + info.si_code = 0; | |
150 | + info._sifields._sigfault._addr = 0; | |
151 | + queue_signal(info.si_signo, &info); | |
152 | + break; | |
153 | + case EXCP06_ILLOP: | |
154 | + info.si_signo = SIGILL; | |
155 | + info.si_errno = 0; | |
156 | + info.si_code = TARGET_ILL_ILLOPN; | |
157 | + info._sifields._sigfault._addr = env->eip; | |
158 | + queue_signal(info.si_signo, &info); | |
159 | + break; | |
160 | + case EXCP_INTERRUPT: | |
161 | + /* just indicate that signals should be handled asap */ | |
162 | + break; | |
128 | 163 | default: |
129 | - trap_error: | |
130 | - fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n", | |
164 | + fprintf(stderr, "0x%08lx: Unknown exception CPU %d, aborting\n", | |
131 | 165 | (long)pc, err); |
132 | 166 | abort(); |
133 | 167 | } |
... | ... | @@ -144,6 +178,9 @@ void usage(void) |
144 | 178 | exit(1); |
145 | 179 | } |
146 | 180 | |
181 | +/* XXX: currently only used for async signals (see signal.c) */ | |
182 | +CPUX86State *global_env; | |
183 | + | |
147 | 184 | int main(int argc, char **argv) |
148 | 185 | { |
149 | 186 | const char *filename; |
... | ... | @@ -199,6 +236,7 @@ int main(int argc, char **argv) |
199 | 236 | signal_init(); |
200 | 237 | |
201 | 238 | env = cpu_x86_init(); |
239 | + global_env = env; | |
202 | 240 | |
203 | 241 | /* linux register setup */ |
204 | 242 | env->regs[R_EAX] = regs->eax; | ... | ... |
linux-user/qemu.h
... | ... | @@ -3,30 +3,12 @@ |
3 | 3 | |
4 | 4 | #include "thunk.h" |
5 | 5 | |
6 | -#ifdef TARGET_I386 | |
7 | - | |
8 | -/* default linux values for the selectors */ | |
9 | -#define __USER_CS (0x23) | |
10 | -#define __USER_DS (0x2B) | |
11 | - | |
12 | -struct target_pt_regs { | |
13 | - long ebx; | |
14 | - long ecx; | |
15 | - long edx; | |
16 | - long esi; | |
17 | - long edi; | |
18 | - long ebp; | |
19 | - long eax; | |
20 | - int xds; | |
21 | - int xes; | |
22 | - long orig_eax; | |
23 | - long eip; | |
24 | - int xcs; | |
25 | - long eflags; | |
26 | - long esp; | |
27 | - int xss; | |
28 | -}; | |
6 | +#include <signal.h> | |
7 | +#include "syscall_defs.h" | |
29 | 8 | |
9 | +#ifdef TARGET_I386 | |
10 | +#include "cpu-i386.h" | |
11 | +#include "syscall-i386.h" | |
30 | 12 | #endif |
31 | 13 | |
32 | 14 | /* This struct is used to hold certain information about the image. |
... | ... | @@ -59,9 +41,10 @@ void syscall_init(void); |
59 | 41 | long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
60 | 42 | long arg4, long arg5, long arg6); |
61 | 43 | void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); |
62 | -struct CPUX86State; | |
63 | -void cpu_loop(struct CPUX86State *env); | |
44 | +extern CPUX86State *global_env; | |
45 | +void cpu_loop(CPUX86State *env); | |
64 | 46 | void process_pending_signals(void *cpu_env); |
65 | 47 | void signal_init(void); |
48 | +int queue_signal(int sig, target_siginfo_t *info); | |
66 | 49 | |
67 | 50 | #endif | ... | ... |
linux-user/signal.c
... | ... | @@ -27,13 +27,6 @@ |
27 | 27 | |
28 | 28 | #include "gemu.h" |
29 | 29 | |
30 | -#include "syscall_defs.h" | |
31 | - | |
32 | -#ifdef TARGET_I386 | |
33 | -#include "cpu-i386.h" | |
34 | -#include "syscall-i386.h" | |
35 | -#endif | |
36 | - | |
37 | 30 | /* signal handling inspired from em86. */ |
38 | 31 | |
39 | 32 | //#define DEBUG_SIGNAL |
... | ... | @@ -42,7 +35,7 @@ |
42 | 35 | |
43 | 36 | struct sigqueue { |
44 | 37 | struct sigqueue *next; |
45 | - siginfo_t info; | |
38 | + target_siginfo_t info; | |
46 | 39 | }; |
47 | 40 | |
48 | 41 | struct emulated_sigaction { |
... | ... | @@ -101,20 +94,66 @@ void target_to_host_old_sigset(sigset_t *sigset, |
101 | 94 | *(unsigned long *)sigset = tswapl(*old_sigset); |
102 | 95 | } |
103 | 96 | |
104 | -/* XXX: finish it */ | |
105 | -void host_to_target_siginfo(target_siginfo_t *tinfo, siginfo_t *info) | |
97 | +/* siginfo conversion */ | |
98 | + | |
99 | +static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, | |
100 | + const siginfo_t *info) | |
106 | 101 | { |
107 | - tinfo->si_signo = tswap32(info->si_signo); | |
102 | + int sig; | |
103 | + sig = host_to_target_signal(info->si_signo); | |
104 | + tinfo->si_signo = sig; | |
105 | + tinfo->si_errno = 0; | |
106 | + tinfo->si_code = 0; | |
107 | + if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) { | |
108 | + /* should never come here, but who knows. The information for | |
109 | + the target is irrelevant */ | |
110 | + tinfo->_sifields._sigfault._addr = 0; | |
111 | + } else if (sig >= TARGET_SIGRTMIN) { | |
112 | + tinfo->_sifields._rt._pid = info->si_pid; | |
113 | + tinfo->_sifields._rt._uid = info->si_uid; | |
114 | + /* XXX: potential problem if 64 bit */ | |
115 | + tinfo->_sifields._rt._sigval.sival_ptr = | |
116 | + (target_ulong)info->si_value.sival_ptr; | |
117 | + } | |
118 | +} | |
119 | + | |
120 | +static void tswap_siginfo(target_siginfo_t *tinfo, | |
121 | + const target_siginfo_t *info) | |
122 | +{ | |
123 | + int sig; | |
124 | + sig = info->si_signo; | |
125 | + tinfo->si_signo = tswap32(sig); | |
108 | 126 | tinfo->si_errno = tswap32(info->si_errno); |
109 | 127 | tinfo->si_code = tswap32(info->si_code); |
128 | + if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) { | |
129 | + tinfo->_sifields._sigfault._addr = | |
130 | + tswapl(info->_sifields._sigfault._addr); | |
131 | + } else if (sig >= TARGET_SIGRTMIN) { | |
132 | + tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid); | |
133 | + tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid); | |
134 | + tinfo->_sifields._rt._sigval.sival_ptr = | |
135 | + tswapl(info->_sifields._rt._sigval.sival_ptr); | |
136 | + } | |
137 | +} | |
138 | + | |
139 | + | |
140 | +void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info) | |
141 | +{ | |
142 | + host_to_target_siginfo_noswap(tinfo, info); | |
143 | + tswap_siginfo(tinfo, tinfo); | |
110 | 144 | } |
111 | 145 | |
112 | -/* XXX: finish it */ | |
113 | -void target_to_host_siginfo(siginfo_t *info, target_siginfo_t *tinfo) | |
146 | +/* XXX: we support only POSIX RT signals are used. */ | |
147 | +/* XXX: find a solution for 64 bit (additionnal malloced data is needed) */ | |
148 | +void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo) | |
114 | 149 | { |
115 | 150 | info->si_signo = tswap32(tinfo->si_signo); |
116 | 151 | info->si_errno = tswap32(tinfo->si_errno); |
117 | 152 | info->si_code = tswap32(tinfo->si_code); |
153 | + info->si_pid = tswap32(tinfo->_sifields._rt._pid); | |
154 | + info->si_uid = tswap32(tinfo->_sifields._rt._uid); | |
155 | + info->si_value.sival_ptr = | |
156 | + (void *)tswapl(tinfo->_sifields._rt._sigval.sival_ptr); | |
118 | 157 | } |
119 | 158 | |
120 | 159 | void signal_init(void) |
... | ... | @@ -122,8 +161,9 @@ void signal_init(void) |
122 | 161 | struct sigaction act; |
123 | 162 | int i; |
124 | 163 | |
125 | - /* set all host signal handlers */ | |
126 | - sigemptyset(&act.sa_mask); | |
164 | + /* set all host signal handlers. ALL signals are blocked during | |
165 | + the handlers to serialize them. */ | |
166 | + sigfillset(&act.sa_mask); | |
127 | 167 | act.sa_flags = SA_SIGINFO; |
128 | 168 | act.sa_sigaction = host_signal_handler; |
129 | 169 | for(i = 1; i < NSIG; i++) { |
... | ... | @@ -155,56 +195,40 @@ static inline void free_sigqueue(struct sigqueue *q) |
155 | 195 | first_free = q; |
156 | 196 | } |
157 | 197 | |
158 | -static int queue_signal(struct emulated_sigaction *k, int sig, siginfo_t *info) | |
159 | -{ | |
160 | - struct sigqueue *q, **pq; | |
161 | - | |
162 | - pq = &k->first; | |
163 | - if (!k->pending || sig < TARGET_SIGRTMIN) { | |
164 | - /* first signal or non real time signal */ | |
165 | - q = &k->info; | |
166 | - } else { | |
167 | - q = alloc_sigqueue(); | |
168 | - if (!q) | |
169 | - return -EAGAIN; | |
170 | - while (*pq != NULL) | |
171 | - pq = &(*pq)->next; | |
172 | - } | |
173 | - *pq = q; | |
174 | - q->info = *info; | |
175 | - q->next = NULL; | |
176 | - k->pending = 1; | |
177 | - /* signal that a new signal is pending */ | |
178 | - signal_pending = 1; | |
179 | - return 0; | |
180 | -} | |
181 | - | |
182 | -void force_sig(int sig) | |
198 | +/* abort execution with signal */ | |
199 | +void __attribute((noreturn)) force_sig(int sig) | |
183 | 200 | { |
184 | 201 | int host_sig; |
185 | - /* abort execution with signal */ | |
186 | 202 | host_sig = target_to_host_signal(sig); |
187 | 203 | fprintf(stderr, "gemu: uncaught target signal %d (%s) - exiting\n", |
188 | 204 | sig, strsignal(host_sig)); |
205 | +#if 1 | |
189 | 206 | _exit(-host_sig); |
207 | +#else | |
208 | + { | |
209 | + struct sigaction act; | |
210 | + sigemptyset(&act.sa_mask); | |
211 | + act.sa_flags = SA_SIGINFO; | |
212 | + act.sa_sigaction = SIG_DFL; | |
213 | + sigaction(SIGABRT, &act, NULL); | |
214 | + abort(); | |
215 | + } | |
216 | +#endif | |
190 | 217 | } |
191 | 218 | |
192 | - | |
193 | -static void host_signal_handler(int host_signum, siginfo_t *info, | |
194 | - void *puc) | |
219 | +/* queue a signal so that it will be send to the virtual CPU as soon | |
220 | + as possible */ | |
221 | +int queue_signal(int sig, target_siginfo_t *info) | |
195 | 222 | { |
196 | 223 | struct emulated_sigaction *k; |
197 | - int sig; | |
224 | + struct sigqueue *q, **pq; | |
198 | 225 | target_ulong handler; |
199 | 226 | |
200 | - /* get target signal number */ | |
201 | - sig = host_to_target_signal(host_signum); | |
202 | - if (sig < 1 || sig > TARGET_NSIG) | |
203 | - return; | |
204 | - k = &sigact_table[sig - 1]; | |
205 | -#ifdef DEBUG_SIGNAL | |
206 | - fprintf(stderr, "gemu: got signal %d\n", sig); | |
227 | +#if defined(DEBUG_SIGNAL) | |
228 | + fprintf(stderr, "queue_sigal: sig=%d\n", | |
229 | + sig); | |
207 | 230 | #endif |
231 | + k = &sigact_table[sig - 1]; | |
208 | 232 | handler = k->sa._sa_handler; |
209 | 233 | if (handler == TARGET_SIG_DFL) { |
210 | 234 | /* default handler : ignore some signal. The other are fatal */ |
... | ... | @@ -212,13 +236,96 @@ static void host_signal_handler(int host_signum, siginfo_t *info, |
212 | 236 | sig != TARGET_SIGURG && |
213 | 237 | sig != TARGET_SIGWINCH) { |
214 | 238 | force_sig(sig); |
239 | + } else { | |
240 | + return 0; /* indicate ignored */ | |
215 | 241 | } |
216 | 242 | } else if (handler == TARGET_SIG_IGN) { |
217 | 243 | /* ignore signal */ |
244 | + return 0; | |
218 | 245 | } else if (handler == TARGET_SIG_ERR) { |
219 | 246 | force_sig(sig); |
220 | 247 | } else { |
221 | - queue_signal(k, sig, info); | |
248 | + pq = &k->first; | |
249 | + if (sig < TARGET_SIGRTMIN) { | |
250 | + /* if non real time signal, we queue exactly one signal */ | |
251 | + if (!k->pending) | |
252 | + q = &k->info; | |
253 | + else | |
254 | + return 0; | |
255 | + } else { | |
256 | + if (!k->pending) { | |
257 | + /* first signal */ | |
258 | + q = &k->info; | |
259 | + } else { | |
260 | + q = alloc_sigqueue(); | |
261 | + if (!q) | |
262 | + return -EAGAIN; | |
263 | + while (*pq != NULL) | |
264 | + pq = &(*pq)->next; | |
265 | + } | |
266 | + } | |
267 | + *pq = q; | |
268 | + q->info = *info; | |
269 | + q->next = NULL; | |
270 | + k->pending = 1; | |
271 | + /* signal that a new signal is pending */ | |
272 | + signal_pending = 1; | |
273 | + return 1; /* indicates that the signal was queued */ | |
274 | + } | |
275 | +} | |
276 | + | |
277 | +#if defined(DEBUG_SIGNAL) | |
278 | +#ifdef __i386__ | |
279 | +static void dump_regs(struct ucontext *uc) | |
280 | +{ | |
281 | + fprintf(stderr, | |
282 | + "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" | |
283 | + "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" | |
284 | + "EFL=%08x EIP=%08x\n", | |
285 | + uc->uc_mcontext.gregs[EAX], | |
286 | + uc->uc_mcontext.gregs[EBX], | |
287 | + uc->uc_mcontext.gregs[ECX], | |
288 | + uc->uc_mcontext.gregs[EDX], | |
289 | + uc->uc_mcontext.gregs[ESI], | |
290 | + uc->uc_mcontext.gregs[EDI], | |
291 | + uc->uc_mcontext.gregs[EBP], | |
292 | + uc->uc_mcontext.gregs[ESP], | |
293 | + uc->uc_mcontext.gregs[EFL], | |
294 | + uc->uc_mcontext.gregs[EIP]); | |
295 | +} | |
296 | +#else | |
297 | +static void dump_regs(struct ucontext *uc) | |
298 | +{ | |
299 | +} | |
300 | +#endif | |
301 | + | |
302 | +#endif | |
303 | + | |
304 | +static void host_signal_handler(int host_signum, siginfo_t *info, | |
305 | + void *puc) | |
306 | +{ | |
307 | + int sig; | |
308 | + target_siginfo_t tinfo; | |
309 | + | |
310 | + /* the CPU emulator uses some host signals to detect exceptions, | |
311 | + we we forward to it some signals */ | |
312 | + if (host_signum == SIGSEGV || host_signum == SIGBUS) { | |
313 | + if (cpu_x86_signal_handler(host_signum, info, puc)) | |
314 | + return; | |
315 | + } | |
316 | + | |
317 | + /* get target signal number */ | |
318 | + sig = host_to_target_signal(host_signum); | |
319 | + if (sig < 1 || sig > TARGET_NSIG) | |
320 | + return; | |
321 | +#if defined(DEBUG_SIGNAL) | |
322 | + fprintf(stderr, "gemu: got signal %d\n", sig); | |
323 | + dump_regs(puc); | |
324 | +#endif | |
325 | + host_to_target_siginfo_noswap(&tinfo, info); | |
326 | + if (queue_signal(sig, &tinfo) == 1) { | |
327 | + /* interrupt the virtual CPU as soon as possible */ | |
328 | + cpu_x86_interrupt(global_env); | |
222 | 329 | } |
223 | 330 | } |
224 | 331 | |
... | ... | @@ -388,9 +495,10 @@ struct rt_sigframe |
388 | 495 | 0;\ |
389 | 496 | }) |
390 | 497 | |
391 | -static inline int copy_siginfo_to_user(target_siginfo_t *tinfo, siginfo_t *info) | |
498 | +static inline int copy_siginfo_to_user(target_siginfo_t *tinfo, | |
499 | + const target_siginfo_t *info) | |
392 | 500 | { |
393 | - host_to_target_siginfo(tinfo, info); | |
501 | + tswap_siginfo(tinfo, info); | |
394 | 502 | return 0; |
395 | 503 | } |
396 | 504 | |
... | ... | @@ -531,7 +639,8 @@ give_sigsegv: |
531 | 639 | force_sig(TARGET_SIGSEGV /* , current */); |
532 | 640 | } |
533 | 641 | |
534 | -static void setup_rt_frame(int sig, struct emulated_sigaction *ka, siginfo_t *info, | |
642 | +static void setup_rt_frame(int sig, struct emulated_sigaction *ka, | |
643 | + target_siginfo_t *info, | |
535 | 644 | target_sigset_t *set, CPUX86State *env) |
536 | 645 | { |
537 | 646 | struct rt_sigframe *frame; |
... | ... | @@ -734,7 +843,8 @@ void process_pending_signals(void *cpu_env) |
734 | 843 | { |
735 | 844 | int sig; |
736 | 845 | target_ulong handler; |
737 | - target_sigset_t set; | |
846 | + sigset_t set, old_set; | |
847 | + target_sigset_t target_old_set; | |
738 | 848 | struct emulated_sigaction *k; |
739 | 849 | struct sigqueue *q; |
740 | 850 | |
... | ... | @@ -774,12 +884,24 @@ void process_pending_signals(void *cpu_env) |
774 | 884 | } else if (handler == TARGET_SIG_ERR) { |
775 | 885 | force_sig(sig); |
776 | 886 | } else { |
777 | - set = k->sa.sa_mask; | |
778 | - /* send the signal to the CPU */ | |
887 | + /* compute the blocked signals during the handler execution */ | |
888 | + target_to_host_sigset(&set, &k->sa.sa_mask); | |
889 | + /* SA_NODEFER indicates that the current signal should not be | |
890 | + blocked during the handler */ | |
891 | + if (!(k->sa.sa_flags & TARGET_SA_NODEFER)) | |
892 | + sigaddset(&set, target_to_host_signal(sig)); | |
893 | + | |
894 | + /* block signals in the handler using Linux */ | |
895 | + sigprocmask(SIG_BLOCK, &set, &old_set); | |
896 | + /* save the previous blocked signal state to restore it at the | |
897 | + end of the signal execution (see do_sigreturn) */ | |
898 | + host_to_target_sigset(&target_old_set, &old_set); | |
899 | + | |
900 | + /* prepare the stack frame of the virtual CPU */ | |
779 | 901 | if (k->sa.sa_flags & TARGET_SA_SIGINFO) |
780 | - setup_rt_frame(sig, k, &q->info, &set, cpu_env); | |
902 | + setup_rt_frame(sig, k, &q->info, &target_old_set, cpu_env); | |
781 | 903 | else |
782 | - setup_frame(sig, k, &set, cpu_env); | |
904 | + setup_frame(sig, k, &target_old_set, cpu_env); | |
783 | 905 | if (k->sa.sa_flags & TARGET_SA_RESETHAND) |
784 | 906 | k->sa._sa_handler = TARGET_SIG_DFL; |
785 | 907 | } | ... | ... |
linux-user/syscall.c
... | ... | @@ -38,6 +38,7 @@ |
38 | 38 | #include <sched.h> |
39 | 39 | #include <sys/socket.h> |
40 | 40 | #include <sys/uio.h> |
41 | +#include <sys/poll.h> | |
41 | 42 | //#include <sys/user.h> |
42 | 43 | |
43 | 44 | #define termios host_termios |
... | ... | @@ -68,15 +69,8 @@ |
68 | 69 | #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2]) |
69 | 70 | #define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2]) |
70 | 71 | |
71 | -#include "syscall_defs.h" | |
72 | - | |
73 | -#ifdef TARGET_I386 | |
74 | -#include "cpu-i386.h" | |
75 | -#include "syscall-i386.h" | |
76 | -#endif | |
77 | - | |
78 | -void host_to_target_siginfo(target_siginfo_t *tinfo, siginfo_t *info); | |
79 | -void target_to_host_siginfo(siginfo_t *info, target_siginfo_t *tinfo); | |
72 | +void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info); | |
73 | +void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo); | |
80 | 74 | long do_sigreturn(CPUX86State *env); |
81 | 75 | long do_rt_sigreturn(CPUX86State *env); |
82 | 76 | |
... | ... | @@ -106,6 +100,9 @@ _syscall2(int,sys_fstatfs,int,fd,struct kernel_statfs *,buf) |
106 | 100 | _syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo) |
107 | 101 | |
108 | 102 | extern int personality(int); |
103 | +extern int flock(int, int); | |
104 | +extern int setfsuid(int); | |
105 | +extern int setfsgid(int); | |
109 | 106 | |
110 | 107 | static inline long get_errno(long ret) |
111 | 108 | { |
... | ... | @@ -437,7 +434,7 @@ static long do_ioctl(long fd, long cmd, long arg) |
437 | 434 | ie++; |
438 | 435 | } |
439 | 436 | arg_type = ie->arg_type; |
440 | -#ifdef DEBUG | |
437 | +#if defined(DEBUG) | |
441 | 438 | gemu_log("ioctl: cmd=0x%04lx (%s)\n", cmd, ie->name); |
442 | 439 | #endif |
443 | 440 | switch(arg_type[0]) { |
... | ... | @@ -1244,9 +1241,30 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1244 | 1241 | ret = get_errno(sethostname((const char *)arg1, arg2)); |
1245 | 1242 | break; |
1246 | 1243 | case TARGET_NR_setrlimit: |
1247 | - goto unimplemented; | |
1244 | + { | |
1245 | + /* XXX: convert resource ? */ | |
1246 | + int resource = arg1; | |
1247 | + struct target_rlimit *target_rlim = (void *)arg2; | |
1248 | + struct rlimit rlim; | |
1249 | + rlim.rlim_cur = tswapl(target_rlim->rlim_cur); | |
1250 | + rlim.rlim_max = tswapl(target_rlim->rlim_max); | |
1251 | + ret = get_errno(setrlimit(resource, &rlim)); | |
1252 | + } | |
1253 | + break; | |
1248 | 1254 | case TARGET_NR_getrlimit: |
1249 | - goto unimplemented; | |
1255 | + { | |
1256 | + /* XXX: convert resource ? */ | |
1257 | + int resource = arg1; | |
1258 | + struct target_rlimit *target_rlim = (void *)arg2; | |
1259 | + struct rlimit rlim; | |
1260 | + | |
1261 | + ret = get_errno(getrlimit(resource, &rlim)); | |
1262 | + if (!is_error(ret)) { | |
1263 | + target_rlim->rlim_cur = tswapl(rlim.rlim_cur); | |
1264 | + target_rlim->rlim_max = tswapl(rlim.rlim_max); | |
1265 | + } | |
1266 | + } | |
1267 | + break; | |
1250 | 1268 | case TARGET_NR_getrusage: |
1251 | 1269 | goto unimplemented; |
1252 | 1270 | case TARGET_NR_gettimeofday: |
... | ... | @@ -1317,6 +1335,27 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1317 | 1335 | case TARGET_NR_munmap: |
1318 | 1336 | ret = get_errno(munmap((void *)arg1, arg2)); |
1319 | 1337 | break; |
1338 | + case TARGET_NR_mprotect: | |
1339 | + ret = get_errno(mprotect((void *)arg1, arg2, arg3)); | |
1340 | + break; | |
1341 | + case TARGET_NR_mremap: | |
1342 | + ret = get_errno((long)mremap((void *)arg1, arg2, arg3, arg4)); | |
1343 | + break; | |
1344 | + case TARGET_NR_msync: | |
1345 | + ret = get_errno(msync((void *)arg1, arg2, arg3)); | |
1346 | + break; | |
1347 | + case TARGET_NR_mlock: | |
1348 | + ret = get_errno(mlock((void *)arg1, arg2)); | |
1349 | + break; | |
1350 | + case TARGET_NR_munlock: | |
1351 | + ret = get_errno(munlock((void *)arg1, arg2)); | |
1352 | + break; | |
1353 | + case TARGET_NR_mlockall: | |
1354 | + ret = get_errno(mlockall(arg1)); | |
1355 | + break; | |
1356 | + case TARGET_NR_munlockall: | |
1357 | + ret = get_errno(munlockall()); | |
1358 | + break; | |
1320 | 1359 | case TARGET_NR_truncate: |
1321 | 1360 | ret = get_errno(truncate((const char *)arg1, arg2)); |
1322 | 1361 | break; |
... | ... | @@ -1506,9 +1545,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1506 | 1545 | #endif |
1507 | 1546 | case TARGET_NR_adjtimex: |
1508 | 1547 | goto unimplemented; |
1509 | - case TARGET_NR_mprotect: | |
1510 | - ret = get_errno(mprotect((void *)arg1, arg2, arg3)); | |
1511 | - break; | |
1512 | 1548 | case TARGET_NR_create_module: |
1513 | 1549 | case TARGET_NR_init_module: |
1514 | 1550 | case TARGET_NR_delete_module: |
... | ... | @@ -1532,9 +1568,11 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1532 | 1568 | case TARGET_NR_afs_syscall: |
1533 | 1569 | goto unimplemented; |
1534 | 1570 | case TARGET_NR_setfsuid: |
1535 | - goto unimplemented; | |
1571 | + ret = get_errno(setfsuid(arg1)); | |
1572 | + break; | |
1536 | 1573 | case TARGET_NR_setfsgid: |
1537 | - goto unimplemented; | |
1574 | + ret = get_errno(setfsgid(arg1)); | |
1575 | + break; | |
1538 | 1576 | case TARGET_NR__llseek: |
1539 | 1577 | { |
1540 | 1578 | int64_t res; |
... | ... | @@ -1596,10 +1634,31 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1596 | 1634 | ret = do_select(arg1, (void *)arg2, (void *)arg3, (void *)arg4, |
1597 | 1635 | (void *)arg5); |
1598 | 1636 | break; |
1637 | + case TARGET_NR_poll: | |
1638 | + { | |
1639 | + struct target_pollfd *target_pfd = (void *)arg1; | |
1640 | + unsigned int nfds = arg2; | |
1641 | + int timeout = arg3; | |
1642 | + struct pollfd *pfd; | |
1643 | + int i; | |
1644 | + | |
1645 | + pfd = alloca(sizeof(struct pollfd) * nfds); | |
1646 | + for(i = 0; i < nfds; i++) { | |
1647 | + pfd->fd = tswap32(target_pfd->fd); | |
1648 | + pfd->events = tswap16(target_pfd->events); | |
1649 | + } | |
1650 | + ret = get_errno(poll(pfd, nfds, timeout)); | |
1651 | + if (!is_error(ret)) { | |
1652 | + for(i = 0; i < nfds; i++) { | |
1653 | + target_pfd->revents = tswap16(pfd->revents); | |
1654 | + } | |
1655 | + } | |
1656 | + } | |
1657 | + break; | |
1599 | 1658 | case TARGET_NR_flock: |
1600 | - goto unimplemented; | |
1601 | - case TARGET_NR_msync: | |
1602 | - ret = get_errno(msync((void *)arg1, arg2, arg3)); | |
1659 | + /* NOTE: the flock constant seems to be the same for every | |
1660 | + Linux platform */ | |
1661 | + ret = get_errno(flock(arg1, arg2)); | |
1603 | 1662 | break; |
1604 | 1663 | case TARGET_NR_readv: |
1605 | 1664 | { |
... | ... | @@ -1638,18 +1697,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1638 | 1697 | goto unimplemented; |
1639 | 1698 | case TARGET_NR__sysctl: |
1640 | 1699 | goto unimplemented; |
1641 | - case TARGET_NR_mlock: | |
1642 | - ret = get_errno(mlock((void *)arg1, arg2)); | |
1643 | - break; | |
1644 | - case TARGET_NR_munlock: | |
1645 | - ret = get_errno(munlock((void *)arg1, arg2)); | |
1646 | - break; | |
1647 | - case TARGET_NR_mlockall: | |
1648 | - ret = get_errno(mlockall(arg1)); | |
1649 | - break; | |
1650 | - case TARGET_NR_munlockall: | |
1651 | - ret = get_errno(munlockall()); | |
1652 | - break; | |
1653 | 1700 | case TARGET_NR_sched_setparam: |
1654 | 1701 | goto unimplemented; |
1655 | 1702 | case TARGET_NR_sched_getparam: |
... | ... | @@ -1681,12 +1728,10 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1681 | 1728 | } |
1682 | 1729 | break; |
1683 | 1730 | |
1684 | - case TARGET_NR_mremap: | |
1685 | 1731 | case TARGET_NR_setresuid: |
1686 | 1732 | case TARGET_NR_getresuid: |
1687 | 1733 | case TARGET_NR_vm86: |
1688 | 1734 | case TARGET_NR_query_module: |
1689 | - case TARGET_NR_poll: | |
1690 | 1735 | case TARGET_NR_nfsservctl: |
1691 | 1736 | case TARGET_NR_setresgid: |
1692 | 1737 | case TARGET_NR_getresgid: |
... | ... | @@ -1800,7 +1845,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
1800 | 1845 | goto unimplemented; |
1801 | 1846 | default: |
1802 | 1847 | unimplemented: |
1803 | - gemu_log("Unsupported syscall: %d\n", num); | |
1848 | + gemu_log("gemu: Unsupported syscall: %d\n", num); | |
1804 | 1849 | ret = -ENOSYS; |
1805 | 1850 | break; |
1806 | 1851 | } | ... | ... |
linux-user/syscall_defs.h
... | ... | @@ -150,6 +150,17 @@ struct target_sigaction; |
150 | 150 | int do_sigaction(int sig, const struct target_sigaction *act, |
151 | 151 | struct target_sigaction *oact); |
152 | 152 | |
153 | +struct target_rlimit { | |
154 | + target_ulong rlim_cur; | |
155 | + target_ulong rlim_max; | |
156 | +}; | |
157 | + | |
158 | +struct target_pollfd { | |
159 | + int fd; /* file descriptor */ | |
160 | + short events; /* requested events */ | |
161 | + short revents; /* returned events */ | |
162 | +}; | |
163 | + | |
153 | 164 | /* Networking ioctls */ |
154 | 165 | #define TARGET_SIOCADDRT 0x890B /* add routing table entry */ |
155 | 166 | #define TARGET_SIOCDELRT 0x890C /* delete routing table entry */ | ... | ... |
op-i386.c
... | ... | @@ -119,40 +119,6 @@ static inline int lshift(int x, int n) |
119 | 119 | return x >> (-n); |
120 | 120 | } |
121 | 121 | |
122 | -/* exception support */ | |
123 | -/* NOTE: not static to force relocation generation by GCC */ | |
124 | -void raise_exception(int exception_index) | |
125 | -{ | |
126 | - /* NOTE: the register at this point must be saved by hand because | |
127 | - longjmp restore them */ | |
128 | -#ifdef reg_EAX | |
129 | - env->regs[R_EAX] = EAX; | |
130 | -#endif | |
131 | -#ifdef reg_ECX | |
132 | - env->regs[R_ECX] = ECX; | |
133 | -#endif | |
134 | -#ifdef reg_EDX | |
135 | - env->regs[R_EDX] = EDX; | |
136 | -#endif | |
137 | -#ifdef reg_EBX | |
138 | - env->regs[R_EBX] = EBX; | |
139 | -#endif | |
140 | -#ifdef reg_ESP | |
141 | - env->regs[R_ESP] = ESP; | |
142 | -#endif | |
143 | -#ifdef reg_EBP | |
144 | - env->regs[R_EBP] = EBP; | |
145 | -#endif | |
146 | -#ifdef reg_ESI | |
147 | - env->regs[R_ESI] = ESI; | |
148 | -#endif | |
149 | -#ifdef reg_EDI | |
150 | - env->regs[R_EDI] = EDI; | |
151 | -#endif | |
152 | - env->exception_index = exception_index; | |
153 | - longjmp(env->jmp_env, 1); | |
154 | -} | |
155 | - | |
156 | 122 | /* we define the various pieces of code used by the JIT */ |
157 | 123 | |
158 | 124 | #define REG EAX |
... | ... | @@ -391,13 +357,15 @@ void OPPROTO op_imull_T0_T1(void) |
391 | 357 | } |
392 | 358 | |
393 | 359 | /* division, flags are undefined */ |
394 | -/* XXX: add exceptions for overflow & div by zero */ | |
360 | +/* XXX: add exceptions for overflow */ | |
395 | 361 | void OPPROTO op_divb_AL_T0(void) |
396 | 362 | { |
397 | 363 | unsigned int num, den, q, r; |
398 | 364 | |
399 | 365 | num = (EAX & 0xffff); |
400 | 366 | den = (T0 & 0xff); |
367 | + if (den == 0) | |
368 | + raise_exception(EXCP00_DIVZ); | |
401 | 369 | q = (num / den) & 0xff; |
402 | 370 | r = (num % den) & 0xff; |
403 | 371 | EAX = (EAX & 0xffff0000) | (r << 8) | q; |
... | ... | @@ -409,6 +377,8 @@ void OPPROTO op_idivb_AL_T0(void) |
409 | 377 | |
410 | 378 | num = (int16_t)EAX; |
411 | 379 | den = (int8_t)T0; |
380 | + if (den == 0) | |
381 | + raise_exception(EXCP00_DIVZ); | |
412 | 382 | q = (num / den) & 0xff; |
413 | 383 | r = (num % den) & 0xff; |
414 | 384 | EAX = (EAX & 0xffff0000) | (r << 8) | q; |
... | ... | @@ -420,6 +390,8 @@ void OPPROTO op_divw_AX_T0(void) |
420 | 390 | |
421 | 391 | num = (EAX & 0xffff) | ((EDX & 0xffff) << 16); |
422 | 392 | den = (T0 & 0xffff); |
393 | + if (den == 0) | |
394 | + raise_exception(EXCP00_DIVZ); | |
423 | 395 | q = (num / den) & 0xffff; |
424 | 396 | r = (num % den) & 0xffff; |
425 | 397 | EAX = (EAX & 0xffff0000) | q; |
... | ... | @@ -432,6 +404,8 @@ void OPPROTO op_idivw_AX_T0(void) |
432 | 404 | |
433 | 405 | num = (EAX & 0xffff) | ((EDX & 0xffff) << 16); |
434 | 406 | den = (int16_t)T0; |
407 | + if (den == 0) | |
408 | + raise_exception(EXCP00_DIVZ); | |
435 | 409 | q = (num / den) & 0xffff; |
436 | 410 | r = (num % den) & 0xffff; |
437 | 411 | EAX = (EAX & 0xffff0000) | q; |
... | ... | @@ -445,6 +419,8 @@ void OPPROTO op_divl_EAX_T0(void) |
445 | 419 | |
446 | 420 | num = EAX | ((uint64_t)EDX << 32); |
447 | 421 | den = T0; |
422 | + if (den == 0) | |
423 | + raise_exception(EXCP00_DIVZ); | |
448 | 424 | q = (num / den); |
449 | 425 | r = (num % den); |
450 | 426 | EAX = q; |
... | ... | @@ -458,6 +434,8 @@ void OPPROTO op_idivl_EAX_T0(void) |
458 | 434 | |
459 | 435 | num = EAX | ((uint64_t)EDX << 32); |
460 | 436 | den = T0; |
437 | + if (den == 0) | |
438 | + raise_exception(EXCP00_DIVZ); | |
461 | 439 | q = (num / den); |
462 | 440 | r = (num % den); |
463 | 441 | EAX = q; | ... | ... |
syscall-i386.h
... | ... | @@ -359,7 +359,7 @@ struct target_sigaction { |
359 | 359 | |
360 | 360 | typedef union target_sigval { |
361 | 361 | int sival_int; |
362 | - void *sival_ptr; | |
362 | + target_ulong sival_ptr; | |
363 | 363 | } target_sigval_t; |
364 | 364 | |
365 | 365 | #define TARGET_SI_MAX_SIZE 128 |
... | ... | @@ -389,7 +389,7 @@ typedef struct target_siginfo { |
389 | 389 | struct { |
390 | 390 | pid_t _pid; /* sender's pid */ |
391 | 391 | uid_t _uid; /* sender's uid */ |
392 | - sigval_t _sigval; | |
392 | + target_sigval_t _sigval; | |
393 | 393 | } _rt; |
394 | 394 | |
395 | 395 | /* SIGCHLD */ |
... | ... | @@ -403,7 +403,7 @@ typedef struct target_siginfo { |
403 | 403 | |
404 | 404 | /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ |
405 | 405 | struct { |
406 | - void *_addr; /* faulting insn/memory ref. */ | |
406 | + target_ulong _addr; /* faulting insn/memory ref. */ | |
407 | 407 | } _sigfault; |
408 | 408 | |
409 | 409 | /* SIGPOLL */ |
... | ... | @@ -414,6 +414,46 @@ typedef struct target_siginfo { |
414 | 414 | } _sifields; |
415 | 415 | } target_siginfo_t; |
416 | 416 | |
417 | +/* | |
418 | + * SIGILL si_codes | |
419 | + */ | |
420 | +#define TARGET_ILL_ILLOPN (2) /* illegal operand */ | |
421 | + | |
422 | +/* | |
423 | + * SIGFPE si_codes | |
424 | + */ | |
425 | +#define TARGET_FPE_INTDIV (1) /* integer divide by zero */ | |
426 | +#define TARGET_FPE_INTOVF (2) /* integer overflow */ | |
427 | +#define TARGET_FPE_FLTDIV (3) /* floating point divide by zero */ | |
428 | +#define TARGET_FPE_FLTOVF (4) /* floating point overflow */ | |
429 | +#define TARGET_FPE_FLTUND (5) /* floating point underflow */ | |
430 | +#define TARGET_FPE_FLTRES (6) /* floating point inexact result */ | |
431 | +#define TARGET_FPE_FLTINV (7) /* floating point invalid operation */ | |
432 | +#define TARGET_FPE_FLTSUB (8) /* subscript out of range */ | |
433 | +#define TARGET_NSIGFPE 8 | |
434 | + | |
435 | +/* default linux values for the selectors */ | |
436 | +#define __USER_CS (0x23) | |
437 | +#define __USER_DS (0x2B) | |
438 | + | |
439 | +struct target_pt_regs { | |
440 | + long ebx; | |
441 | + long ecx; | |
442 | + long edx; | |
443 | + long esi; | |
444 | + long edi; | |
445 | + long ebp; | |
446 | + long eax; | |
447 | + int xds; | |
448 | + int xes; | |
449 | + long orig_eax; | |
450 | + long eip; | |
451 | + int xcs; | |
452 | + long eflags; | |
453 | + long esp; | |
454 | + int xss; | |
455 | +}; | |
456 | + | |
417 | 457 | /* ioctls */ |
418 | 458 | |
419 | 459 | /* | ... | ... |
tests/testsig.c
1 | +#define _GNU_SOURCE | |
1 | 2 | #include <stdlib.h> |
2 | 3 | #include <stdio.h> |
4 | +#include <string.h> | |
3 | 5 | #include <signal.h> |
4 | 6 | #include <unistd.h> |
7 | +#include <setjmp.h> | |
8 | +#include <sys/ucontext.h> | |
9 | + | |
10 | +jmp_buf jmp_env; | |
5 | 11 | |
6 | 12 | void alarm_handler(int sig) |
7 | 13 | { |
... | ... | @@ -9,15 +15,82 @@ void alarm_handler(int sig) |
9 | 15 | alarm(1); |
10 | 16 | } |
11 | 17 | |
18 | +void dump_regs(struct ucontext *uc) | |
19 | +{ | |
20 | + printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" | |
21 | + "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" | |
22 | + "EFL=%08x EIP=%08x\n", | |
23 | + uc->uc_mcontext.gregs[EAX], | |
24 | + uc->uc_mcontext.gregs[EBX], | |
25 | + uc->uc_mcontext.gregs[ECX], | |
26 | + uc->uc_mcontext.gregs[EDX], | |
27 | + uc->uc_mcontext.gregs[ESI], | |
28 | + uc->uc_mcontext.gregs[EDI], | |
29 | + uc->uc_mcontext.gregs[EBP], | |
30 | + uc->uc_mcontext.gregs[ESP], | |
31 | + uc->uc_mcontext.gregs[EFL], | |
32 | + uc->uc_mcontext.gregs[EIP]); | |
33 | +} | |
34 | + | |
35 | +void sig_handler(int sig, siginfo_t *info, void *puc) | |
36 | +{ | |
37 | + struct ucontext *uc = puc; | |
38 | + | |
39 | + printf("%s: si_signo=%d si_errno=%d si_code=%d si_addr=0x%08lx\n", | |
40 | + strsignal(info->si_signo), | |
41 | + info->si_signo, info->si_errno, info->si_code, | |
42 | + (unsigned long)info->si_addr); | |
43 | + dump_regs(uc); | |
44 | + longjmp(jmp_env, 1); | |
45 | +} | |
46 | + | |
47 | +int v1; | |
48 | + | |
12 | 49 | int main(int argc, char **argv) |
13 | 50 | { |
14 | 51 | struct sigaction act; |
52 | + int i; | |
53 | + | |
54 | + /* test division by zero reporting */ | |
55 | + if (setjmp(jmp_env) == 0) { | |
56 | + act.sa_sigaction = sig_handler; | |
57 | + sigemptyset(&act.sa_mask); | |
58 | + act.sa_flags = SA_SIGINFO | SA_ONESHOT; | |
59 | + sigaction(SIGFPE, &act, NULL); | |
60 | + | |
61 | + /* now divide by zero */ | |
62 | + v1 = 0; | |
63 | + v1 = 2 / v1; | |
64 | + } | |
65 | + | |
66 | + /* test illegal instruction reporting */ | |
67 | + if (setjmp(jmp_env) == 0) { | |
68 | + act.sa_sigaction = sig_handler; | |
69 | + sigemptyset(&act.sa_mask); | |
70 | + act.sa_flags = SA_SIGINFO | SA_ONESHOT; | |
71 | + sigaction(SIGILL, &act, NULL); | |
72 | + | |
73 | + /* now execute an invalid instruction */ | |
74 | + asm volatile("ud2"); | |
75 | + } | |
76 | + | |
77 | + /* test SEGV reporting */ | |
78 | + if (setjmp(jmp_env) == 0) { | |
79 | + act.sa_sigaction = sig_handler; | |
80 | + sigemptyset(&act.sa_mask); | |
81 | + act.sa_flags = SA_SIGINFO | SA_ONESHOT; | |
82 | + sigaction(SIGSEGV, &act, NULL); | |
83 | + | |
84 | + /* now store in an invalid address */ | |
85 | + *(char *)0x1234 = 1; | |
86 | + } | |
87 | + | |
15 | 88 | act.sa_handler = alarm_handler; |
16 | 89 | sigemptyset(&act.sa_mask); |
17 | 90 | act.sa_flags = 0; |
18 | 91 | sigaction(SIGALRM, &act, NULL); |
19 | 92 | alarm(1); |
20 | - for(;;) { | |
93 | + for(i = 0;i < 2; i++) { | |
21 | 94 | sleep(1); |
22 | 95 | } |
23 | 96 | return 0; | ... | ... |
translate-i386.c
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | #include <stdio.h> |
23 | 23 | #include <string.h> |
24 | 24 | #include <inttypes.h> |
25 | +#include <signal.h> | |
25 | 26 | #include <assert.h> |
26 | 27 | |
27 | 28 | #define DEBUG_DISAS |
... | ... | @@ -3487,7 +3488,8 @@ static void dump_ops(const uint16_t *opc_buf) |
3487 | 3488 | static uint16_t gen_opc_buf[OPC_BUF_SIZE]; |
3488 | 3489 | static uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE]; |
3489 | 3490 | |
3490 | -/* return the next pc */ | |
3491 | +/* return non zero if the very first instruction is invalid so that | |
3492 | + the virtual CPU can trigger an exception. */ | |
3491 | 3493 | int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
3492 | 3494 | int *gen_code_size_ptr, |
3493 | 3495 | 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, |
3519 | 3521 | do { |
3520 | 3522 | ret = disas_insn(dc, pc_ptr); |
3521 | 3523 | if (ret == -1) { |
3522 | - fprintf(stderr, "unknown instruction at PC=0x%08lx B=%02x %02x %02x", | |
3523 | - (long)pc_ptr, pc_ptr[0], pc_ptr[1], pc_ptr[2]); | |
3524 | - abort(); | |
3524 | + /* we trigger an illegal instruction operation only if it | |
3525 | + is the first instruction. Otherwise, we simply stop | |
3526 | + generating the code just before it */ | |
3527 | + if (pc_ptr == pc_start) | |
3528 | + return -1; | |
3529 | + else | |
3530 | + break; | |
3525 | 3531 | } |
3526 | 3532 | pc_ptr = (void *)ret; |
3527 | 3533 | } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end); |
... | ... | @@ -3640,8 +3646,7 @@ CPUX86State *cpu_x86_init(void) |
3640 | 3646 | env->fptags[i] = 1; |
3641 | 3647 | env->fpuc = 0x37f; |
3642 | 3648 | /* flags setup */ |
3643 | - env->cc_op = CC_OP_EFLAGS; | |
3644 | - env->df = 1; | |
3649 | + env->eflags = 0; | |
3645 | 3650 | |
3646 | 3651 | /* init various static tables */ |
3647 | 3652 | if (!inited) { | ... | ... |