Date: Mon, 17 Nov 2008 00:29:29 GMT From: Peter Wemm <peter@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 153061 for review Message-ID: <200811170029.mAH0TTYl036565@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=153061 Change 153061 by peter@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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200811170029.mAH0TTYl036565>