qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] Fix TLS support on x86


From: Alexander Graf
Subject: [Qemu-devel] [PATCH] Fix TLS support on x86
Date: Fri, 08 Jun 2007 16:17:36 +0200
User-agent: Thunderbird 1.5.0.10 (X11/20060911)

Hi,

this patch is based on the NPTL/TLS patch, David Woodhouse sent to the
list some months ago, which unfortulately did not work for me, so these
are the fixes needed to get it working. After all there is a certain
chance, that I got something wrong but basically it does the following:

1. Implement the tg_kill syscall
2. Set the GS shadow register according to the information
set_thread_area receives. I'm not sure about that part, but using the
qemu internal functions (cpu_x86_load_seg) did not work for me.
3. Implement the "new" (2.5.xx) TID setting features of clone()
4. Use clone() for forking, since fork() did not always work for me
(especially when using TLS)

I'm open for suggestions. Using this patch all single-threaded and most
simple multi-threaded current applications (glibc 2.5) do work.
Everything bigger however does not. If anyone on this list is more into
these things, I would very much appreciate any help.


Regards,

Alexander Graf

Index: qemu-0.9.0/linux-user/i386/syscall_nr.h
===================================================================
--- qemu-0.9.0.orig/linux-user/i386/syscall_nr.h
+++ qemu-0.9.0/linux-user/i386/syscall_nr.h
@@ -271,5 +271,6 @@
 #define TARGET_NR_clock_getres (TARGET_NR_timer_create+7)
 #define TARGET_NR_clock_nanosleep      (TARGET_NR_timer_create+8)
 
+#define TARGET_NR_tgkill               270
 #define TARGET_NR_utimes               271
 #define TARGET_NR_fadvise64_64         272
