PERFORCE change 153061 for review

Peter Wemm peter at FreeBSD.org
Sun Nov 16 16:29:30 PST 2008


http://perforce.freebsd.org/chv.cgi?CH=153061

Change 153061 by peter at peter_overcee on 2008/11/17 00:29:25

	Add rfork() stubs.  On i386, assume ports/devel/linuxthreads.  On amd64, fail.
	We need to be able to predict the stack location in the new child, and the
	rfork() syscall has nothing to do with that.  The only consumer that I know of
	that uses this is linuxthreads (which is i386-only in ports), and we have to sniff
	the undocumented register contents before it calls rfork() in clone.S.
	It is stubbed out right now anyway.

Affected files ...

.. //depot/projects/valgrind/coregrind/m_syswrap/syswrap-amd64-freebsd.c#9 edit
.. //depot/projects/valgrind/coregrind/m_syswrap/syswrap-freebsd.c#32 edit
.. //depot/projects/valgrind/coregrind/m_syswrap/syswrap-x86-freebsd.c#15 edit

Differences ...

==== //depot/projects/valgrind/coregrind/m_syswrap/syswrap-amd64-freebsd.c#9 (text+ko) ====

@@ -258,6 +258,17 @@
    SET_STATUS_from_SysRes(res);
 }
 
+PRE(sys_rfork)
+{
+   PRINT("sys_rfork ( %#lx )", ARG1 );
+   PRE_REG_READ1(long, "rfork", int, flags);
+
+   VG_(message)(Vg_UserMsg, "rfork() not implemented");
+   VG_(unimplemented)("Valgrind does not support rfork().");
+
+   SET_STATUS_Failure(VKI_ENOSYS);
+}
+
 PRE(sys_sigreturn)
 {
    ThreadState* tst;

==== //depot/projects/valgrind/coregrind/m_syswrap/syswrap-freebsd.c#32 (text+ko) ====

@@ -3371,7 +3371,7 @@
 // BSDXY(__NR_ntp_gettime,		sys_ntp_gettime),		// 248
    // nosys								   249
 // BSDXY(__NR_minherit,			sys_minherit),			// 250
-// BSDX_(__NR_rfork,			sys_rfork),			// 251
+   BSDX_(__NR_rfork,			sys_rfork),			// 251
 
    GENXY(__NR_openbsd_poll,		sys_poll),			// 252
    BSDX_(__NR_issetugid,		sys_issetugid),			// 253

==== //depot/projects/valgrind/coregrind/m_syswrap/syswrap-x86-freebsd.c#15 (text+ko) ====

@@ -98,6 +98,219 @@
 );
 
 
