mirror of
https://github.com/xemu-project/xemu.git
synced 2025-02-25 07:02:38 +00:00
basic clone() support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
612384d771
commit
1b6b029e40
9
TODO
9
TODO
@ -1,8 +1,9 @@
|
||||
- overrides/16bit for string ops
|
||||
- optimize translated cache chaining (DLL PLT-like system)
|
||||
- 64 bit syscalls
|
||||
- verify thread support (clone() and various locks)
|
||||
- signals
|
||||
- threads
|
||||
- optimize translated cache chaining (DLL PLT-like system)
|
||||
- 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)
|
||||
|
50
exec-i386.c
50
exec-i386.c
@ -52,6 +52,52 @@ int nb_tbs;
|
||||
uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
|
||||
uint8_t *code_gen_ptr;
|
||||
|
||||
/* thread support */
|
||||
|
||||
#ifdef __powerpc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
__asm__ __volatile__ (
|
||||
"0: lwarx %0,0,%1 ;"
|
||||
" xor. %0,%3,%0;"
|
||||
" bne 1f;"
|
||||
" stwcx. %2,0,%1;"
|
||||
" bne- 0b;"
|
||||
"1: "
|
||||
: "=&r" (ret)
|
||||
: "r" (p), "r" (1), "r" (0)
|
||||
: "cr0", "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
char ret;
|
||||
long int readval;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
|
||||
: "=q" (ret), "=m" (*p), "=a" (readval)
|
||||
: "r" (1), "m" (*p), "a" (0)
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int global_cpu_lock = 0;
|
||||
|
||||
void cpu_lock(void)
|
||||
{
|
||||
while (testandset(&global_cpu_lock));
|
||||
}
|
||||
|
||||
void cpu_unlock(void)
|
||||
{
|
||||
global_cpu_lock = 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_EXEC
|
||||
static const char *cc_op_str[] = {
|
||||
"DYNAMIC",
|
||||
@ -266,11 +312,15 @@ int cpu_x86_exec(CPUX86State *env1)
|
||||
tc_ptr = tb->tc_ptr;
|
||||
if (!tb->tc_ptr) {
|
||||
/* 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);
|
||||
tb->tc_ptr = tc_ptr;
|
||||
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 */
|
||||
gen_func = (void *)tc_ptr;
|
||||
|
@ -139,3 +139,5 @@ typedef struct CCTable {
|
||||
extern CCTable cc_table[];
|
||||
|
||||
void load_seg(int seg_reg, int selector);
|
||||
void cpu_lock(void);
|
||||
void cpu_unlock(void);
|
||||
|
@ -104,6 +104,40 @@ void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
|
||||
uint64_t gdt_table[6];
|
||||
|
||||
void cpu_loop(struct CPUX86State *env)
|
||||
{
|
||||
for(;;) {
|
||||
int err;
|
||||
uint8_t *pc;
|
||||
|
||||
err = cpu_x86_exec(env);
|
||||
pc = env->seg_cache[R_CS].base + env->eip;
|
||||
switch(err) {
|
||||
case EXCP0D_GPF:
|
||||
if (pc[0] == 0xcd && pc[1] == 0x80) {
|
||||
/* syscall */
|
||||
env->eip += 2;
|
||||
env->regs[R_EAX] = do_syscall(env,
|
||||
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]);
|
||||
} else {
|
||||
goto trap_error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
trap_error:
|
||||
fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n",
|
||||
(long)pc, err);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("gemu version " GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
|
||||
@ -113,8 +147,6 @@ void usage(void)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *filename;
|
||||
@ -193,35 +225,7 @@ int main(int argc, char **argv)
|
||||
cpu_x86_load_seg(env, R_FS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_GS, __USER_DS);
|
||||
|
||||
for(;;) {
|
||||
int err;
|
||||
uint8_t *pc;
|
||||
|
||||
err = cpu_x86_exec(env);
|
||||
pc = env->seg_cache[R_CS].base + env->eip;
|
||||
switch(err) {
|
||||
case EXCP0D_GPF:
|
||||
if (pc[0] == 0xcd && pc[1] == 0x80) {
|
||||
/* syscall */
|
||||
env->eip += 2;
|
||||
env->regs[R_EAX] = do_syscall(env,
|
||||
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]);
|
||||
} else {
|
||||
goto trap_error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
trap_error:
|
||||
fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n",
|
||||
(long)pc, err);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
cpu_loop(env);
|
||||
/* never exits */
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ 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);
|
||||
|
||||
#endif
|
||||
|
@ -762,8 +762,48 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* this stack is the equivalent of the kernel stack associated with a
|
||||
thread/process */
|
||||
#define NEW_STACK_SIZE 8192
|
||||
|
||||
static int clone_func(void *arg)
|
||||
{
|
||||
CPUX86State *env = arg;
|
||||
cpu_loop(env);
|
||||
/* never exits */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_fork(CPUX86State *env, unsigned int flags, unsigned long newsp)
|
||||
{
|
||||
int ret;
|
||||
uint8_t *new_stack;
|
||||
CPUX86State *new_env;
|
||||
|
||||
if (flags & CLONE_VM) {
|
||||
if (!newsp)
|
||||
newsp = env->regs[R_ESP];
|
||||
new_stack = malloc(NEW_STACK_SIZE);
|
||||
|
||||
/* we create a new CPU instance. */
|
||||
new_env = cpu_x86_init();
|
||||
memcpy(new_env, env, sizeof(CPUX86State));
|
||||
new_env->regs[R_ESP] = newsp;
|
||||
new_env->regs[R_EAX] = 0;
|
||||
ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
|
||||
} else {
|
||||
/* if no CLONE_VM, we consider it is a fork */
|
||||
if ((flags & ~CSIGNAL) != 0)
|
||||
return -EINVAL;
|
||||
ret = fork();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void syscall_init(void)
|
||||
{
|
||||
#define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def);
|
||||
@ -788,6 +828,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||
#ifdef HAVE_GPROF
|
||||
_mcleanup();
|
||||
#endif
|
||||
/* XXX: should free thread stack and CPU env */
|
||||
_exit(arg1);
|
||||
ret = 0; /* avoid warning */
|
||||
break;
|
||||
@ -807,7 +848,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||
ret = do_brk((char *)arg1);
|
||||
break;
|
||||
case TARGET_NR_fork:
|
||||
ret = get_errno(fork());
|
||||
ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
|
||||
break;
|
||||
case TARGET_NR_waitpid:
|
||||
{
|
||||
@ -1241,7 +1282,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||
case TARGET_NR_sigreturn:
|
||||
goto unimplemented;
|
||||
case TARGET_NR_clone:
|
||||
goto unimplemented;
|
||||
ret = get_errno(do_fork(cpu_env, arg1, arg2));
|
||||
break;
|
||||
case TARGET_NR_setdomainname:
|
||||
ret = get_errno(setdomainname((const char *)arg1, arg2));
|
||||
break;
|
||||
@ -1310,7 +1352,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||
case TARGET_NR_sysfs:
|
||||
goto unimplemented;
|
||||
case TARGET_NR_personality:
|
||||
ret = get_errno(mprotect((void *)arg1, arg2, arg3));
|
||||
ret = get_errno(personality(arg1));
|
||||
break;
|
||||
case TARGET_NR_afs_syscall:
|
||||
goto unimplemented;
|
||||
@ -1447,7 +1489,23 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||
case TARGET_NR_sched_get_priority_max:
|
||||
case TARGET_NR_sched_get_priority_min:
|
||||
case TARGET_NR_sched_rr_get_interval:
|
||||
goto unimplemented;
|
||||
|
||||
case TARGET_NR_nanosleep:
|
||||
{
|
||||
struct target_timespec *target_req = (void *)arg1;
|
||||
struct target_timespec *target_rem = (void *)arg2;
|
||||
struct timespec req, rem;
|
||||
req.tv_sec = tswapl(target_req->tv_sec);
|
||||
req.tv_nsec = tswapl(target_req->tv_nsec);
|
||||
ret = get_errno(nanosleep(&req, &rem));
|
||||
if (target_rem) {
|
||||
target_rem->tv_sec = tswapl(rem.tv_sec);
|
||||
target_rem->tv_nsec = tswapl(rem.tv_nsec);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TARGET_NR_mremap:
|
||||
case TARGET_NR_setresuid:
|
||||
case TARGET_NR_getresuid:
|
||||
@ -1481,7 +1539,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||
case TARGET_NR_getpmsg:
|
||||
case TARGET_NR_putpmsg:
|
||||
case TARGET_NR_vfork:
|
||||
ret = get_errno(vfork());
|
||||
ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
|
||||
break;
|
||||
case TARGET_NR_ugetrlimit:
|
||||
case TARGET_NR_truncate64:
|
||||
|
@ -24,6 +24,11 @@ struct target_timeval {
|
||||
target_long tv_usec;
|
||||
};
|
||||
|
||||
struct target_timespec {
|
||||
target_long tv_sec;
|
||||
target_long tv_nsec;
|
||||
};
|
||||
|
||||
struct target_iovec {
|
||||
target_long iov_base; /* Starting address */
|
||||
target_long iov_len; /* Number of bytes */
|
||||
|
11
op-i386.c
11
op-i386.c
@ -2272,3 +2272,14 @@ void OPPROTO op_fninit(void)
|
||||
env->fptags[6] = 1;
|
||||
env->fptags[7] = 1;
|
||||
}
|
||||
|
||||
/* threading support */
|
||||
void OPPROTO op_lock(void)
|
||||
{
|
||||
cpu_lock();
|
||||
}
|
||||
|
||||
void OPPROTO op_unlock(void)
|
||||
{
|
||||
cpu_unlock();
|
||||
}
|
||||
|
@ -530,3 +530,5 @@ DEF(fnstcw_A0)
|
||||
DEF(fldcw_A0)
|
||||
DEF(fclex)
|
||||
DEF(fninit)
|
||||
DEF(lock)
|
||||
DEF(unlock)
|
||||
|
@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g
|
||||
LDFLAGS=
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
TESTS=test2 sha1-i386 test-i386
|
||||
TESTS=testclone testsig testthread sha1-i386 test-i386
|
||||
endif
|
||||
TESTS+=sha1
|
||||
|
||||
@ -16,9 +16,15 @@ hello: hello.c
|
||||
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
strip hello
|
||||
|
||||
test2: test2.c
|
||||
testclone: testclone.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
testsig: testsig.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
testthread: testthread.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
|
||||
|
||||
# i386 emulation test (test various opcodes) */
|
||||
test-i386: test-i386.c test-i386-code16.S \
|
||||
test-i386.h test-i386-shift.h test-i386-muldiv.h
|
||||
|
61
tests/testclone.c
Normal file
61
tests/testclone.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sched.h>
|
||||
|
||||
int thread1_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
|
||||
for(i=0;i<10;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thread2_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
for(i=0;i<20;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(120 * 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STACK_SIZE 16384
|
||||
|
||||
void test_clone(void)
|
||||
{
|
||||
uint8_t *stack1, *stack2;
|
||||
int pid1, pid2, status1, status2;
|
||||
|
||||
stack1 = malloc(STACK_SIZE);
|
||||
pid1 = clone(thread1_func, stack1 + STACK_SIZE,
|
||||
CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1");
|
||||
|
||||
stack2 = malloc(STACK_SIZE);
|
||||
pid2 = clone(thread2_func, stack2 + STACK_SIZE,
|
||||
CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2");
|
||||
|
||||
while (waitpid(pid1, &status1, 0) != pid1);
|
||||
while (waitpid(pid2, &status2, 0) != pid2);
|
||||
printf("status1=0x%x\n", status1);
|
||||
printf("status2=0x%x\n", status2);
|
||||
printf("End of clone test.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_clone();
|
||||
return 0;
|
||||
}
|
24
tests/testsig.c
Normal file
24
tests/testsig.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void alarm_handler(int sig)
|
||||
{
|
||||
printf("alarm signal=%d\n", sig);
|
||||
alarm(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction act;
|
||||
act.sa_handler = alarm_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGALRM, &act, NULL);
|
||||
alarm(1);
|
||||
for(;;) {
|
||||
sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
50
tests/testthread.c
Normal file
50
tests/testthread.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sched.h>
|
||||
|
||||
void *thread1_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
|
||||
for(i=0;i<10;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *thread2_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
for(i=0;i<20;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(150 * 1000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void test_pthread(void)
|
||||
{
|
||||
pthread_t tid1, tid2;
|
||||
|
||||
pthread_create(&tid1, NULL, thread1_func, "hello1");
|
||||
pthread_create(&tid2, NULL, thread2_func, "hello2");
|
||||
pthread_join(tid1, NULL);
|
||||
pthread_join(tid2, NULL);
|
||||
printf("End of pthread test.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_pthread();
|
||||
return 0;
|
||||
}
|
@ -1395,6 +1395,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
s->aflag = aflag;
|
||||
s->dflag = dflag;
|
||||
|
||||
/* lock generation */
|
||||
if (prefixes & PREFIX_LOCK)
|
||||
gen_op_lock();
|
||||
|
||||
/* now check op code */
|
||||
reswitch:
|
||||
switch(b) {
|
||||
@ -3153,8 +3157,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
default:
|
||||
goto illegal_op;
|
||||
}
|
||||
/* lock generation */
|
||||
if (s->prefix & PREFIX_LOCK)
|
||||
gen_op_unlock();
|
||||
return (long)s->pc;
|
||||
illegal_op:
|
||||
/* XXX: ensure that no lock was generated */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -3609,6 +3617,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
|
||||
pc += count;
|
||||
}
|
||||
fprintf(logfile, "\n");
|
||||
fflush(logfile);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user