Index: qemu-0.9.0/linux-user/syscall.c
===================================================================
--- qemu-0.9.0.orig/linux-user/syscall.c
+++ qemu-0.9.0/linux-user/syscall.c
@@ -145,6 +145,8 @@ type name (type1 arg1,type2 arg2,type3 a
 #define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo
 #define __NR_sys_syslog __NR_syslog
 #define __NR_sys_fadvise64 __NR_fadvise64
+#define __NR_sys_tgkill __NR_tgkill
+#define __NR_sys_clone __NR_clone
 
 #if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__)
 #define __NR__llseek __NR_lseek
@@ -166,6 +168,8 @@ _syscall5(int, _llseek,  uint,  fd, ulon
 _syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo)
 _syscall3(int,sys_syslog,int,type,char*,bufp,int,len)
 _syscall4(int,sys_fadvise64,int,fd,loff_t,offset,loff_t,len,int,advice)
+_syscall3(int,sys_tgkill,int,tgid,int,pid,int,sig)
+_syscall5(int,sys_clone, int, flags, void *, child_stack, int *, 
parent_tidptr, struct user_desc *, newtls, int *, child_tidptr)
 #ifdef __NR_exit_group
 _syscall1(int,exit_group,int,error_code)
 #endif
@@ -1699,7 +1704,7 @@ int do_modify_ldt(CPUX86State *env, int 
     return ret;
 }
 
-int do_set_thread_area(CPUX86State *env, target_ulong ptr)
+int do_set_thread_area(CPUX86State *env, target_ulong ptr, int 
set_shadow_registers)
 {
     uint64_t *gdt_table = g2h(env->gdt.base);
     struct target_modify_ldt_ldt_s ldt_info;
@@ -1708,6 +1713,7 @@ int do_set_thread_area(CPUX86State *env,
     int seg_not_present, useable;
     uint32_t *lp, entry_1, entry_2;
     int i;
+    SegmentCache *sc = &env->segs[R_GS];
 
     lock_user_struct(target_ldt_info, ptr, 1);
     ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
@@ -1767,6 +1767,12 @@ int do_set_thread_area(CPUX86State *env,
        (useable << 20) |
        0x7000;
 
+    if(set_shadow_registers) {
+        sc->selector = env->regs[R_GS];
+        sc->base = ldt_info.base_addr;
+        sc->limit = ldt_info.limit;
+        sc->flags = entry_2;
+    }
     /* Install the new entry ...  */
 install:
     lp = (uint32_t *)(gdt_table + ldt_info.entry_number);
@@ -1779,20 +1802,21 @@ install:
    thread/process */
 #define NEW_STACK_SIZE 8192
 
-static int clone_func(void *arg)
+static int clone_func(CPUState *cloneenv)
 {
-    CPUState *env = arg;
-    cpu_loop(env);
+    cpu_loop(cloneenv);
     /* never exits */
     return 0;
 }
 
-int do_fork(CPUState *env, unsigned int flags, unsigned long newsp)
+int do_fork(CPUState *env, unsigned int flags, target_ulong newsp, 
target_ulong parent_tidptr, target_ulong newtls, target_ulong child_tidptr)
 {
     int ret;
+    int cpu_index;
+    unsigned long parent_tid=gettid();
     TaskState *ts;
     uint8_t *new_stack;
-    CPUState *new_env;
+    CPUState *new_env, *next_cpu;
 #if defined(TARGET_I386)
     uint64_t *new_gdt_table;
 #endif
@@ -1807,9 +1835,14 @@ int do_fork(CPUState *env, unsigned int 
         /* add in task state list */
         ts->next = first_task_state;
         first_task_state = ts;
-        /* we create a new CPU instance. */
-        new_env = cpu_init();
-        memcpy(new_env, env, sizeof(CPUState));
+        /* we create a new CPU instance. (cpu_copy() in cvs) */
+       new_env = cpu_init();
+       /* preserve chaining and index */
+       next_cpu = new_env->next_cpu;                                           
                                                                       
+       cpu_index = new_env->cpu_index;                                         
                                                                             
+       memcpy(new_env, env, sizeof(CPUState));                                 
                                                                                
 
+        new_env->next_cpu = next_cpu;                                          
                                                                                
  
+       new_env->cpu_index = cpu_index;                                         
                                                                                
 
 #if defined(TARGET_I386)
         if (!newsp)
             newsp = env->regs[R_ESP];
@@ -1818,26 +1851,20 @@ int do_fork(CPUState *env, unsigned int 
                 free(new_env);
                 return -ENOMEM;
        }
        /* Copy main GDT table from parent, but clear TLS entries */
        memcpy(new_gdt_table, g2h(env->gdt.base), 6 * 8);
        memset(&new_gdt_table[6], 0, 3 * 8); 
        new_env->gdt.base = h2g(new_gdt_table);
-       if (flags & 0x00080000 /* CLONE_SETTLS */) {
-               ret = do_set_thread_area(new_env, new_env->regs[R_ESI]);
+       if (flags & CLONE_SETTLS) {
+               ret = do_set_thread_area(new_env, newtls, 1);
                if (ret) {
                        free(new_gdt_table);
                        free(new_env);
                        return ret;
                }
        }
-       cpu_x86_load_seg(env, R_CS, new_env->regs[R_CS]);
-       cpu_x86_load_seg(env, R_DS, new_env->regs[R_DS]);
-       cpu_x86_load_seg(env, R_ES, new_env->regs[R_ES]);
-       cpu_x86_load_seg(env, R_SS, new_env->regs[R_SS]);
-       cpu_x86_load_seg(env, R_FS, new_env->regs[R_FS]);
-       cpu_x86_load_seg(env, R_GS, new_env->regs[R_GS]);
         new_env->regs[R_ESP] = newsp;
         new_env->regs[R_EAX] = 0;
 #elif defined(TARGET_ARM)
         if (!newsp)
             newsp = env->regs[13];
@@ -1877,16 +1933,28 @@ int do_fork(CPUState *env, unsigned int 
 #endif
         new_env->opaque = ts;
 #ifdef __ia64__
-        ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+       ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags & 
~(CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID), new_env);
 #else
-       ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+       ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags & 
~(CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID), new_env);
 #endif
     } else {
         /* if no CLONE_VM, we consider it is a fork */
-        if ((flags & ~CSIGNAL) != 0)
-            return -EINVAL;
-        ret = fork();
+       ret = sys_clone(flags & 
~(CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID), 0, 
g2h(parent_tidptr), NULL, g2h(child_tidptr));
+    }
+    /* Store child thread ID at location parent_tidptr in parent and child 
memory. 
+       Currently this is only done in client memory*/
+     if(flags & CLONE_PARENT_SETTID) {
+        tput32(parent_tidptr, parent_tid);
+     }
+ 
+    /* Store child thread ID at location child_tidptr in child memory. */
+    if(flags & CLONE_CHILD_SETTID) {
+      if(ret==0) { // only in client memory for fork()
+        tput32(child_tidptr, gettid());
+      } else if(flags & CLONE_VM) { // real threads need it too
+        tput32(child_tidptr, ret);
+      }
     }
     return ret;
 }
 
@@ -2206,7 +2221,7 @@ long do_syscall(void *cpu_env, int num, 
         ret = do_brk(arg1);
         break;
     case TARGET_NR_fork:
-        ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
+        ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0,0,0));
         break;
     case TARGET_NR_waitpid:
         {
@@ -3281,7 +3297,7 @@ long do_syscall(void *cpu_env, int num, 
         ret = get_errno(fsync(arg1));
         break;
     case TARGET_NR_clone:
-        ret = get_errno(do_fork(cpu_env, arg1, arg2));
+        ret = get_errno(do_fork(cpu_env, arg1, arg2,arg3,arg4,arg5));
         break;
 #ifdef __NR_exit_group
         /* new thread calls */
@@ -3339,7 +3397,7 @@ long do_syscall(void *cpu_env, int num, 
         ret = do_vm86(cpu_env, arg1, arg2);
         break;
     case TARGET_NR_set_thread_area:
-       ret = get_errno(do_set_thread_area(cpu_env, arg1));
+       ret = get_errno(do_set_thread_area(cpu_env, arg1, 0));
        break;
 #endif
     case TARGET_NR_adjtimex:
@@ -3635,7 +3651,7 @@ long do_syscall(void *cpu_env, int num, 
 #endif
 #ifdef TARGET_NR_vfork
     case TARGET_NR_vfork:
-        ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
+        ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0, 
0,0,0));
         break;
 #endif
 #ifdef TARGET_NR_ugetrlimit
@@ -4164,6 +4180,11 @@ long do_syscall(void *cpu_env, int num, 
        ret = get_errno(sys_fadvise64((int)arg1, arg2, arg3, (int)arg4));
        break;
 #endif
+#ifdef TARGET_NR_tgkill
+    case TARGET_NR_tgkill:
+       ret = get_errno(sys_tgkill((int)arg1, (int)arg2, (int)arg3));
+       break;
+#endif
     default:
     unimplemented:
         gemu_log("qemu: Unsupported syscall: %d\n", num);

reply via email to

[Prev in Thread] Current Thread [Next in Thread]