+#if 0
+/*
+        Perform a rfork system call.  rfork is strange because it has
+        fork()-like return-twice semantics, so it needs special
+        handling here.
+
+        Upon entry, we have:
+
+            int (fn)(void*)     in  0+FSZ(%esp)
+            void* child_stack   in  4+FSZ(%esp)
+            int flags           in  8+FSZ(%esp)
+            void* arg           in 12+FSZ(%esp)
+            pid_t* child_tid    in 16+FSZ(%esp)
+            pid_t* parent_tid   in 20+FSZ(%esp)
+            void* tls_ptr       in 24+FSZ(%esp)
+
+        System call requires:
+
+            int    $__NR_clone  in %eax
+            int    flags        in %ebx
+            void*  child_stack  in %ecx
+            pid_t* parent_tid   in %edx
+            pid_t* child_tid    in %edi
+            void*  tls_ptr      in %esi
+
+	Returns an Int encoded in the linux-x86 way, not a SysRes.
+ */
+#define FSZ               "4+4+4+4" /* frame size = retaddr+ebx+edi+esi */
+#define __NR_CLONE        VG_STRINGIFY(__NR_clone)
+#define __NR_EXIT         VG_STRINGIFY(__NR_exit)
+
+extern
+Int do_syscall_clone_x86_freebsd ( Word (*fn)(void *), 
+                                 void* stack, 
+                                 Int   flags, 
+                                 void* arg,
+                                 Int*  child_tid, 
+                                 Int*  parent_tid, 
+                                 vki_modify_ldt_t * );
+asm(
+".text\n"
+"do_syscall_clone_x86_freebsd:\n"
+"        push    %ebx\n"
+"        push    %edi\n"
+"        push    %esi\n"
+
+         /* set up child stack with function and arg */
+"        movl     4+"FSZ"(%esp), %ecx\n"    /* syscall arg2: child stack */
+"        movl    12+"FSZ"(%esp), %ebx\n"    /* fn arg */
+"        movl     0+"FSZ"(%esp), %eax\n"    /* fn */
+"        lea     -8(%ecx), %ecx\n"          /* make space on stack */
+"        movl    %ebx, 4(%ecx)\n"           /*   fn arg */
+"        movl    %eax, 0(%ecx)\n"           /*   fn */
+
+         /* get other args to clone */
+"        movl     8+"FSZ"(%esp), %ebx\n"    /* syscall arg1: flags */
+"        movl    20+"FSZ"(%esp), %edx\n"    /* syscall arg3: parent tid * */
+"        movl    16+"FSZ"(%esp), %edi\n"    /* syscall arg5: child tid * */
+"        movl    24+"FSZ"(%esp), %esi\n"    /* syscall arg4: tls_ptr * */
+"        movl    $"__NR_CLONE", %eax\n"
+"        int     $0x80\n"                   /* clone() */
+"        testl   %eax, %eax\n"              /* child if retval == 0 */
+"        jnz     1f\n"
+
+         /* CHILD - call thread function */
+"        popl    %eax\n"
+"        call    *%eax\n"                   /* call fn */
+
+         /* exit with result */
+"        movl    %eax, %ebx\n"              /* arg1: return value from fn */
+"        movl    $"__NR_EXIT", %eax\n"
+"        int     $0x80\n"
+
+         /* Hm, exit returned */
+"        ud2\n"
+
+"1:\n"   /* PARENT or ERROR */
+"        pop     %esi\n"
+"        pop     %edi\n"
+"        pop     %ebx\n"
+"        ret\n"
+".previous\n"
+);
+
+#undef FSZ
+#undef __NR_CLONE
+#undef __NR_EXIT
+
+
+// forward declarations
+static void setup_child ( ThreadArchState*, ThreadArchState*, Bool );
+
+/* 
+   When a client clones, we need to keep track of the new thread.  This means:
+   1. allocate a ThreadId+ThreadState+stack for the the thread
+
+   2. initialize the thread's new VCPU state
+
+   3. create the thread using the same args as the client requested,
+   but using the scheduler entrypoint for EIP, and a separate stack
+   for ESP.
+ */
+static SysRes do_rfork ( ThreadId ptid, 
+                         UInt flags)
+{
+   static const Bool debug = False;
+
+   Addr         esp;
+   ThreadId     ctid = VG_(alloc_ThreadState)();
+   ThreadState* ptst = VG_(get_ThreadState)(ptid);
+   ThreadState* ctst = VG_(get_ThreadState)(ctid);
+   UWord*       stack;
+   NSegment const* seg;
+   SysRes       res;
+   Int          eax;
+   vki_sigset_t blockall, savedmask;
+
+   VG_(sigfillset)(&blockall);
+
+   vg_assert(VG_(is_running_thread)(ptid));
+   vg_assert(VG_(is_valid_tid)(ctid));
+
+   stack = (UWord*)ML_(allocstack)(ctid);
+   if (stack == NULL) {
+      res = VG_(mk_SysRes_Error)( VKI_ENOMEM );
+      goto out;
+   }
+
+   /* Copy register state
+
+      Both parent and child return to the same place, and the code
+      following the clone syscall works out which is which, so we
+      don't need to worry about it.
+
+      The parent gets the child's new tid returned from clone, but the
+      child gets 0.
+
+      If the clone call specifies a NULL esp for the new thread, then
+      it actually gets a copy of the parent's esp.
+   */
+   /* Note: the clone call done by the Quadrics Elan3 driver specifies
+      clone flags of 0xF00, and it seems to rely on the assumption
+      that the child inherits a copy of the parent's GDT.  
+      setup_child takes care of setting that up. */
+   setup_child( &ctst->arch, &ptst->arch, True );
+
+   /* Make sys_clone appear to have returned Success(0) in the
+      child. */
+   ctst->arch.vex.guest_EAX = 0;
+
+   /* Assume linuxthreads port storing its intended stack in %esi */
+   esp = ctst->arch.vex.guest_ESI;
+
+   ctst->os_state.parent = ptid;
+
+   /* inherit signal mask */
+   ctst->sig_mask     = ptst->sig_mask;
+   ctst->tmp_sig_mask = ptst->sig_mask;
+
+   /* We don't really know where the client stack is, because its
+      allocated by the client.  The best we can do is look at the
+      memory mappings and try to derive some useful information.  We
+      assume that esp starts near its highest possible value, and can
+      only go down to the start of the mmaped segment. */
+   seg = VG_(am_find_nsegment)((Addr)esp);
+   if (seg && seg->kind != SkResvn) {
+      ctst->client_stack_highest_word = (Addr)VG_PGROUNDUP(esp);
+      ctst->client_stack_szB = ctst->client_stack_highest_word - seg->start;
+
+      VG_(register_stack)(seg->start, ctst->client_stack_highest_word);
+
+      if (debug)
+	 VG_(printf)("tid %d: guessed client stack range %#lx-%#lx\n",
+		     ctid, seg->start, VG_PGROUNDUP(esp));
+   } else {
+      VG_(message)(Vg_UserMsg, "!? New thread %d starts with ESP(%#lx) unmapped\n",
+		   ctid, esp);
+      ctst->client_stack_szB  = 0;
+   }
+
+   /* Assume the clone will succeed, and tell any tool that wants to
+      know that this thread has come into existence.  We cannot defer
+      it beyond this point because sys_set_thread_area, just below,
+      causes tCheck to assert by making references to the new ThreadId
+      if we don't state the new thread exists prior to that point.
+      If the clone fails, we'll send out a ll_exit notification for it
+      at the out: label below, to clean up. */
+   VG_TRACK ( pre_thread_ll_create, ptid, ctid );
+
+   /* start the thread with everything blocked */
+   VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
+
+   /* Create the new thread */
+   /* XXX need to see what happens with tids etc with rfork */
+   eax = do_syscall_clone_x86_freebsd(
+            ML_(start_thread_NORETURN), stack, flags /*, &VG_(threads)[ctid], NULL*/ );
+   res = VG_(mk_SysRes_x86_freebsd)( eax ); /* XXX edx returns too! */
+
+   VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
+
+  out:
+   if (res.isError) {
+      /* clone failed */
+      VG_(cleanup_thread)(&ctst->arch);
+      ctst->status = VgTs_Empty;
+      /* oops.  Better tell the tool the thread exited in a hurry :-) */
+      VG_TRACK( pre_thread_ll_exit, ctid );
+   }
+
+   return res;
+}
+#endif
+
 /* Translate a struct modify_ldt_ldt_s to a VexGuestX86SegDescr */
 
 static
@@ -249,6 +462,40 @@
 }  
 
 
+#if 0
+static void setup_child ( /*OUT*/ ThreadArchState *child,
+                          /*IN*/  ThreadArchState *parent,
+                          Bool inherit_parents_GDT )
+{
+   /* We inherit our parent's guest state. */
+   child->vex = parent->vex;
+   child->vex_shadow1 = parent->vex_shadow1;
+   child->vex_shadow2 = parent->vex_shadow2;
+
+   /* We inherit our parent's LDT. */
+   if (parent->vex.guest_LDT == (HWord)NULL) {
+      /* We hope this is the common case. */
+      child->vex.guest_LDT = (HWord)NULL;
+   } else {
+      /* No luck .. we have to take a copy of the parent's. */
+      child->vex.guest_LDT = (HWord)alloc_zeroed_x86_LDT();
+      copy_LDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_LDT,
+                        (VexGuestX86SegDescr*)child->vex.guest_LDT );
+   }
+      
+   /* Either we start with an empty GDT (the usual case) or inherit a
+      copy of our parents' one (Quadrics Elan3 driver -style clone
+      only). */
+   child->vex.guest_GDT = (HWord)NULL;
+      
+   if (inherit_parents_GDT && parent->vex.guest_GDT != (HWord)NULL) {
+      child->vex.guest_GDT = (HWord)alloc_zeroed_x86_GDT();
+      copy_GDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_GDT,
+                        (VexGuestX86SegDescr*)child->vex.guest_GDT );
+   }
+}
+#endif
+
 /* ---------------------------------------------------------------------
    PRE/POST wrappers for x86/Linux-specific syscalls
    ------------------------------------------------------------------ */
@@ -394,6 +641,34 @@
    SET_STATUS_from_SysRes(res);
 }
 
+
+PRE(sys_rfork)
+{
+   PRINT("sys_rfork ( %lx )",ARG1);
+   PRE_REG_READ1(int, "rfork",
+                 unsigned int, flags);
+
+#if 0
+   cloneflags = ARG1;
+
+   if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
+      SET_STATUS_Failure( VKI_EINVAL );
+      return;
+   }
+
+   SET_STATUS_from_SysRes( do_clone(tid, ARG1));
+
+   if (SUCCESS) {
+      *flags |= SfYieldAfter;
+   }
+#else
+   VG_(message)(Vg_UserMsg, "No rfork for you!");
+   VG_(unimplemented)
+         ("Valgrind does not support rfork() yet.");
+   SET_STATUS_Failure( VKI_ENOSYS );
+#endif
+}
+
 PRE(sys_sigreturn)
 {
    /* See comments on PRE(sys_rt_sigreturn) in syswrap-amd64-linux.c for


More information about the p4-projects mailing list