* ================== PLATFORM CHANGES =================
- Support for demangling Rust symbols (n-i-bz)
+ - Support for demangling Rust symbols (n-i-bz)
+
+ - On linux, clone handling was improved to honour the CLONE_VFORK flag
+ and setting a child stack. Note however that CLONE_VFORK | CLONE_VM
+ is handled like CLONE_VFORK (so removing CLONE_VM flag).
+ Applications that depends on CLONE_VM exact semantic will (still) not work.
* ==================== TOOL CHANGES ====================
https://bugs.kde.org/show_bug.cgi?id=XXXXXX
where XXXXXX is the bug number as listed below.
+342040 Valgrind mishandles clone with CLONE_VFORK | CLONE_VM that clones
+ to a different stack.
348616 Wine/valgrind: noted but unhandled ioctl 0x5390 [..] (DVD_READ_STRUCT)
352395 Please provide SVN revision info in --version -v
352767 Wine/valgrind: noted but unhandled ioctl 0x5307 [..] (CDROMSTOP)
372504 Hanging on exit_group
372600 process loops forever when fatal signals are arriving quickly
373046 Stacks registered by core are never deregistered
+373192 Calling posix_spawn in glibc 2.24 completely broken
Release 3.12.0 (20 October 2016)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
extern Addr ML_(allocstack) ( ThreadId tid );
extern void ML_(call_on_new_stack_0_1) ( Addr stack, Addr retaddr,
void (*f)(Word), Word arg1 );
-extern SysRes ML_(do_fork_clone) ( ThreadId tid, UInt flags,
- Int* parent_tidptr, Int* child_tidptr );
-
// Linux-specific (but non-arch-specific) syscalls
+DECL_TEMPLATE(linux, sys_clone)
DECL_TEMPLATE(linux, sys_mount);
DECL_TEMPLATE(linux, sys_oldumount);
DECL_TEMPLATE(linux, sys_umount);
DECL_TEMPLATE(linux, sys_readahead);
DECL_TEMPLATE(linux, sys_move_pages);
+// clone is similar enough between linux variants to have a generic
+// version, but which will call an extern defined in syswrap-<platform>-linux.c
+DECL_TEMPLATE(linux, sys_clone);
+
// POSIX, but various sub-cases differ between Linux and Darwin.
DECL_TEMPLATE(linux, sys_fcntl);
DECL_TEMPLATE(linux, sys_fcntl64);
DECL_TEMPLATE(linux, sys_socketpair);
DECL_TEMPLATE(linux, sys_kcmp);
-#endif // __PRIV_SYSWRAP_LINUX_H
+// Some arch specific functions called from syswrap-linux.c
+extern Int do_syscall_clone_x86_linux ( Word (*fn)(void *),
+ void* stack,
+ Int flags,
+ void* arg,
+ Int* child_tid,
+ Int* parent_tid,
+ void* tls_ptr);
+extern SysRes ML_(x86_sys_set_thread_area) ( ThreadId tid,
+ vki_modify_ldt_t* info );
+extern void ML_(x86_setup_LDT_GDT) ( /*OUT*/ ThreadArchState *child,
+ /*IN*/ ThreadArchState *parent );
+
+extern Long do_syscall_clone_amd64_linux ( Word (*fn)(void *),
+ void* stack,
+ Long flags,
+ void* arg,
+ Int* child_tid,
+ Int* parent_tid,
+ void* tls_ptr);
+extern ULong do_syscall_clone_ppc32_linux ( Word (*fn)(void *),
+ void* stack,
+ Int flags,
+ void* arg,
+ Int* child_tid,
+ Int* parent_tid,
+ void* tls_ptr);
+extern ULong do_syscall_clone_ppc64_linux ( Word (*fn)(void *),
+ void* stack,
+ Int flags,
+ void* arg,
+ Int* child_tid,
+ Int* parent_tid,
+ void* tls_ptr );
+extern ULong do_syscall_clone_s390x_linux ( void *stack,
+ ULong flags,
+ Int *parent_tid,
+ Int *child_tid,
+ void* tls_ptr,
+ Word (*fn)(void *),
+ void *arg);
+extern Long do_syscall_clone_arm64_linux ( Word (*fn)(void *),
+ void* stack,
+ Long flags,
+ void* arg,
+ Int* child_tid,
+ Int* parent_tid,
+ void* tls_ptr );
+extern ULong do_syscall_clone_arm_linux ( Word (*fn)(void *),
+ void* stack,
+ Int flags,
+ void* arg,
+ Int* child_tid,
+ Int* parent_tid,
+ void* tls_ptr );
+extern ULong do_syscall_clone_mips64_linux ( Word (*fn) (void *), /* a0 - 4 */
+ void* stack, /* a1 - 5 */
+ Int flags, /* a2 - 6 */
+ void* arg, /* a3 - 7 */
+ Int* parent_tid, /* a4 - 8 */
+ void* tls_ptr, /* a5 - 9 */
+ Int* child_tid ); /* a6 - 10 */
+extern UInt do_syscall_clone_mips_linux ( Word (*fn) (void *), //a0 0 32
+ void* stack, //a1 4 36
+ Int flags, //a2 8 40
+ void* arg, //a3 12 44
+ Int* child_tid, //stack 16 48
+ Int* parent_tid, //stack 20 52
+ void* tls_ptr); //stack 24 56
+extern Long do_syscall_clone_tilegx_linux ( Word (*fn) (void *), //r0
+ void* stack, //r1
+ Long flags, //r2
+ void* arg, //r3
+ Long* child_tid, //r4
+ Long* parent_tid, //r5
+ void* tls_ptr ); //r6
+ #endif // __PRIV_SYSWRAP_LINUX_H
/*--------------------------------------------------------------------*/
/*--- end ---*/
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-extern
-Long do_syscall_clone_amd64_linux ( Word (*fn)(void *),
- void* stack,
- Long flags,
- void* arg,
- Long* child_tid,
- Long* parent_tid,
- vki_modify_ldt_t * );
+// See priv_syswrap-linux.h for arg profile.
asm(
".text\n"
".globl do_syscall_clone_amd64_linux\n"
#undef __NR_EXIT
-// forward declaration
-static void setup_child ( ThreadArchState*, ThreadArchState* );
-
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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_clone ( ThreadId ptid,
- ULong flags, Addr rsp,
- Long* parent_tidptr,
- Long* child_tidptr,
- Addr tlsaddr )
-{
- static const Bool debug = False;
-
- ThreadId ctid = VG_(alloc_ThreadState)();
- ThreadState* ptst = VG_(get_ThreadState)(ptid);
- ThreadState* ctst = VG_(get_ThreadState)(ctid);
- UWord* stack;
- SysRes res;
- Long rax;
- 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 rsp for the new thread, then
- it actually gets a copy of the parent's rsp.
- */
- setup_child( &ctst->arch, &ptst->arch );
-
- /* Make sys_clone appear to have returned Success(0) in the
- child. */
- ctst->arch.vex.guest_RAX = 0;
-
- if (rsp != 0)
- ctst->arch.vex.guest_RSP = rsp;
-
- ctst->os_state.parent = ptid;
-
- /* inherit signal mask */
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (rsp, ctst);
-
- /* Assume the clone will succeed, and tell any tool that wants to
- know that this thread has come into existence. If the clone
- fails, we'll send out a ll_exit notification for it at the out:
- label below, to clean up. */
- vg_assert(VG_(owns_BigLock_LL)(ptid));
- VG_TRACK ( pre_thread_ll_create, ptid, ctid );
-
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls at %#lx\n", tlsaddr);
- ctst->arch.vex.guest_FS_CONST = tlsaddr;
- }
-
- flags &= ~VKI_CLONE_SETTLS;
-
- /* start the thread with everything blocked */
- VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
-
- /* Create the new thread */
- rax = do_syscall_clone_amd64_linux(
- ML_(start_thread_NORETURN), stack, flags, &VG_(threads)[ctid],
- child_tidptr, parent_tidptr, NULL
- );
- res = VG_(mk_SysRes_amd64_linux)( rax );
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError(res)) {
- /* 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;
-}
-
-
/* ---------------------------------------------------------------------
More thread stuff
------------------------------------------------------------------ */
{
}
-void setup_child ( /*OUT*/ ThreadArchState *child,
- /*IN*/ ThreadArchState *parent )
-{
- /* We inherit our parent's guest state. */
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
-
/* ---------------------------------------------------------------------
PRE/POST wrappers for AMD64/Linux-specific syscalls
------------------------------------------------------------------ */
the right thing to do is to make these wrappers 'static' since they
aren't visible outside this file, but that requires even more macro
magic. */
-DECL_TEMPLATE(amd64_linux, sys_clone);
DECL_TEMPLATE(amd64_linux, sys_rt_sigreturn);
DECL_TEMPLATE(amd64_linux, sys_arch_prctl);
DECL_TEMPLATE(amd64_linux, sys_ptrace);
DECL_TEMPLATE(amd64_linux, sys_syscall184);
-PRE(sys_clone)
-{
- ULong cloneflags;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
- PRE_REG_READ2(int, "clone",
- unsigned long, flags,
- void *, child_stack);
-
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA3("clone", int *, parent_tidptr);
- }
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int), VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG1 & VKI_CLONE_SETTLS) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA4("clone", vki_modify_ldt_t *, tlsinfo);
- }
- PRE_MEM_READ("clone(tlsinfo)", ARG4, sizeof(vki_modify_ldt_t));
- if (!VG_(am_is_valid_for_client)(ARG4, sizeof(vki_modify_ldt_t),
- VKI_PROT_READ)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA5("clone", int *, child_tidptr);
- }
- PRE_MEM_WRITE("clone(child_tidptr)", ARG4, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG4, sizeof(Int), VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-
- cloneflags = ARG1;
-
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child ESP */
- (Long *)ARG3, /* parent_tidptr */
- (Long *)ARG4, /* child_tidptr */
- (Addr)ARG5)); /* set_tls */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG4)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg,
- "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg,
- "\n");
- VG_(message)(Vg_UserMsg,
- "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg,
- " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg,
- " - via the implementation of fork or vfork\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG4, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_rt_sigreturn)
{
/* This isn't really a syscall at all - it's a misuse of the
LINX_(__NR_setsockopt, sys_setsockopt), // 54
LINXY(__NR_getsockopt, sys_getsockopt), // 55
- PLAX_(__NR_clone, sys_clone), // 56
+ LINX_(__NR_clone, sys_clone), // 56
GENX_(__NR_fork, sys_fork), // 57
GENX_(__NR_vfork, sys_fork), // 58 treat as fork
GENX_(__NR_execve, sys_execve), // 59
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-extern
-ULong do_syscall_clone_arm_linux ( Word (*fn)(void *),
- void* stack,
- Int flags,
- void* arg,
- Int* child_tid,
- Int* parent_tid,
- void* tls );
+// See priv_syswrap-linux.h for arg profile.
asm(
".text\n"
".globl do_syscall_clone_arm_linux\n"
#undef __NR_EXIT
// forward declarations
-static void setup_child ( ThreadArchState*, ThreadArchState* );
-static void assign_guest_tls(ThreadId ctid, Addr tlsptr);
static SysRes sys_set_tls ( ThreadId tid, Addr tlsptr );
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack
- for SP.
- */
-static SysRes do_clone ( ThreadId ptid,
- UInt flags, Addr sp,
- Int *parent_tidptr,
- Int *child_tidptr,
- Addr child_tls)
-{
- ThreadId ctid = VG_(alloc_ThreadState)();
- ThreadState* ptst = VG_(get_ThreadState)(ptid);
- ThreadState* ctst = VG_(get_ThreadState)(ctid);
- UInt r0;
- UWord *stack;
- SysRes res;
- 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;
- }
-
- setup_child( &ctst->arch, &ptst->arch );
-
- ctst->arch.vex.guest_R0 = 0;
- if(sp != 0)
- ctst->arch.vex.guest_R13 = sp;
-
- ctst->os_state.parent = ptid;
-
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (sp, ctst);
-
- vg_assert(VG_(owns_BigLock_LL)(ptid));
- VG_TRACK ( pre_thread_ll_create, ptid, ctid );
-
- if (flags & VKI_CLONE_SETTLS) {
- /* Just assign the tls pointer in the guest TPIDRURO. */
- assign_guest_tls(ctid, child_tls);
- }
-
- flags &= ~VKI_CLONE_SETTLS;
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
-
- r0 = do_syscall_clone_arm_linux(
- ML_(start_thread_NORETURN), stack, flags, &VG_(threads)[ctid],
- child_tidptr, parent_tidptr, NULL
- );
- //VG_(printf)("AFTER SYSCALL, %x and %x CHILD: %d PARENT: %d\n",child_tidptr, parent_tidptr,*child_tidptr,*parent_tidptr);
-
- res = VG_(mk_SysRes_arm_linux)( r0 );
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
-out:
- if (sr_isError(res)) {
- VG_(cleanup_thread)(&ctst->arch);
- ctst->status = VgTs_Empty;
- VG_TRACK( pre_thread_ll_exit, ctid );
- }
-
- return res;
-}
-
-
-
/* ---------------------------------------------------------------------
More thread stuff
------------------------------------------------------------------ */
{
}
-void setup_child ( /*OUT*/ ThreadArchState *child,
- /*IN*/ ThreadArchState *parent )
-{
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
-static void assign_guest_tls(ThreadId tid, Addr tlsptr)
-{
- VG_(threads)[tid].arch.vex.guest_TPIDRURO = tlsptr;
-}
-
/* Assigns tlsptr to the guest TPIDRURO.
If needed for the specific hardware, really executes
the set_tls syscall.
*/
static SysRes sys_set_tls ( ThreadId tid, Addr tlsptr )
{
- assign_guest_tls(tid, tlsptr);
+ VG_(threads)[tid].arch.vex.guest_TPIDRURO = tlsptr;
if (KernelVariantiS(KernelVariant_android_no_hw_tls,
VG_(clo_kernel_variant))) {
DECL_TEMPLATE(arm_linux, sys_lstat64);
DECL_TEMPLATE(arm_linux, sys_fstatat64);
DECL_TEMPLATE(arm_linux, sys_fstat64);
-DECL_TEMPLATE(arm_linux, sys_clone);
DECL_TEMPLATE(arm_linux, sys_sigreturn);
DECL_TEMPLATE(arm_linux, sys_rt_sigreturn);
DECL_TEMPLATE(arm_linux, sys_sigsuspend);
POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) );
}
-PRE(sys_clone)
-{
- UInt cloneflags;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
- PRE_REG_READ5(int, "clone",
- unsigned long, flags,
- void *, child_stack,
- int *, parent_tidptr,
- void *, child_tls,
- int *, child_tidptr);
-
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- PRE_MEM_WRITE("clone(child_tidptr)", ARG5, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG5, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG1 & VKI_CLONE_SETTLS) {
- PRE_MEM_READ("clone(tls_user_desc)", ARG4, sizeof(vki_modify_ldt_t));
- if (!VG_(am_is_valid_for_client)(ARG4, sizeof(vki_modify_ldt_t),
- VKI_PROT_READ)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-
- cloneflags = ARG1;
-
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child ESP */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5, /* child_tidptr */
- (Addr)ARG4)); /* set_tls */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg, " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg, " - via the implementation of fork or vfork\n");
- VG_(message)(Vg_UserMsg, " - for the Quadrics Elan3 user-space driver\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG5, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_sigreturn)
{
/* See comments on PRE(sys_rt_sigreturn) in syswrap-amd64-linux.c for
GENX_(__NR_fsync, sys_fsync), // 118
PLAX_(__NR_sigreturn, sys_sigreturn), // 119 ?/Linux
- PLAX_(__NR_clone, sys_clone), // 120
+ LINX_(__NR_clone, sys_clone), // 120
//zz // (__NR_setdomainname, sys_setdomainname), // 121 */*(?)
GENXY(__NR_uname, sys_newuname), // 122
// PLAX_(__NR_modify_ldt, sys_modify_ldt), // 123
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-extern
-Long do_syscall_clone_arm64_linux ( Word (*fn)(void *),
- void* child_stack,
- Long flags,
- void* arg,
- Int* child_tid,
- Int* parent_tid,
- void* tls );
+// See priv_syswrap-linux.h for arg profile.
asm(
".text\n"
".globl do_syscall_clone_arm64_linux\n"
static void assign_guest_tls(ThreadId ctid, Addr tlsptr);
//ZZ static SysRes sys_set_tls ( ThreadId tid, Addr tlsptr );
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack
- for SP.
- */
-static SysRes do_clone ( ThreadId ptid,
- ULong flags,
- Addr child_xsp,
- Int* parent_tidptr,
- Int* child_tidptr,
- Addr child_tls )
-{
- ThreadId ctid = VG_(alloc_ThreadState)();
- ThreadState* ptst = VG_(get_ThreadState)(ptid);
- ThreadState* ctst = VG_(get_ThreadState)(ctid);
- UWord* stack;
- SysRes res;
- ULong x0;
- 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 xsp for the new thread, then
- it actually gets a copy of the parent's xsp.
- */
- setup_child( &ctst->arch, &ptst->arch );
-
- /* Make sys_clone appear to have returned Success(0) in the
- child. */
- ctst->arch.vex.guest_X0 = 0;
-
- if (child_xsp != 0)
- ctst->arch.vex.guest_XSP = child_xsp;
-
- ctst->os_state.parent = ptid;
-
- /* inherit signal mask */
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack)(child_xsp, ctst);
-
- /* Assume the clone will succeed, and tell any tool that wants to
- know that this thread has come into existence. If the clone
- fails, we'll send out a ll_exit notification for it at the out:
- label below, to clean up. */
- vg_assert(VG_(owns_BigLock_LL)(ptid));
- VG_TRACK ( pre_thread_ll_create, ptid, ctid );
-
- if (flags & VKI_CLONE_SETTLS) {
- /* Just assign the tls pointer in the guest TPIDR_EL0. */
- assign_guest_tls(ctid, child_tls);
- }
-
- flags &= ~VKI_CLONE_SETTLS;
-
- /* start the thread with everything blocked */
- VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
-
- x0 = do_syscall_clone_arm64_linux(
- ML_(start_thread_NORETURN), stack, flags, &VG_(threads)[ctid],
- child_tidptr, parent_tidptr, NULL
- );
-
- res = VG_(mk_SysRes_arm64_linux)( x0 );
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError(res)) {
- /* 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;
-}
-
-
/* ---------------------------------------------------------------------
More thread stuff
------------------------------------------------------------------ */
//ZZ DECL_TEMPLATE(arm_linux, sys_lstat64);
//ZZ DECL_TEMPLATE(arm_linux, sys_fstatat64);
//ZZ DECL_TEMPLATE(arm_linux, sys_fstat64);
-DECL_TEMPLATE(arm64_linux, sys_clone);
//ZZ DECL_TEMPLATE(arm_linux, sys_sigreturn);
DECL_TEMPLATE(arm64_linux, sys_rt_sigreturn);
//ZZ DECL_TEMPLATE(arm_linux, sys_sigsuspend);
//ZZ POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) );
//ZZ }
-/* Aarch64 seems to use CONFIG_CLONE_BACKWARDS in the kernel. See:
- http://dev.gentoo.org/~vapier/aarch64/linux-3.12.6.config
- http://people.redhat.com/wcohen/aarch64/aarch64_config
- from linux-3.10.5/kernel/fork.c
- #ifdef CONFIG_CLONE_BACKWARDS
- SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
- int __user *, parent_tidptr,
- int, tls_val,
- int __user *, child_tidptr)
-*/
-PRE(sys_clone)
-{
- UInt cloneflags;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
- PRE_REG_READ5(int, "clone",
- unsigned long, flags,
- void *, child_stack,
- int *, parent_tidptr,
- void *, child_tls,
- int *, child_tidptr);
-
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-//ZZ if (ARG1 & VKI_CLONE_SETTLS) {
-//ZZ PRE_MEM_READ("clone(tls_user_desc)", ARG4, sizeof(vki_modify_ldt_t));
-//ZZ if (!VG_(am_is_valid_for_client)(ARG4, sizeof(vki_modify_ldt_t),
-//ZZ VKI_PROT_READ)) {
-//ZZ SET_STATUS_Failure( VKI_EFAULT );
-//ZZ return;
-//ZZ }
-//ZZ }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- PRE_MEM_WRITE("clone(child_tidptr)", ARG5, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG5, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-
- cloneflags = ARG1;
-
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child SP */
- (Int*)ARG3, /* parent_tidptr */
- (Int*)ARG5, /* child_tidptr */
- (Addr)ARG4)); /* tls_val */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int*)ARG3, /* parent_tidptr */
- (Int*)ARG5)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg, " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg, " - via the implementation of fork or vfork\n");
- VG_(message)(Vg_UserMsg, " - for the Quadrics Elan3 user-space driver\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG5, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
//ZZ PRE(sys_sigreturn)
//ZZ {
//ZZ /* See comments on PRE(sys_rt_sigreturn) in syswrap-amd64-linux.c for
LINX_(__NR_add_key, sys_add_key), // 217
LINXY(__NR_keyctl, sys_keyctl), // 219
- PLAX_(__NR_clone, sys_clone), // 220
+ LINX_(__NR_clone, sys_clone), // 220
GENX_(__NR_execve, sys_execve), // 221
PLAX_(__NR_mmap, sys_mmap), // 222
PLAX_(__NR_fadvise64, sys_fadvise64), // 223
VG_TRACK(pre_thread_first_insn, tid);
tst->os_state.lwpid = VG_(gettid)();
- /* Set the threadgroup for real. This overwrites the provisional
- value set in do_clone() syswrap-*-linux.c. See comments in
- do_clone for background, also #226116. */
+ /* Set the threadgroup for real. This overwrites the provisional value set
+ in do_clone(). See comments in do_clone for background, also #226116. */
tst->os_state.threadgroup = VG_(getpid)();
/* Thread created with all signals blocked; scheduler will set the
vg_assert(0);
}
+/* Clone a new thread. Note that in the clone syscalls, we hard-code
+ tlsaddr argument as NULL : the guest TLS is emulated via guest
+ registers, and Valgrind itself has no thread local storage. */
+static SysRes clone_new_thread ( Word (*fn)(void *),
+ void* stack,
+ Word flags,
+ ThreadState* ctst,
+ Int* child_tidptr,
+ Int* parent_tidptr)
+{
+ SysRes res;
+ /* Note that in all the below, we make sys_clone appear to have returned
+ Success(0) in the child, by assigning the relevant child guest
+ register(s) just before the clone syscall. */
+#if defined(VGP_x86_linux)
+ Int eax;
+ ctst->arch.vex.guest_EAX = 0;
+ eax = do_syscall_clone_x86_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ res = VG_(mk_SysRes_x86_linux)( eax );
+#elif defined(VGP_amd64_linux)
+ Long rax;
+ ctst->arch.vex.guest_RAX = 0;
+ rax = do_syscall_clone_amd64_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ res = VG_(mk_SysRes_amd64_linux)( rax );
+#elif defined(VGP_ppc32_linux)
+ ULong word64;
+ UInt old_cr = LibVEX_GuestPPC32_get_CR( &ctst->arch.vex );
+ /* %r3 = 0 */
+ ctst->arch.vex.guest_GPR3 = 0;
+ /* %cr0.so = 0 */
+ LibVEX_GuestPPC32_put_CR( old_cr & ~(1<<28), &ctst->arch.vex );
+ word64 = do_syscall_clone_ppc32_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ /* High half word64 is syscall return value. Low half is
+ the entire CR, from which we need to extract CR0.SO. */
+ /* VG_(printf)("word64 = 0x%llx\n", word64); */
+ res = VG_(mk_SysRes_ppc32_linux)(/*val*/(UInt)(word64 >> 32),
+ /*errflag*/ (((UInt)word64) >> 28) & 1);
+#elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux)
+ ULong word64;
+ UInt old_cr = LibVEX_GuestPPC64_get_CR( &ctst->arch.vex );
+ /* %r3 = 0 */
+ ctst->arch.vex.guest_GPR3 = 0;
+ /* %cr0.so = 0 */
+ LibVEX_GuestPPC64_put_CR( old_cr & ~(1<<28), &ctst->arch.vex );
+ word64 = do_syscall_clone_ppc64_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ /* Low half word64 is syscall return value. Hi half is
+ the entire CR, from which we need to extract CR0.SO. */
+ /* VG_(printf)("word64 = 0x%llx\n", word64); */
+ res = VG_(mk_SysRes_ppc64_linux)
+ (/*val*/(UInt)(word64 & 0xFFFFFFFFULL),
+ /*errflag*/ (UInt)((word64 >> (32+28)) & 1));
+#elif defined(VGP_s390x_linux)
+ ULong r2;
+ ctst->arch.vex.guest_r2 = 0;
+ r2 = do_syscall_clone_s390x_linux
+ (stack, flags, parent_tidptr, child_tidptr, NULL,
+ ML_(start_thread_NORETURN), ctst);
+ res = VG_(mk_SysRes_s390x_linux)( r2 );
+#elif defined(VGP_arm64_linux)
+ ULong x0;
+ ctst->arch.vex.guest_X0 = 0;
+ x0 = do_syscall_clone_arm64_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ res = VG_(mk_SysRes_arm64_linux)( x0 );
+#elif defined(VGP_arm_linux)
+ UInt r0;
+ ctst->arch.vex.guest_R0 = 0;
+ r0 = do_syscall_clone_arm_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ res = VG_(mk_SysRes_arm_linux)( r0 );
+#elif defined(VGP_mips64_linux)
+ UInt ret = 0;
+ ctst->arch.vex.guest_r2 = 0;
+ ctst->arch.vex.guest_r7 = 0;
+ ret = do_syscall_clone_mips64_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ parent_tidptr, NULL, child_tidptr);
+ res = VG_(mk_SysRes_mips64_linux)( /* val */ ret, 0, /* errflag */ 0);
+#elif defined(VGP_mips32_linux)
+ UInt ret = 0;
+ ctst->arch.vex.guest_r2 = 0;
+ ctst->arch.vex.guest_r7 = 0;
+ ret = do_syscall_clone_mips_linux
+ (ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ /* High half word64 is syscall return value. Low half is
+ the entire CR, from which we need to extract CR0.SO. */
+ res = VG_ (mk_SysRes_mips32_linux) (/*val */ ret, 0, /*errflag */ 0);
+#elif defined(VGP_tilegx_linux)
+ Long ret = 0;
+ ctst->arch.vex.guest_r0 = 0;
+ ctst->arch.vex.guest_r3 = 0;
+ ret = do_syscall_clone_tilegx_linux
+ (ML_ (start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr, NULL);
+ /* High half word64 is syscall return value. */
+ res = VG_(mk_SysRes_tilegx_linux) (/*val */ ret);
+#else
+# error Unknown platform
+#endif
+ return res;
+}
+
+static void setup_child ( /*OUT*/ ThreadArchState *child,
+ /*IN*/ ThreadArchState *parent )
+{
+ /* We inherit our parent's guest state. */
+ child->vex = parent->vex;
+ child->vex_shadow1 = parent->vex_shadow1;
+ child->vex_shadow2 = parent->vex_shadow2;
-/* Do a clone which is really a fork() */
-SysRes ML_(do_fork_clone) ( ThreadId tid, UInt flags,
- Int* parent_tidptr, Int* child_tidptr )
+#if defined(VGP_x86_linux)
+ extern void ML_(x86_setup_LDT_GDT) ( /*OUT*/ ThreadArchState *child,
+ /*IN*/ ThreadArchState *parent );
+ ML_(x86_setup_LDT_GDT)(child, parent);
+#endif
+}
+
+static SysRes setup_child_tls (ThreadId ctid, Addr tlsaddr)
+{
+ static const Bool debug = False;
+ ThreadState* ctst = VG_(get_ThreadState)(ctid);
+ // res is succesful by default, overriden if a real syscall is needed/done.
+ SysRes res = VG_(mk_SysRes_Success)(0);
+
+ if (debug)
+ VG_(printf)("clone child has SETTLS: tls at %#lx\n", tlsaddr);
+
+#if defined(VGP_x86_linux)
+ vki_modify_ldt_t* tlsinfo = (vki_modify_ldt_t*)tlsaddr;
+ if (debug)
+ VG_(printf)("clone child has SETTLS: tls info at %p: idx=%u "
+ "base=%#lx limit=%x; esp=%#x fs=%x gs=%x\n",
+ tlsinfo, tlsinfo->entry_number,
+ tlsinfo->base_addr, tlsinfo->limit,
+ ctst->arch.vex.guest_ESP,
+ ctst->arch.vex.guest_FS, ctst->arch.vex.guest_GS);
+ res = ML_(x86_sys_set_thread_area)(ctid, tlsinfo);
+#elif defined(VGP_amd64_linux)
+ ctst->arch.vex.guest_FS_CONST = tlsaddr;
+#elif defined(VGP_ppc32_linux)
+ ctst->arch.vex.guest_GPR2 = tlsaddr;
+#elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux)
+ ctst->arch.vex.guest_GPR13 = tlsaddr;
+#elif defined(VGP_s390x_linux)
+ ctst->arch.vex.guest_a0 = (UInt) (tlsaddr >> 32);
+ ctst->arch.vex.guest_a1 = (UInt) tlsaddr;
+#elif defined(VGP_arm64_linux)
+ /* Just assign the tls pointer in the guest TPIDR_EL0. */
+ ctst->arch.vex.guest_TPIDR_EL0 = tlsaddr;
+#elif defined(VGP_arm_linux)
+ /* Just assign the tls pointer in the guest TPIDRURO. */
+ ctst->arch.vex.guest_TPIDRURO = tlsaddr;
+#elif defined(VGP_mips64_linux)
+ ctst->arch.vex.guest_ULR = tlsaddr;
+ ctst->arch.vex.guest_r27 = tlsaddr;
+#elif defined(VGP_mips32_linux)
+ ctst->arch.vex.guest_ULR = tlsaddr;
+ ctst->arch.vex.guest_r27 = tlsaddr;
+#elif defined(VGP_tilegx_linux)
+ ctst->arch.vex.guest_r53 = tlsaddr;
+#else
+# error Unknown platform
+#endif
+ return res;
+}
+
+/*
+ When a client clones, we need to keep track of the new thread. This means:
+ 1. allocate a ThreadId+ThreadState+stack for 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_clone ( ThreadId ptid,
+ UWord flags, Addr sp,
+ Int* parent_tidptr,
+ Int* child_tidptr,
+ Addr tlsaddr)
+{
+ ThreadId ctid = VG_(alloc_ThreadState)();
+ ThreadState* ptst = VG_(get_ThreadState)(ptid);
+ ThreadState* ctst = VG_(get_ThreadState)(ctid);
+ UWord* stack;
+ SysRes res;
+ 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 sp for the new thread, then
+ it actually gets a copy of the parent's sp.
+ */
+ setup_child( &ctst->arch, &ptst->arch );
+
+ if (sp != 0)
+ VG_(set_SP)(ctid, sp);
+
+ ctst->os_state.parent = ptid;
+
+ /* inherit signal mask */
+ ctst->sig_mask = ptst->sig_mask;
+ ctst->tmp_sig_mask = ptst->sig_mask;
+
+ /* Start the child with its threadgroup being the same as the
+ parent's. This is so that any exit_group calls that happen
+ after the child is created but before it sets its
+ os_state.threadgroup field for real (in thread_wrapper in
+ syswrap-linux.c), really kill the new thread. a.k.a this avoids
+ a race condition in which the thread is unkillable (via
+ exit_group) because its threadgroup is not set. The race window
+ is probably only a few hundred or a few thousand cycles long.
+ See #226116. */
+ ctst->os_state.threadgroup = ptst->os_state.threadgroup;
+
+ ML_(guess_and_register_stack) (sp, ctst);
+
+ /* 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 setup_tls, just below,
+ causes checks 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_assert(VG_(owns_BigLock_LL)(ptid));
+ VG_TRACK ( pre_thread_ll_create, ptid, ctid );
+
+ if (flags & VKI_CLONE_SETTLS) {
+ res = setup_child_tls(ctid, tlsaddr);
+ if (sr_isError(res))
+ goto out;
+ }
+ flags &= ~VKI_CLONE_SETTLS;
+
+ /* start the thread with everything blocked */
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
+
+ /* Create the new thread */
+ res = clone_new_thread ( ML_(start_thread_NORETURN), stack, flags, ctst,
+ child_tidptr, parent_tidptr);
+
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
+
+ out:
+ if (sr_isError(res)) {
+ /* 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 );
+ }
+
+#if defined(VGP_mips64_linux) || defined(VGP_mips32_linux)
+ // ??? why do we set unconditionally r2 to 0, even when error out ???
+ ptst->arch.vex.guest_r2 = 0;
+#elif defined(VGP_tilegx_linux)
+ // ??? why do we set unconditionally r0 to 0, even when error out ???
+ ptst->arch.vex.guest_r0 = 0;
+#endif
+
+ return res;
+}
+
+/* Do a clone which is really a fork().
+ ML_(do_fork_clone) uses the clone syscall to fork a child process.
+ Note that this should not be called for a thread creation.
+ Also, some flags combinations are not supported, and such combinations
+ are handled either by masking the non supported flags or by asserting.
+
+ The CLONE_VFORK flag is accepted, as this just tells that the parent is
+ suspended till the child exits or calls execve. We better keep this flag,
+ just in case the guests parent/client code depends on this synchronisation.
+
+ We cannot keep the flag CLONE_VM, as Valgrind will do whatever host
+ instructions in the child process, that will mess up the parent host
+ memory. So, we hope for the best and assumes that the guest application does
+ not (really) depends on sharing the memory between parent and child in the
+ interval between clone and exits/execve.
+
+ If child_sp != 0, the child (guest) sp will be set to child_sp just after the
+ clone syscall, before child guest instructions are executed. */
+static SysRes ML_(do_fork_clone) ( ThreadId tid, UInt flags,
+ Int* parent_tidptr, Int* child_tidptr,
+ Addr child_sp)
{
vki_sigset_t fork_saved_mask;
vki_sigset_t mask;
SysRes res;
if (flags & (VKI_CLONE_SETTLS | VKI_CLONE_FS | VKI_CLONE_VM
- | VKI_CLONE_FILES | VKI_CLONE_VFORK))
+ | VKI_CLONE_FILES))
return VG_(mk_SysRes_Error)( VKI_EINVAL );
/* Block all signals during fork, so that we can fix things up in
if (!sr_isError(res) && sr_Res(res) == 0) {
/* child */
+ if (child_sp != 0)
+ VG_(set_SP)(tid, child_sp);
VG_(do_atfork_child)(tid);
/* restore signal mask */
return res;
}
-
/* ---------------------------------------------------------------------
PRE/POST wrappers for arch-generic, Linux-specific syscalls
------------------------------------------------------------------ */
#define PRE(name) DEFN_PRE_TEMPLATE(linux, name)
#define POST(name) DEFN_POST_TEMPLATE(linux, name)
+PRE(sys_clone)
+{
+ UInt cloneflags;
+ Bool badarg = False;
+
+ PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
+
+// Order of arguments differs between platforms.
+#if defined(VGP_x86_linux) \
+ || defined(VGP_ppc32_linux) \
+ || defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) \
+ || defined(VGP_arm_linux) || defined(VGP_mips32_linux) \
+ || defined(VGP_mips64_linux) || defined(VGP_arm64_linux)
+#define ARG_CHILD_TIDPTR ARG5
+#define PRA_CHILD_TIDPTR PRA5
+#define ARG_TLS ARG4
+#define PRA_TLS PRA4
+#elif defined(VGP_amd64_linux) || defined(VGP_tilegx_linux) \
+ || defined(VGP_s390x_linux)
+#define ARG_CHILD_TIDPTR ARG4
+#define PRA_CHILD_TIDPTR PRA4
+#define ARG_TLS ARG5
+#define PRA_TLS PRA5
+#else
+# error Unknown platform
+#endif
+// And s390x is even more special, and inverts flags and child stack args
+#if defined(VGP_s390x_linux)
+#define ARG_FLAGS ARG2
+#define PRA_FLAGS PRA2
+#define ARG_CHILD_STACK ARG1
+#define PRA_CHILD_STACK PRA1
+#else
+#define ARG_FLAGS ARG1
+#define PRA_FLAGS PRA1
+#define ARG_CHILD_STACK ARG2
+#define PRA_CHILD_STACK PRA2
+#endif
+
+ if (VG_(tdict).track_pre_reg_read) {
+ PRA_FLAGS("clone", unsigned long, flags);
+ PRA_CHILD_STACK("clone", void *, child_stack);
+ }
+
+ if (ARG_FLAGS & VKI_CLONE_PARENT_SETTID) {
+ if (VG_(tdict).track_pre_reg_read) {
+ PRA3("clone", int *, parent_tidptr);
+ }
+ PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
+ if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int),
+ VKI_PROT_WRITE)) {
+ badarg = True;
+ }
+ }
+ if (ARG_FLAGS & VKI_CLONE_SETTLS) {
+ if (VG_(tdict).track_pre_reg_read) {
+ PRA_TLS("clone", vki_modify_ldt_t *, tlsinfo);
+ }
+ /* Not very clear what is vki_modify_ldt_t: for many platforms, it is a
+ dummy type (that we define as a char). We only dereference/check the
+ ARG_TLS pointer if the type looks like a real type, i.e. sizeof > 1. */
+ if (sizeof(vki_modify_ldt_t) > 1) {
+ PRE_MEM_READ("clone(tlsinfo)", ARG_TLS, sizeof(vki_modify_ldt_t));
+ if (!VG_(am_is_valid_for_client)(ARG_TLS, sizeof(vki_modify_ldt_t),
+ VKI_PROT_READ)) {
+ badarg = True;
+ }
+ }
+ }
+ if (ARG_FLAGS & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
+ if (VG_(tdict).track_pre_reg_read) {
+ PRA_CHILD_TIDPTR("clone", int *, child_tidptr);
+ }
+ PRE_MEM_WRITE("clone(child_tidptr)", ARG_CHILD_TIDPTR, sizeof(Int));
+ if (!VG_(am_is_valid_for_client)(ARG_CHILD_TIDPTR, sizeof(Int),
+ VKI_PROT_WRITE)) {
+ badarg = True;
+ }
+ }
+
+ if (badarg) {
+ SET_STATUS_Failure( VKI_EFAULT );
+ return;
+ }
+
+ cloneflags = ARG_FLAGS;
+
+ if (!ML_(client_signal_OK)(ARG_FLAGS & VKI_CSIGNAL)) {
+ SET_STATUS_Failure( VKI_EINVAL );
+ return;
+ }
+
+ /* Only look at the flags we really care about */
+ switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
+ | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
+ case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
+ /* thread creation */
+ SET_STATUS_from_SysRes(
+ do_clone(tid,
+ ARG_FLAGS, /* flags */
+ (Addr)ARG_CHILD_STACK, /* child ESP */
+ (Int*)ARG3, /* parent_tidptr */
+ (Int*)ARG_CHILD_TIDPTR, /* child_tidptr */
+ (Addr)ARG_TLS)); /* set_tls */
+ break;
+
+ case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
+ // FALLTHROUGH - assume vfork (somewhat) == fork, see ML_(do_fork_clone).
+ cloneflags &= ~VKI_CLONE_VM;
+
+ case 0: /* plain fork */
+ SET_STATUS_from_SysRes(
+ ML_(do_fork_clone)(tid,
+ cloneflags, /* flags */
+ (Int*)ARG3, /* parent_tidptr */
+ (Int*)ARG_CHILD_TIDPTR, /* child_tidptr */
+ (Addr)ARG_CHILD_STACK));
+ break;
+
+ default:
+ /* should we just ENOSYS? */
+ VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG_FLAGS);
+ VG_(message)(Vg_UserMsg, "\n");
+ VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
+ VG_(message)(Vg_UserMsg, " - via a threads library (LinuxThreads or NPTL)\n");
+ VG_(message)(Vg_UserMsg, " - via the implementation of fork or vfork\n");
+ VG_(unimplemented)
+ ("Valgrind does not support general clone().");
+ }
+
+ if (SUCCESS) {
+ if (ARG_FLAGS & VKI_CLONE_PARENT_SETTID)
+ POST_MEM_WRITE(ARG3, sizeof(Int));
+ if (ARG_FLAGS & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
+ POST_MEM_WRITE(ARG_CHILD_TIDPTR, sizeof(Int));
+
+ /* Thread creation was successful; let the child have the chance
+ to run */
+ *flags |= SfYieldAfter;
+ }
+
+#undef ARG_CHILD_TIDPTR
+#undef PRA_CHILD_TIDPTR
+#undef ARG_TLS
+#undef PRA_TLS
+#undef ARG_FLAGS
+#undef PRA_FLAGS
+#undef ARG_CHILD_STACK
+#undef PRA_CHILD_STACK
+}
+
/* ---------------------------------------------------------------------
*mount wrappers
------------------------------------------------------------------ */
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-//extern
-UInt do_syscall_clone_mips_linux (Word (*fn) (void *), //a0 0 32
- void *stack, //a1 4 36
- Int flags, //a2 8 40
- void *arg, //a3 12 44
- Int * child_tid, //stack 16 48
- Int * parent_tid, //stack 20 52
- Int tls); //stack 24 56
+// See priv_syswrap-linux.h for arg profile.
asm (
".text\n"
" .globl do_syscall_clone_mips_linux\n"
#undef __NR_EXIT
// forward declarations
-
-static void setup_child (ThreadArchState *, ThreadArchState *);
static SysRes sys_set_tls (ThreadId tid, Addr tlsptr);
static SysRes mips_PRE_sys_mmap (ThreadId tid,
UWord arg1, UWord arg2, UWord arg3,
UWord arg4, UWord arg5, Off64T arg6);
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack
- for SP.
- */
-
-static SysRes do_clone (ThreadId ptid,
- UInt flags, Addr sp,
- Int * parent_tidptr,
- Int * child_tidptr,
- Addr child_tls)
-{
- const Bool debug = False;
- ThreadId ctid = VG_ (alloc_ThreadState) ();
- ThreadState * ptst = VG_ (get_ThreadState) (ptid);
- ThreadState * ctst = VG_ (get_ThreadState) (ctid);
- UInt ret = 0;
- UWord * stack;
- SysRes res;
- 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;
- }
- setup_child (&ctst->arch, &ptst->arch);
-
- /* on MIPS we need to set V0 and A3 to zero */
- ctst->arch.vex.guest_r2 = 0;
- ctst->arch.vex.guest_r7 = 0;
- if (sp != 0)
- ctst->arch.vex.guest_r29 = sp;
-
- ctst->os_state.parent = ptid;
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
-
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (sp, ctst);
-
- VG_TRACK (pre_thread_ll_create, ptid, ctid);
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls at %#lx\n", child_tls);
- ctst->arch.vex.guest_r27 = child_tls;
- res = sys_set_tls(ctid, child_tls);
- if (sr_isError(res))
- goto out;
- ctst->arch.vex.guest_r27 = child_tls;
- }
-
- flags &= ~VKI_CLONE_SETTLS;
- VG_ (sigprocmask) (VKI_SIG_SETMASK, &blockall, &savedmask);
- /* Create the new thread */
- ret = do_syscall_clone_mips_linux (ML_ (start_thread_NORETURN),
- stack, flags, &VG_ (threads)[ctid],
- child_tidptr, parent_tidptr,
- 0 /*child_tls*/);
-
- /* High half word64 is syscall return value. Low half is
- the entire CR, from which we need to extract CR0.SO. */
- if (debug)
- VG_(printf)("ret: 0x%x\n", ret);
-
- res = VG_ (mk_SysRes_mips32_linux) (/*val */ ret, 0, /*errflag */ 0);
-
- VG_ (sigprocmask) (VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError (res)) {
- VG_(cleanup_thread) (&ctst->arch);
- ctst->status = VgTs_Empty;
- VG_TRACK (pre_thread_ll_exit, ctid);
- }
- ptst->arch.vex.guest_r2 = 0;
-
- return res;
-}
-
/* ---------------------------------------------------------------------
More thread stuff
------------------------------------------------------------------ */
void
VG_ (cleanup_thread) (ThreadArchState * arch) { }
-void
-setup_child ( /*OUT*/ ThreadArchState * child,
- /*IN*/ ThreadArchState * parent)
-{
- /* We inherit our parent's guest state. */
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
SysRes sys_set_tls ( ThreadId tid, Addr tlsptr )
{
VG_(threads)[tid].arch.vex.guest_ULR = tlsptr;
DECL_TEMPLATE (mips_linux, sys_fadvise64);
DECL_TEMPLATE (mips_linux, sys_fstatat64);
DECL_TEMPLATE (mips_linux, sys_fstat64);
-DECL_TEMPLATE (mips_linux, sys_clone);
DECL_TEMPLATE (mips_linux, sys_sigreturn);
DECL_TEMPLATE (mips_linux, sys_rt_sigreturn);
DECL_TEMPLATE (mips_linux, sys_cacheflush);
POST_MEM_WRITE (ARG2, sizeof (struct vki_stat64));
}
-PRE(sys_clone)
- {
- Bool badarg = False;
- UInt cloneflags;
- PRINT ("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )", ARG1, ARG2, ARG3,
- ARG4, ARG5);
- PRE_REG_READ2 (int, "clone", unsigned long, flags, void *, child_stack);
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- {
- if (VG_ (tdict).track_pre_reg_read)
- {
- PRA3 ("clone", int *, parent_tidptr);
- }
- PRE_MEM_WRITE ("clone(parent_tidptr)", ARG3, sizeof (Int));
- if (!VG_ (am_is_valid_for_client)(ARG3, sizeof (Int), VKI_PROT_WRITE))
- {
- badarg = True;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- {
- if (VG_ (tdict).track_pre_reg_read)
- {
- PRA5 ("clone", int *, child_tidptr);
- }
- PRE_MEM_WRITE ("clone(child_tidptr)", ARG5, sizeof (Int));
- if (!VG_ (am_is_valid_for_client)(ARG5, sizeof (Int), VKI_PROT_WRITE))
- {
- badarg = True;
- }
- }
- if (badarg)
- {
- SET_STATUS_Failure (VKI_EFAULT);
- return;
- }
- cloneflags = ARG1;
- if (!ML_ (client_signal_OK) (ARG1 & VKI_CSIGNAL))
- {
- SET_STATUS_Failure (VKI_EINVAL);
- return;
- }
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- |VKI_CLONE_FILES | VKI_CLONE_VFORK))
- {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- PRINT ("sys_clone1 ( %#lx, %#lx, %#lx, %#lx, %#lx )",
- ARG1, ARG2, ARG3, ARG4, ARG5);
- SET_STATUS_from_SysRes (do_clone (tid,
- ARG1, /* flags */
- (Addr) ARG2, /* child SP */
- (Int *) ARG3, /* parent_tidptr */
- (Int *) ARG5, /* child_tidptr */
- (Addr) ARG4)); /* child_tls */
-
- break;
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
- case 0: /* plain fork */
- SET_STATUS_from_SysRes (ML_ (do_fork_clone) (tid,
- cloneflags, /* flags */
- (Int *) ARG3, /* parent_tidptr */
- (Int *) ARG5)); /* child_tidptr */
- break;
- default:
- /* should we just ENOSYS? */
- VG_ (message) (Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_ (message) (Vg_UserMsg, "\n");
- VG_ (message) (Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_ (message) (Vg_UserMsg,
- " - via a threads library (LinuxThreads or NPTL)\n");
- VG_ (message) (Vg_UserMsg,
- " - via the implementation of fork or vfork\n");
- VG_ (unimplemented)("Valgrind does not support general clone().");
- }
- if (SUCCESS)
- {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE (ARG3, sizeof (Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE (ARG5, sizeof (Int));
- /* Thread creation was successful; let the child have the chance
- * to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_sigreturn)
{
PRINT ("sys_sigreturn ( )");
LINXY (__NR_ipc, sys_ipc), // 117
GENX_ (__NR_fsync, sys_fsync), // 118
PLAX_ (__NR_sigreturn, sys_sigreturn), // 119
- PLAX_ (__NR_clone, sys_clone), // 120
+ LINX_ (__NR_clone, sys_clone), // 120
//.. // (__NR_setdomainname, sys_setdomainname), // 121
GENXY (__NR_uname, sys_newuname), // 122
//.. PLAX_(__NR_modify_ldt, sys_modify_ldt), // 123
#define __NR_CLONE __NR_clone
#define __NR_EXIT __NR_exit
-ULong do_syscall_clone_mips64_linux ( Word (*fn) (void *), /* a0 - 4 */
- void* stack, /* a1 - 5 */
- Int flags, /* a2 - 6 */
- void* arg, /* a3 - 7 */
- Int* parent_tid, /* a4 - 8 */
- void* /* Int tls */, /* a5 - 9 */
- Int* child_tid ); /* a6 - 10 */
-
+// See priv_syswrap-linux.h for arg profile.
asm(
".text\n"
".set noreorder\n"
#undef __NR_EXIT
/* forward declarations */
-static void setup_child ( ThreadArchState *, ThreadArchState *);
static SysRes sys_set_tls ( ThreadId tid, Addr tlsptr);
-/* When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack for SP. */
-static SysRes do_clone ( ThreadId ptid,
- UInt flags, Addr sp,
- Int* parent_tidptr,
- Int* child_tidptr,
- Addr child_tls )
-{
- const Bool debug = False;
- ThreadId ctid = VG_ (alloc_ThreadState) ();
- ThreadState * ptst = VG_ (get_ThreadState) (ptid);
- ThreadState * ctst = VG_ (get_ThreadState) (ctid);
- UInt ret = 0;
- UWord * stack;
- SysRes res;
- 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;
- }
- setup_child(&ctst->arch, &ptst->arch);
-
- /* on MIPS we need to set V0 and A3 to zero */
- ctst->arch.vex.guest_r2 = 0;
- ctst->arch.vex.guest_r7 = 0;
- if (sp != 0)
- ctst->arch.vex.guest_r29 = sp;
-
- ctst->os_state.parent = ptid;
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (sp, ctst);
-
- VG_TRACK(pre_thread_ll_create, ptid, ctid);
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls at %#lx\n", child_tls);
- res = sys_set_tls(ctid, child_tls);
- if (sr_isError(res))
- goto out;
- ctst->arch.vex.guest_r27 = child_tls;
- }
-
- flags &= ~VKI_CLONE_SETTLS;
- VG_ (sigprocmask) (VKI_SIG_SETMASK, &blockall, &savedmask);
- /* Create the new thread */
- ret = do_syscall_clone_mips64_linux(ML_(start_thread_NORETURN),
- stack, flags, &VG_(threads)[ctid],
- parent_tidptr, NULL /*child_tls*/,
- child_tidptr);
- if (debug)
- VG_(printf)("ret: 0x%x\n", ret);
-
- res = VG_(mk_SysRes_mips64_linux)( /* val */ ret, 0, /* errflag */ 0);
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError (res)) {
- VG_ (cleanup_thread) (&ctst->arch);
- ctst->status = VgTs_Empty;
- VG_TRACK (pre_thread_ll_exit, ctid);
- }
- ptst->arch.vex.guest_r2 = 0;
-
- return res;
-}
-
/* ---------------------------------------------------------------------
More thread stuff
------------------------------------------------------------------ */
void VG_(cleanup_thread) ( ThreadArchState * arch ) { };
-void setup_child ( /* OUT */ ThreadArchState * child,
- /* IN */ ThreadArchState * parent )
-{
- /* We inherit our parent's guest state. */
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
SysRes sys_set_tls ( ThreadId tid, Addr tlsptr )
{
VG_(threads)[tid].arch.vex.guest_ULR = tlsptr;
file, but that requires even more macro magic. */
DECL_TEMPLATE (mips_linux, sys_set_thread_area);
-DECL_TEMPLATE (mips_linux, sys_clone);
DECL_TEMPLATE (mips_linux, sys_tee);
DECL_TEMPLATE (mips_linux, sys_splice);
DECL_TEMPLATE (mips_linux, sys_vmsplice);
(Off64T) ARG6);
SET_STATUS_from_SysRes(r);
}
-
-PRE(sys_clone)
-{
- Bool badarg = False;
- UInt cloneflags;
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )", ARG1, ARG2, ARG3,
- ARG4, ARG5);
- PRE_REG_READ2(int, "clone", unsigned long, flags, void *, child_stack);
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA3("clone", int *, parent_tidptr);
- }
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int), VKI_PROT_WRITE)) {
- badarg = True;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA5("clone", int *, child_tidptr);
- }
- PRE_MEM_WRITE("clone(child_tidptr)", ARG5, sizeof (Int));
- if (!VG_(am_is_valid_for_client)(ARG5, sizeof (Int), VKI_PROT_WRITE))
- badarg = True;
- }
- if (badarg) {
- SET_STATUS_Failure(VKI_EFAULT);
- return;
- }
- cloneflags = ARG1;
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure(VKI_EINVAL);
- return;
- }
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- |VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child SP */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5, /* child_tidptr */
- (Addr)ARG4)); /* child_tls */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg,
- " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg,
- " - via the implementation of fork or vfork\n");
- VG_(unimplemented)("Valgrind does not support general clone().");
- }
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG5, sizeof(Int));
- /* Thread creation was successful; let the child have the chance to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_rt_sigreturn)
{
/* See comments on PRE(sys_rt_sigreturn) in syswrap-s390x-linux.c for
LINXY (__NR_socketpair, sys_socketpair),
LINX_ (__NR_setsockopt, sys_setsockopt),
LINXY (__NR_getsockopt, sys_getsockopt),
- PLAX_ (__NR_clone, sys_clone),
+ LINX_ (__NR_clone, sys_clone),
GENX_ (__NR_fork, sys_fork),
GENX_ (__NR_execve, sys_execve),
GENX_ (__NR_exit, sys_exit),
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-extern
-ULong do_syscall_clone_ppc32_linux ( Word (*fn)(void *),
- void* stack,
- Int flags,
- void* arg,
- Int* child_tid,
- Int* parent_tid,
- vki_modify_ldt_t * );
+// See priv_syswrap-linux.h for arg profile.
asm(
".text\n"
".globl do_syscall_clone_ppc32_linux\n"
#undef __NR_CLONE
#undef __NR_EXIT
-// forward declarations
-static void setup_child ( ThreadArchState*, ThreadArchState* );
-
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack
- for SP.
- */
-static SysRes do_clone ( ThreadId ptid,
- UInt flags, Addr sp,
- Int *parent_tidptr,
- Int *child_tidptr,
- Addr child_tls)
-{
- const Bool debug = False;
-
- ThreadId ctid = VG_(alloc_ThreadState)();
- ThreadState* ptst = VG_(get_ThreadState)(ptid);
- ThreadState* ctst = VG_(get_ThreadState)(ctid);
- ULong word64;
- UWord* stack;
- SysRes res;
- 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;
- }
-
-//? /* make a stack frame */
-//? stack -= 16;
-//? *(UWord *)stack = 0;
-
-
- /* 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 SP for the new thread, then
- it actually gets a copy of the parent's SP.
-
- The child's TLS register (r2) gets set to the tlsaddr argument
- if the CLONE_SETTLS flag is set.
- */
- setup_child( &ctst->arch, &ptst->arch );
-
- /* Make sys_clone appear to have returned Success(0) in the
- child. */
- { UInt old_cr = LibVEX_GuestPPC32_get_CR( &ctst->arch.vex );
- /* %r3 = 0 */
- ctst->arch.vex.guest_GPR3 = 0;
- /* %cr0.so = 0 */
- LibVEX_GuestPPC32_put_CR( old_cr & ~(1<<28), &ctst->arch.vex );
- }
-
- if (sp != 0)
- ctst->arch.vex.guest_GPR1 = sp;
-
- ctst->os_state.parent = ptid;
-
- /* inherit signal mask */
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (sp, ctst);
-
- /* Assume the clone will succeed, and tell any tool that wants to
- know that this thread has come into existence. If the clone
- fails, we'll send out a ll_exit notification for it at the out:
- label below, to clean up. */
- vg_assert(VG_(owns_BigLock_LL)(ptid));
- VG_TRACK ( pre_thread_ll_create, ptid, ctid );
-
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls at %#lx\n", child_tls);
- ctst->arch.vex.guest_GPR2 = child_tls;
- }
-
- flags &= ~VKI_CLONE_SETTLS;
-
- /* start the thread with everything blocked */
- VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
-
- /* Create the new thread */
- word64 = do_syscall_clone_ppc32_linux(
- ML_(start_thread_NORETURN), stack, flags, &VG_(threads)[ctid],
- child_tidptr, parent_tidptr, NULL
- );
- /* High half word64 is syscall return value. Low half is
- the entire CR, from which we need to extract CR0.SO. */
- /* VG_(printf)("word64 = 0x%llx\n", word64); */
- res = VG_(mk_SysRes_ppc32_linux)(
- /*val*/(UInt)(word64 >> 32),
- /*errflag*/ (((UInt)word64) >> 28) & 1
- );
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError(res)) {
- /* 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;
-}
-
-
/* ---------------------------------------------------------------------
More thread stuff
{
}
-void setup_child ( /*OUT*/ ThreadArchState *child,
- /*IN*/ ThreadArchState *parent )
-{
- /* We inherit our parent's guest state. */
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
-
/* ---------------------------------------------------------------------
PRE/POST wrappers for ppc32/Linux-specific syscalls
------------------------------------------------------------------ */
DECL_TEMPLATE(ppc32_linux, sys_lstat64);
DECL_TEMPLATE(ppc32_linux, sys_fstatat64);
DECL_TEMPLATE(ppc32_linux, sys_fstat64);
-DECL_TEMPLATE(ppc32_linux, sys_clone);
DECL_TEMPLATE(ppc32_linux, sys_sigreturn);
DECL_TEMPLATE(ppc32_linux, sys_rt_sigreturn);
DECL_TEMPLATE(ppc32_linux, sys_sigsuspend);
//.. }
//.. }
-PRE(sys_clone)
-{
- UInt cloneflags;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
- PRE_REG_READ5(int, "clone",
- unsigned long, flags,
- void *, child_stack,
- int *, parent_tidptr,
- void *, child_tls,
- int *, child_tidptr);
-
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- PRE_MEM_WRITE("clone(child_tidptr)", ARG5, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG5, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-
- cloneflags = ARG1;
-
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child SP */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5, /* child_tidptr */
- (Addr)ARG4)); /* child_tls */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg, " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg, " - via the implementation of fork or vfork\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG5, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_sigreturn)
{
/* See comments on PRE(sys_rt_sigreturn) in syswrap-amd64-linux.c for
GENX_(__NR_fsync, sys_fsync), // 118
PLAX_(__NR_sigreturn, sys_sigreturn), // 119 ?/Linux
//..
- PLAX_(__NR_clone, sys_clone), // 120
+ LINX_(__NR_clone, sys_clone), // 120
//.. // (__NR_setdomainname, sys_setdomainname), // 121 */*(?)
GENXY(__NR_uname, sys_newuname), // 122
//.. PLAX_(__NR_modify_ldt, sys_modify_ldt), // 123
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-extern
-ULong do_syscall_clone_ppc64_linux ( Word (*fn)(void *),
- void* stack,
- Int flags,
- void* arg,
- Int* child_tid,
- Int* parent_tid,
- void/*vki_modify_ldt_t*/ * );
+// See priv_syswrap-linux.h for arg profile.
asm(
#if defined(VGP_ppc64be_linux)
" .align 2\n"
#undef __NR_CLONE
#undef __NR_EXIT
-// forward declarations
-static void setup_child ( ThreadArchState*, ThreadArchState* );
-
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack
- for SP.
- */
-static SysRes do_clone ( ThreadId ptid,
- UInt flags, Addr sp,
- Int *parent_tidptr,
- Int *child_tidptr,
- Addr child_tls)
-{
- const Bool debug = False;
-
- ThreadId ctid = VG_(alloc_ThreadState)();
- ThreadState* ptst = VG_(get_ThreadState)(ptid);
- ThreadState* ctst = VG_(get_ThreadState)(ctid);
- ULong word64;
- UWord* stack;
- SysRes res;
- 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;
- }
-
-//? /* make a stack frame */
-//? stack -= 16;
-//? *(UWord *)stack = 0;
-
-
- /* 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 SP for the new thread, then
- it actually gets a copy of the parent's SP.
-
- The child's TLS register (r2) gets set to the tlsaddr argument
- if the CLONE_SETTLS flag is set.
- */
- setup_child( &ctst->arch, &ptst->arch );
-
- /* Make sys_clone appear to have returned Success(0) in the
- child. */
- { UInt old_cr = LibVEX_GuestPPC64_get_CR( &ctst->arch.vex );
- /* %r3 = 0 */
- ctst->arch.vex.guest_GPR3 = 0;
- /* %cr0.so = 0 */
- LibVEX_GuestPPC64_put_CR( old_cr & ~(1<<28), &ctst->arch.vex );
- }
-
- if (sp != 0)
- ctst->arch.vex.guest_GPR1 = sp;
-
- ctst->os_state.parent = ptid;
-
- /* inherit signal mask */
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (sp, ctst);
-
- /* Assume the clone will succeed, and tell any tool that wants to
- know that this thread has come into existence. If the clone
- fails, we'll send out a ll_exit notification for it at the out:
- label below, to clean up. */
- vg_assert(VG_(owns_BigLock_LL)(ptid));
- VG_TRACK ( pre_thread_ll_create, ptid, ctid );
-
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls at %#lx\n", child_tls);
- ctst->arch.vex.guest_GPR13 = child_tls;
- }
-
- flags &= ~VKI_CLONE_SETTLS;
-
- /* start the thread with everything blocked */
- VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
-
- /* Create the new thread */
- word64 = do_syscall_clone_ppc64_linux(
- ML_(start_thread_NORETURN),
- stack, flags, &VG_(threads)[ctid],
- child_tidptr, parent_tidptr, NULL
- );
-
- /* Low half word64 is syscall return value. Hi half is
- the entire CR, from which we need to extract CR0.SO. */
- /* VG_(printf)("word64 = 0x%llx\n", word64); */
- res = VG_(mk_SysRes_ppc64_linux)(
- /*val*/(UInt)(word64 & 0xFFFFFFFFULL),
- /*errflag*/ (UInt)((word64 >> (32+28)) & 1)
- );
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError(res)) {
- /* 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;
-}
-
-
-
/* ---------------------------------------------------------------------
More thread stuff
------------------------------------------------------------------ */
{
}
-void setup_child ( /*OUT*/ ThreadArchState *child,
- /*IN*/ ThreadArchState *parent )
-{
- /* We inherit our parent's guest state. */
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
-
/* ---------------------------------------------------------------------
PRE/POST wrappers for ppc64/Linux-specific syscalls
------------------------------------------------------------------ */
//zz DECL_TEMPLATE(ppc64_linux, sys_stat64);
//zz DECL_TEMPLATE(ppc64_linux, sys_lstat64);
//zz DECL_TEMPLATE(ppc64_linux, sys_fstat64);
-DECL_TEMPLATE(ppc64_linux, sys_clone);
//zz DECL_TEMPLATE(ppc64_linux, sys_sigreturn);
DECL_TEMPLATE(ppc64_linux, sys_rt_sigreturn);
DECL_TEMPLATE(ppc64_linux, sys_fadvise64);
//zz POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) );
//zz }
-
-PRE(sys_clone)
-{
- UInt cloneflags;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
- PRE_REG_READ5(int, "clone",
- unsigned long, flags,
- void *, child_stack,
- int *, parent_tidptr,
- void *, child_tls,
- int *, child_tidptr);
-
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- PRE_MEM_WRITE("clone(child_tidptr)", ARG5, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG5, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-
- cloneflags = ARG1;
-
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child SP */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5, /* child_tidptr */
- (Addr)ARG4)); /* child_tls */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg, " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg, " - via the implementation of fork or vfork\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG5, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_fadvise64)
{
PRINT("sys_fadvise64 ( %ld, %ld, %lu, %ld )", SARG1, SARG2, SARG3, SARG4);
GENX_(__NR_fsync, sys_fsync), // 118
// _____(__NR_sigreturn, sys_sigreturn), // 119
- PLAX_(__NR_clone, sys_clone), // 120
+ LINX_(__NR_clone, sys_clone), // 120
// _____(__NR_setdomainname, sys_setdomainname), // 121
GENXY(__NR_uname, sys_newuname), // 122
// _____(__NR_modify_ldt, sys_modify_ldt), // 123
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-extern
-ULong do_syscall_clone_s390x_linux ( void *stack,
- ULong flags,
- Int *parent_tid,
- Int *child_tid,
- Addr tlsaddr,
- Word (*fn)(void *),
- void *arg);
+// See priv_syswrap-linux.h for arg profile.
asm(
" .text\n"
" .align 4\n"
/* only used on x86 for descriptor tables */
}
-static void setup_child ( /*OUT*/ ThreadArchState *child,
- /*IN*/ ThreadArchState *parent )
-{
- /* We inherit our parent's guest state. */
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
-
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack
- for SP.
- */
-static SysRes do_clone ( ThreadId ptid,
- Addr sp, ULong flags,
- Int *parent_tidptr,
- Int *child_tidptr,
- Addr tlsaddr)
-{
- static const Bool debug = False;
-
- ThreadId ctid = VG_(alloc_ThreadState)();
- ThreadState* ptst = VG_(get_ThreadState)(ptid);
- ThreadState* ctst = VG_(get_ThreadState)(ctid);
- UWord* stack;
- SysRes res;
- ULong r2;
- 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 sp for the new thread, then
- it actually gets a copy of the parent's sp.
- */
- setup_child( &ctst->arch, &ptst->arch );
-
- /* Make sys_clone appear to have returned Success(0) in the
- child. */
- ctst->arch.vex.guest_r2 = 0;
-
- if (sp != 0)
- ctst->arch.vex.guest_SP = sp;
-
- ctst->os_state.parent = ptid;
-
- /* inherit signal mask */
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* have the parents thread group */
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (sp, ctst);
-
- /* Assume the clone will succeed, and tell any tool that wants to
- know that this thread has come into existence. If the clone
- fails, we'll send out a ll_exit notification for it at the out:
- label below, to clean up. */
- vg_assert(VG_(owns_BigLock_LL)(ptid));
- VG_TRACK ( pre_thread_ll_create, ptid, ctid );
-
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls at %#lx\n", tlsaddr);
- ctst->arch.vex.guest_a0 = (UInt) (tlsaddr >> 32);
- ctst->arch.vex.guest_a1 = (UInt) tlsaddr;
- }
- flags &= ~VKI_CLONE_SETTLS;
-
- /* start the thread with everything blocked */
- VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
-
- /* Create the new thread */
- r2 = do_syscall_clone_s390x_linux(
- stack, flags, parent_tidptr, child_tidptr, tlsaddr,
- ML_(start_thread_NORETURN), &VG_(threads)[ctid]);
-
- res = VG_(mk_SysRes_s390x_linux)( r2 );
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError(res)) {
- /* clone failed */
- ctst->status = VgTs_Empty;
- /* oops. Better tell the tool the thread exited in a hurry :-) */
- VG_TRACK( pre_thread_ll_exit, ctid );
- }
-
- return res;
-
-}
-
-
-
/* ---------------------------------------------------------------------
PRE/POST wrappers for s390x/Linux-specific syscalls
------------------------------------------------------------------ */
DECL_TEMPLATE(s390x_linux, sys_ptrace);
DECL_TEMPLATE(s390x_linux, sys_mmap);
-DECL_TEMPLATE(s390x_linux, sys_clone);
DECL_TEMPLATE(s390x_linux, sys_sigreturn);
DECL_TEMPLATE(s390x_linux, sys_rt_sigreturn);
DECL_TEMPLATE(s390x_linux, sys_fadvise64);
SET_STATUS_from_SysRes(r);
}
-PRE(sys_clone)
-{
- UInt cloneflags;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4, ARG5);
- PRE_REG_READ2(int, "clone",
- void *, child_stack,
- unsigned long, flags);
-
- if (ARG2 & VKI_CLONE_PARENT_SETTID) {
- if (VG_(tdict).track_pre_reg_read)
- PRA3("clone(parent_tidptr)", int *, parent_tidptr);
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG2 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- if (VG_(tdict).track_pre_reg_read)
- PRA4("clone(child_tidptr)", int *, child_tidptr);
- PRE_MEM_WRITE("clone(child_tidptr)", ARG4, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG4, sizeof(Int),
- VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-
- /* The kernel simply copies reg6 (ARG5) into AR0 and AR1, no checks */
- if (ARG2 & VKI_CLONE_SETTLS) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA5("clone", Addr, tlsinfo);
- }
- }
-
- cloneflags = ARG2;
-
- if (!ML_(client_signal_OK)(ARG2 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- (Addr)ARG1, /* child SP */
- ARG2, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG4, /* child_tidptr */
- (Addr)ARG5)); /* tlsaddr */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG4)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG2);
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg, " - via a threads library (NPTL)\n");
- VG_(message)(Vg_UserMsg, " - via the implementation of fork or vfork\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG2 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG2 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG4, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_sigreturn)
{
ThreadState* tst;
GENX_(__NR_fsync, sys_fsync), // 118
PLAX_(__NR_sigreturn, sys_sigreturn), // 119
- PLAX_(__NR_clone, sys_clone), // 120
+ LINX_(__NR_clone, sys_clone), // 120
// ?????(__NR_setdomainname, ), // 121
GENXY(__NR_uname, sys_newuname), // 122
GENX_(123, sys_ni_syscall), /* unimplemented (by the kernel) */ // 123
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-Long do_syscall_clone_tilegx_linux ( Word (*fn) (void *), //r0
- void *stack, //r1
- Long flags, //r2
- void *arg, //r3
- Long * child_tid, //r4
- Long * parent_tid, //r5
- Long tls ); //r6
- /*
+ /*
stack
high -> 4 r29
3
1 r10
low -> 0 lr <- sp
*/
+// See priv_syswrap-linux.h for arg profile.
asm (
".text\n"
" .globl do_syscall_clone_tilegx_linux\n"
#undef __NR_EXIT
// forward declarations
-static void setup_child ( ThreadArchState *, ThreadArchState * );
-static SysRes sys_set_tls ( ThreadId tid, Addr tlsptr );
- /*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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 IP, and a separate stack
- for SP.
- */
-static SysRes do_clone ( ThreadId ptid,
- Long flags, Addr sp,
- Long * parent_tidptr,
- Long * child_tidptr,
- Addr child_tls )
-{
- const Bool debug = False;
- ThreadId ctid = VG_ (alloc_ThreadState) ();
- ThreadState * ptst = VG_ (get_ThreadState) (ptid);
- ThreadState * ctst = VG_ (get_ThreadState) (ctid);
- Long ret = 0;
- Long * stack;
- SysRes res;
- vki_sigset_t blockall, savedmask;
-
- VG_ (sigfillset) (&blockall);
- vg_assert (VG_ (is_running_thread) (ptid));
- vg_assert (VG_ (is_valid_tid) (ctid));
- stack = (Long *) ML_ (allocstack) (ctid);
- if (stack == NULL) {
- res = VG_ (mk_SysRes_Error) (VKI_ENOMEM);
- goto out;
- }
- setup_child (&ctst->arch, &ptst->arch);
-
- /* On TILEGX we need to set r0 and r3 to zero */
- ctst->arch.vex.guest_r0 = 0;
- ctst->arch.vex.guest_r3 = 0;
- if (sp != 0)
- ctst->arch.vex.guest_r54 = sp;
-
- ctst->os_state.parent = ptid;
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
-
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
- ML_(guess_and_register_stack) (sp, ctst);
-
- VG_TRACK (pre_thread_ll_create, ptid, ctid);
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls at %#lx\n", child_tls);
- ctst->arch.vex.guest_r53 = child_tls;
- res = sys_set_tls(ctid, child_tls);
- if (sr_isError(res))
- goto out;
- }
-
- flags &= ~VKI_CLONE_SETTLS;
- VG_ (sigprocmask) (VKI_SIG_SETMASK, &blockall, &savedmask);
- /* Create the new thread */
- ret = do_syscall_clone_tilegx_linux (ML_ (start_thread_NORETURN),
- stack, flags, &VG_ (threads)[ctid],
- child_tidptr, parent_tidptr,
- (Long)NULL /*child_tls*/);
-
- /* High half word64 is syscall return value. */
- if (debug)
- VG_(printf)("ret: 0x%llx\n", (ULong)ret);
-
- res = VG_(mk_SysRes_tilegx_linux) (/*val */ ret);
-
- VG_ (sigprocmask) (VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError (res)) {
- VG_(cleanup_thread) (&ctst->arch);
- ctst->status = VgTs_Empty;
- VG_TRACK (pre_thread_ll_exit, ctid);
- }
- ptst->arch.vex.guest_r0 = 0;
-
- return res;
-}
-
extern Addr do_brk ( Addr newbrk );
extern
void
VG_ (cleanup_thread) ( ThreadArchState * arch ) { }
-void
-setup_child ( /*OUT*/ ThreadArchState * child,
- /*IN*/ ThreadArchState * parent )
-{
- /* We inherit our parent's guest state. */
- child->vex = parent->vex;
- child->vex_shadow1 = parent->vex_shadow1;
- child->vex_shadow2 = parent->vex_shadow2;
-}
-
-SysRes sys_set_tls ( ThreadId tid, Addr tlsptr )
-{
- VG_(threads)[tid].arch.vex.guest_r53 = tlsptr;
- return VG_(mk_SysRes_Success)( 0 );
-}
-
-
/* ---------------------------------------------------------------------
PRE/POST wrappers for tilegx/Linux-specific syscalls
------------------------------------------------------------------ */
aren't visible outside this file, but that requires even more macro
magic. */
-DECL_TEMPLATE (tilegx_linux, sys_clone);
DECL_TEMPLATE (tilegx_linux, sys_rt_sigreturn);
DECL_TEMPLATE (tilegx_linux, sys_socket);
DECL_TEMPLATE (tilegx_linux, sys_setsockopt);
DECL_TEMPLATE (tilegx_linux, sys_cacheflush);
DECL_TEMPLATE (tilegx_linux, sys_set_dataplane);
-PRE(sys_clone)
-{
- ULong cloneflags;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
- PRE_REG_READ5(int, "clone",
- unsigned long, flags,
- void *, child_stack,
- int *, parent_tidptr,
- int *, child_tidptr,
- void *, tlsaddr);
-
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int), VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- PRE_MEM_WRITE("clone(child_tidptr)", ARG4, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG4, sizeof(Int), VKI_PROT_WRITE)) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
- }
-
- cloneflags = ARG1;
-
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child ESP */
- (Long *)ARG3, /* parent_tidptr */
- (Long *)ARG4, /* child_tidptr */
- (Addr)ARG5)); /* set_tls */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG4)); /* child_tidptr */
- break;
-
- default:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg,
- "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg,
- "\n");
- VG_(message)(Vg_UserMsg,
- "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg,
- " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg,
- " - via the implementation of fork or vfork\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG4, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_rt_sigreturn)
{
/* This isn't really a syscall at all - it's a misuse of the
LINX_(__NR_add_key, sys_add_key), // 217
LINX_(__NR_request_key, sys_request_key), // 218
LINXY(__NR_keyctl, sys_keyctl), // 219
- PLAX_(__NR_clone, sys_clone), // 220
+ LINX_(__NR_clone, sys_clone), // 220
GENX_(__NR_execve, sys_execve), // 221
PLAX_(__NR_mmap, sys_mmap), // 222
GENXY(__NR_mprotect, sys_mprotect), // 226
#define __NR_CLONE VG_STRINGIFY(__NR_clone)
#define __NR_EXIT VG_STRINGIFY(__NR_exit)
-extern
-Int do_syscall_clone_x86_linux ( Word (*fn)(void *),
- void* stack,
- Int flags,
- void* arg,
- Int* child_tid,
- Int* parent_tid,
- vki_modify_ldt_t * );
+// See priv_syswrap-linux.h for arg profile.
asm(
".text\n"
".globl do_syscall_clone_x86_linux\n"
#undef __NR_EXIT
-// forward declarations
-static void setup_child ( ThreadArchState*, ThreadArchState*, Bool );
-static SysRes sys_set_thread_area ( ThreadId, vki_modify_ldt_t* );
-
-/*
- When a client clones, we need to keep track of the new thread. This means:
- 1. allocate a ThreadId+ThreadState+stack for 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_clone ( ThreadId ptid,
- UInt flags, Addr esp,
- Int* parent_tidptr,
- Int* child_tidptr,
- vki_modify_ldt_t *tlsinfo)
-{
- static const Bool debug = False;
-
- ThreadId ctid = VG_(alloc_ThreadState)();
- ThreadState* ptst = VG_(get_ThreadState)(ptid);
- ThreadState* ctst = VG_(get_ThreadState)(ctid);
- UWord* stack;
- 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;
-
- if (esp != 0)
- ctst->arch.vex.guest_ESP = esp;
-
- ctst->os_state.parent = ptid;
-
- /* inherit signal mask */
- ctst->sig_mask = ptst->sig_mask;
- ctst->tmp_sig_mask = ptst->sig_mask;
-
- /* Start the child with its threadgroup being the same as the
- parent's. This is so that any exit_group calls that happen
- after the child is created but before it sets its
- os_state.threadgroup field for real (in thread_wrapper in
- syswrap-linux.c), really kill the new thread. a.k.a this avoids
- a race condition in which the thread is unkillable (via
- exit_group) because its threadgroup is not set. The race window
- is probably only a few hundred or a few thousand cycles long.
- See #226116. */
- ctst->os_state.threadgroup = ptst->os_state.threadgroup;
-
- ML_(guess_and_register_stack) (esp, ctst);
-
- /* 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_assert(VG_(owns_BigLock_LL)(ptid));
- VG_TRACK ( pre_thread_ll_create, ptid, ctid );
-
- if (flags & VKI_CLONE_SETTLS) {
- if (debug)
- VG_(printf)("clone child has SETTLS: tls info at %p: idx=%u "
- "base=%#lx limit=%x; esp=%#x fs=%x gs=%x\n",
- tlsinfo, tlsinfo->entry_number,
- tlsinfo->base_addr, tlsinfo->limit,
- ptst->arch.vex.guest_ESP,
- ctst->arch.vex.guest_FS, ctst->arch.vex.guest_GS);
- res = sys_set_thread_area(ctid, tlsinfo);
- if (sr_isError(res))
- goto out;
- }
-
- flags &= ~VKI_CLONE_SETTLS;
-
- /* start the thread with everything blocked */
- VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
-
- /* Create the new thread */
- eax = do_syscall_clone_x86_linux(
- ML_(start_thread_NORETURN), stack, flags, &VG_(threads)[ctid],
- child_tidptr, parent_tidptr, NULL
- );
- res = VG_(mk_SysRes_x86_linux)( eax );
-
- VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
-
- out:
- if (sr_isError(res)) {
- /* 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;
-}
-
-
/* ---------------------------------------------------------------------
LDT/GDT simulation
------------------------------------------------------------------ */
}
-static SysRes sys_set_thread_area ( ThreadId tid, vki_modify_ldt_t* info )
+SysRes ML_(x86_sys_set_thread_area) ( ThreadId tid, vki_modify_ldt_t* info )
{
Int idx;
VexGuestX86SegDescr* gdt;
}
-static void setup_child ( /*OUT*/ ThreadArchState *child,
- /*IN*/ ThreadArchState *parent,
- Bool inherit_parents_GDT )
+void ML_(x86_setup_LDT_GDT) ( /*OUT*/ ThreadArchState *child,
+ /*IN*/ ThreadArchState *parent )
{
- /* 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. */
only). */
child->vex.guest_GDT = (HWord)NULL;
- if (inherit_parents_GDT && parent->vex.guest_GDT != (HWord)NULL) {
+ if (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 );
DECL_TEMPLATE(x86_linux, sys_fstatat64);
DECL_TEMPLATE(x86_linux, sys_fstat64);
DECL_TEMPLATE(x86_linux, sys_lstat64);
-DECL_TEMPLATE(x86_linux, sys_clone);
DECL_TEMPLATE(x86_linux, old_mmap);
DECL_TEMPLATE(x86_linux, sys_mmap2);
DECL_TEMPLATE(x86_linux, sys_sigreturn);
}
}
-PRE(sys_clone)
-{
- UInt cloneflags;
- Bool badarg = False;
-
- PRINT("sys_clone ( %lx, %#lx, %#lx, %#lx, %#lx )",ARG1,ARG2,ARG3,ARG4,ARG5);
- PRE_REG_READ2(int, "clone",
- unsigned long, flags,
- void *, child_stack);
-
- if (ARG1 & VKI_CLONE_PARENT_SETTID) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA3("clone", int *, parent_tidptr);
- }
- PRE_MEM_WRITE("clone(parent_tidptr)", ARG3, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG3, sizeof(Int),
- VKI_PROT_WRITE)) {
- badarg = True;
- }
- }
- if (ARG1 & VKI_CLONE_SETTLS) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA4("clone", vki_modify_ldt_t *, tlsinfo);
- }
- PRE_MEM_READ("clone(tlsinfo)", ARG4, sizeof(vki_modify_ldt_t));
- if (!VG_(am_is_valid_for_client)(ARG4, sizeof(vki_modify_ldt_t),
- VKI_PROT_READ)) {
- badarg = True;
- }
- }
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID)) {
- if (VG_(tdict).track_pre_reg_read) {
- PRA5("clone", int *, child_tidptr);
- }
- PRE_MEM_WRITE("clone(child_tidptr)", ARG5, sizeof(Int));
- if (!VG_(am_is_valid_for_client)(ARG5, sizeof(Int),
- VKI_PROT_WRITE)) {
- badarg = True;
- }
- }
-
- if (badarg) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
- }
-
- cloneflags = ARG1;
-
- if (!ML_(client_signal_OK)(ARG1 & VKI_CSIGNAL)) {
- SET_STATUS_Failure( VKI_EINVAL );
- return;
- }
-
- /* Be ultra-paranoid and filter out any clone-variants we don't understand:
- - ??? specifies clone flags of 0x100011
- - ??? specifies clone flags of 0x1200011.
- - NPTL specifies clone flags of 0x7D0F00.
- - The Quadrics Elan3 driver specifies clone flags of 0xF00.
- - Newer Quadrics Elan3 drivers with NTPL support specify 0x410F00.
- Everything else is rejected.
- */
- if (
- 1 ||
- /* 11 Nov 05: for the time being, disable this ultra-paranoia.
- The switch below probably does a good enough job. */
- (cloneflags == 0x100011 || cloneflags == 0x1200011
- || cloneflags == 0x7D0F00
- || cloneflags == 0x790F00
- || cloneflags == 0x3D0F00
- || cloneflags == 0x410F00
- || cloneflags == 0xF00
- || cloneflags == 0xF21)) {
- /* OK */
- }
- else {
- /* Nah. We don't like it. Go away. */
- goto reject;
- }
-
- /* Only look at the flags we really care about */
- switch (cloneflags & (VKI_CLONE_VM | VKI_CLONE_FS
- | VKI_CLONE_FILES | VKI_CLONE_VFORK)) {
- case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
- /* thread creation */
- SET_STATUS_from_SysRes(
- do_clone(tid,
- ARG1, /* flags */
- (Addr)ARG2, /* child ESP */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5, /* child_tidptr */
- (vki_modify_ldt_t *)ARG4)); /* set_tls */
- break;
-
- case VKI_CLONE_VFORK | VKI_CLONE_VM: /* vfork */
- /* FALLTHROUGH - assume vfork == fork */
- cloneflags &= ~(VKI_CLONE_VFORK | VKI_CLONE_VM);
-
- case 0: /* plain fork */
- SET_STATUS_from_SysRes(
- ML_(do_fork_clone)(tid,
- cloneflags, /* flags */
- (Int *)ARG3, /* parent_tidptr */
- (Int *)ARG5)); /* child_tidptr */
- break;
-
- default:
- reject:
- /* should we just ENOSYS? */
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "Unsupported clone() flags: 0x%lx\n", ARG1);
- VG_(message)(Vg_UserMsg, "\n");
- VG_(message)(Vg_UserMsg, "The only supported clone() uses are:\n");
- VG_(message)(Vg_UserMsg, " - via a threads library (LinuxThreads or NPTL)\n");
- VG_(message)(Vg_UserMsg, " - via the implementation of fork or vfork\n");
- VG_(message)(Vg_UserMsg, " - for the Quadrics Elan3 user-space driver\n");
- VG_(unimplemented)
- ("Valgrind does not support general clone().");
- }
-
- if (SUCCESS) {
- if (ARG1 & VKI_CLONE_PARENT_SETTID)
- POST_MEM_WRITE(ARG3, sizeof(Int));
- if (ARG1 & (VKI_CLONE_CHILD_SETTID | VKI_CLONE_CHILD_CLEARTID))
- POST_MEM_WRITE(ARG5, sizeof(Int));
-
- /* Thread creation was successful; let the child have the chance
- to run */
- *flags |= SfYieldAfter;
- }
-}
-
PRE(sys_sigreturn)
{
/* See comments on PRE(sys_rt_sigreturn) in syswrap-amd64-linux.c for
PRE_MEM_READ( "set_thread_area(u_info)", ARG1, sizeof(vki_modify_ldt_t) );
/* "do" the syscall ourselves; the kernel never sees it */
- SET_STATUS_from_SysRes( sys_set_thread_area( tid, (void *)ARG1 ) );
+ SET_STATUS_from_SysRes( ML_(x86_sys_set_thread_area)( tid, (void *)ARG1 ) );
}
PRE(sys_get_thread_area)
GENX_(__NR_fsync, sys_fsync), // 118
PLAX_(__NR_sigreturn, sys_sigreturn), // 119 ?/Linux
- PLAX_(__NR_clone, sys_clone), // 120
+ LINX_(__NR_clone, sys_clone), // 120
//zz // (__NR_setdomainname, sys_setdomainname), // 121 */*(?)
GENXY(__NR_uname, sys_newuname), // 122
PLAX_(__NR_modify_ldt, sys_modify_ldt), // 123
//ZZ };
//ZZ
//ZZ // [[Nb: for our convenience within Valgrind, use a more specific name]]
-//ZZ typedef struct vki_user_desc vki_modify_ldt_t;
+
+typedef char vki_modify_ldt_t;
//----------------------------------------------------------------------
// From linux-3.10.5/include/asm-generic/ipcbuf.h
};
// CAB: TODO
-typedef void vki_modify_ldt_t;
+typedef char vki_modify_ldt_t;
//----------------------------------------------------------------------
// From linux-2.6.35.5/include/asm-mips/ipcbuf.h
vki_sigset_t uc_sigmask; /* mask last for extensibility */
};
+typedef char vki_modify_ldt_t;
//----------------------------------------------------------------------
// From linux-2.6.35.9/include/asm-mips/ipcbuf.h
//----------------------------------------------------------------------
//.. };
//..
//.. // [[Nb: for our convenience within Valgrind, use a more specific name]]
-//.. typedef struct vki_user_desc vki_modify_ldt_t;
// CAB: TODO
-typedef void vki_modify_ldt_t;
+typedef char vki_modify_ldt_t;
//----------------------------------------------------------------------
struct vki_sigcontext uc_mcontext; /* last for extensibility */
};
+// CAB: TODO
+typedef char vki_modify_ldt_t;
+
//----------------------------------------------------------------------
// From linux-2.6.13/include/asm-ppc64/ipcbuf.h
//----------------------------------------------------------------------
vki_sigset_t uc_sigmask; /* mask last for extensibility */
};
+typedef char vki_modify_ldt_t;
+
//----------------------------------------------------------------------
// From linux-2.6.16.60/include/asm-s390/ipcbuf.h
//----------------------------------------------------------------------
blockfault.stderr.exp blockfault.vgtest \
brk-overflow1.stderr.exp brk-overflow1.vgtest \
brk-overflow2.stderr.exp brk-overflow2.vgtest \
+ clonev.stderr.exp clonev.vgtest \
mremap.stderr.exp mremap.stderr.exp-glibc27 mremap.stdout.exp \
mremap.vgtest \
mremap2.stderr.exp mremap2.stdout.exp mremap2.vgtest \
blockfault \
brk-overflow1 \
brk-overflow2 \
+ clonev \
mremap \
mremap2 \
mremap3 \
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
# Special needs
+clonev_LDADD = -lpthread
pthread_stack_LDADD = -lpthread
stack_overflow_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@ \
--- /dev/null
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <unistd.h>
+// Based on a test by Steven Stewart-Gallus, see 342040
+int fork_routine(void *arg)
+{
+ write(1, "fork_routine\n", 13);
+ _Exit(EXIT_SUCCESS);
+}
+
+int main(void)
+{
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ assert(page_size != -1);
+
+ /* We need an extra page for signals */
+ long stack_size = sysconf(_SC_THREAD_STACK_MIN) + page_size;
+ assert(stack_size != -1);
+
+ size_t stack_and_guard_size = page_size + stack_size + page_size;
+ void *child_stack = mmap(
+ NULL, stack_and_guard_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
+ if (NULL == child_stack) {
+ perror("mmap");
+ return EXIT_FAILURE;
+ }
+
+ /* Guard pages are shared between the stacks */
+ if (-1 == mprotect((char *)child_stack, page_size, PROT_NONE)) {
+ perror("mprotect");
+ return EXIT_FAILURE;
+ }
+
+ if (-1 == mprotect((char *)child_stack + page_size + stack_size,
+ page_size, PROT_NONE)) {
+ perror("mprotect");
+ return EXIT_FAILURE;
+ }
+
+ void *stack_start = (char *)child_stack + page_size + stack_size;
+ if (0)
+ printf("stack_start %p page_size %d stack_size %d\n",
+ stack_start, (int)page_size, (int)stack_size);
+ write(1, "parent before clone\n", 20);
+ pid_t child =
+ clone(fork_routine, stack_start,
+ SIGCHLD | CLONE_VFORK | CLONE_VM, NULL);
+ write(1, "parent after clone\n", 19);
+ if (-1 == child) {
+ perror("clone");
+ return EXIT_FAILURE;
+ }
+
+ for (;;) {
+ int xx;
+ switch (waitpid(child, &xx, 0)) {
+ case -1:
+ switch (errno) {
+ case EINTR:
+ continue;
+ default:
+ perror("waitpid");
+ return EXIT_FAILURE;
+ }
+
+ default:
+ return EXIT_SUCCESS;
+ }
+ }
+}
--- /dev/null
+parent before clone
+fork_routine
+parent after clone
--- /dev/null
+prog: clonev
+vgopts: -q