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
|
- verify thread support (clone() and various locks)
|
||||||
- optimize translated cache chaining (DLL PLT-like system)
|
|
||||||
- 64 bit syscalls
|
|
||||||
- signals
|
- 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)
|
- make it self runnable (use same trick as ld.so : include its own relocator and libc)
|
||||||
- improved 16 bit support
|
- improved 16 bit support
|
||||||
- fix FPU exceptions (in particular: gen_op_fpush not before mem load)
|
- 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_buffer[CODE_GEN_BUFFER_SIZE];
|
||||||
uint8_t *code_gen_ptr;
|
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
|
#ifdef DEBUG_EXEC
|
||||||
static const char *cc_op_str[] = {
|
static const char *cc_op_str[] = {
|
||||||
"DYNAMIC",
|
"DYNAMIC",
|
||||||
|
@ -266,11 +312,15 @@ int cpu_x86_exec(CPUX86State *env1)
|
||||||
tc_ptr = tb->tc_ptr;
|
tc_ptr = tb->tc_ptr;
|
||||||
if (!tb->tc_ptr) {
|
if (!tb->tc_ptr) {
|
||||||
/* if no translated code available, then translate it now */
|
/* 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;
|
tc_ptr = code_gen_ptr;
|
||||||
cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
|
cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
|
||||||
&code_gen_size, pc, cs_base, flags);
|
&code_gen_size, pc, cs_base, flags);
|
||||||
tb->tc_ptr = tc_ptr;
|
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));
|
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 */
|
/* execute the generated code */
|
||||||
gen_func = (void *)tc_ptr;
|
gen_func = (void *)tc_ptr;
|
||||||
|
|
|
@ -139,3 +139,5 @@ typedef struct CCTable {
|
||||||
extern CCTable cc_table[];
|
extern CCTable cc_table[];
|
||||||
|
|
||||||
void load_seg(int seg_reg, int selector);
|
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];
|
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)
|
void usage(void)
|
||||||
{
|
{
|
||||||
printf("gemu version " GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
|
printf("gemu version " GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
|
||||||
|
@ -113,8 +147,6 @@ void usage(void)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *filename;
|
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_FS, __USER_DS);
|
||||||
cpu_x86_load_seg(env, R_GS, __USER_DS);
|
cpu_x86_load_seg(env, R_GS, __USER_DS);
|
||||||
|
|
||||||
for(;;) {
|
cpu_loop(env);
|
||||||
int err;
|
/* never exits */
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
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 do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||||
long arg4, long arg5, long arg6);
|
long arg4, long arg5, long arg6);
|
||||||
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
|
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
|
||||||
|
struct CPUX86State;
|
||||||
|
void cpu_loop(struct CPUX86State *env);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -762,8 +762,48 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou
|
||||||
}
|
}
|
||||||
return ret;
|
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
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void syscall_init(void)
|
void syscall_init(void)
|
||||||
{
|
{
|
||||||
#define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def);
|
#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
|
#ifdef HAVE_GPROF
|
||||||
_mcleanup();
|
_mcleanup();
|
||||||
#endif
|
#endif
|
||||||
|
/* XXX: should free thread stack and CPU env */
|
||||||
_exit(arg1);
|
_exit(arg1);
|
||||||
ret = 0; /* avoid warning */
|
ret = 0; /* avoid warning */
|
||||||
break;
|
break;
|
||||||
|
@ -807,7 +848,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||||
ret = do_brk((char *)arg1);
|
ret = do_brk((char *)arg1);
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_fork:
|
case TARGET_NR_fork:
|
||||||
ret = get_errno(fork());
|
ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_waitpid:
|
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:
|
case TARGET_NR_sigreturn:
|
||||||
goto unimplemented;
|
goto unimplemented;
|
||||||
case TARGET_NR_clone:
|
case TARGET_NR_clone:
|
||||||
goto unimplemented;
|
ret = get_errno(do_fork(cpu_env, arg1, arg2));
|
||||||
|
break;
|
||||||
case TARGET_NR_setdomainname:
|
case TARGET_NR_setdomainname:
|
||||||
ret = get_errno(setdomainname((const char *)arg1, arg2));
|
ret = get_errno(setdomainname((const char *)arg1, arg2));
|
||||||
break;
|
break;
|
||||||
|
@ -1310,7 +1352,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||||
case TARGET_NR_sysfs:
|
case TARGET_NR_sysfs:
|
||||||
goto unimplemented;
|
goto unimplemented;
|
||||||
case TARGET_NR_personality:
|
case TARGET_NR_personality:
|
||||||
ret = get_errno(mprotect((void *)arg1, arg2, arg3));
|
ret = get_errno(personality(arg1));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_afs_syscall:
|
case TARGET_NR_afs_syscall:
|
||||||
goto unimplemented;
|
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_max:
|
||||||
case TARGET_NR_sched_get_priority_min:
|
case TARGET_NR_sched_get_priority_min:
|
||||||
case TARGET_NR_sched_rr_get_interval:
|
case TARGET_NR_sched_rr_get_interval:
|
||||||
|
goto unimplemented;
|
||||||
|
|
||||||
case TARGET_NR_nanosleep:
|
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_mremap:
|
||||||
case TARGET_NR_setresuid:
|
case TARGET_NR_setresuid:
|
||||||
case TARGET_NR_getresuid:
|
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_getpmsg:
|
||||||
case TARGET_NR_putpmsg:
|
case TARGET_NR_putpmsg:
|
||||||
case TARGET_NR_vfork:
|
case TARGET_NR_vfork:
|
||||||
ret = get_errno(vfork());
|
ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_ugetrlimit:
|
case TARGET_NR_ugetrlimit:
|
||||||
case TARGET_NR_truncate64:
|
case TARGET_NR_truncate64:
|
||||||
|
|
|
@ -24,6 +24,11 @@ struct target_timeval {
|
||||||
target_long tv_usec;
|
target_long tv_usec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct target_timespec {
|
||||||
|
target_long tv_sec;
|
||||||
|
target_long tv_nsec;
|
||||||
|
};
|
||||||
|
|
||||||
struct target_iovec {
|
struct target_iovec {
|
||||||
target_long iov_base; /* Starting address */
|
target_long iov_base; /* Starting address */
|
||||||
target_long iov_len; /* Number of bytes */
|
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[6] = 1;
|
||||||
env->fptags[7] = 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(fldcw_A0)
|
||||||
DEF(fclex)
|
DEF(fclex)
|
||||||
DEF(fninit)
|
DEF(fninit)
|
||||||
|
DEF(lock)
|
||||||
|
DEF(unlock)
|
||||||
|
|
|
@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g
|
||||||
LDFLAGS=
|
LDFLAGS=
|
||||||
|
|
||||||
ifeq ($(ARCH),i386)
|
ifeq ($(ARCH),i386)
|
||||||
TESTS=test2 sha1-i386 test-i386
|
TESTS=testclone testsig testthread sha1-i386 test-i386
|
||||||
endif
|
endif
|
||||||
TESTS+=sha1
|
TESTS+=sha1
|
||||||
|
|
||||||
|
@ -16,9 +16,15 @@ hello: hello.c
|
||||||
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||||
strip hello
|
strip hello
|
||||||
|
|
||||||
test2: test2.c
|
testclone: testclone.c
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
$(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) */
|
# i386 emulation test (test various opcodes) */
|
||||||
test-i386: test-i386.c test-i386-code16.S \
|
test-i386: test-i386.c test-i386-code16.S \
|
||||||
test-i386.h test-i386-shift.h test-i386-muldiv.h
|
test-i386.h test-i386-shift.h test-i386-muldiv.h
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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->aflag = aflag;
|
||||||
s->dflag = dflag;
|
s->dflag = dflag;
|
||||||
|
|
||||||
|
/* lock generation */
|
||||||
|
if (prefixes & PREFIX_LOCK)
|
||||||
|
gen_op_lock();
|
||||||
|
|
||||||
/* now check op code */
|
/* now check op code */
|
||||||
reswitch:
|
reswitch:
|
||||||
switch(b) {
|
switch(b) {
|
||||||
|
@ -3153,8 +3157,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||||
default:
|
default:
|
||||||
goto illegal_op;
|
goto illegal_op;
|
||||||
}
|
}
|
||||||
|
/* lock generation */
|
||||||
|
if (s->prefix & PREFIX_LOCK)
|
||||||
|
gen_op_unlock();
|
||||||
return (long)s->pc;
|
return (long)s->pc;
|
||||||
illegal_op:
|
illegal_op:
|
||||||
|
/* XXX: ensure that no lock was generated */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3609,6 +3617,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
|
||||||
pc += count;
|
pc += count;
|
||||||
}
|
}
|
||||||
fprintf(logfile, "\n");
|
fprintf(logfile, "\n");
|
||||||
|
fflush(logfile);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue