Sunday, December 08, 2019

Valgrind: patch to run Pthread target program

Hi, ports

Valgrind does not run Pthread target program.
I made patch to run Pthread target program.

I applied this patch and run attached main.c as:

$ cc main.c -lpthread
$ while :; do valgrind ./a.out 32 8; done

for 24 hours. I got no problem.

ok?

Index: patches/patch-coregrind_m_libcsignal_c
===================================================================
RCS file: patches/patch-coregrind_m_libcsignal_c
diff -N patches/patch-coregrind_m_libcsignal_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-coregrind_m_libcsignal_c 9 Dec 2019 05:30:18 -0000
@@ -0,0 +1,21 @@
+--- coregrind/m_libcsignal.c.orig
++++ coregrind/m_libcsignal.c
+@@ -397,6 +397,7 @@
+ Int VG_(tkill)( Int lwpid, Int signo )
+ {
+ # if defined(__NR_tkill)
++ PRINT("sys___tfork ( %ld, %ld )",ARG1,ARG2);
+ SysRes res = VG_(mk_SysRes_Error)(VKI_ENOSYS);
+ res = VG_(do_syscall2)(__NR_tkill, lwpid, signo);
+ if (sr_isError(res) && sr_Err(res) == VKI_ENOSYS)
+@@ -415,7 +416,9 @@
+ return sr_isError(res) ? -1 : 0;
+
+ # elif defined(VGO_openbsd)
+- I_die_here;
++ SysRes res;
++ res = VG_(do_syscall3)(__NR_thrkill, lwpid, signo, NULL);
++ return sr_isError(res) ? -1 : 0;
+
+ # else
+ # error "Unsupported plat"
Index: patches/patch-coregrind_m_syswrap_priv_syswrap_openbsd_h
===================================================================
RCS file: /cvs/ports/devel/valgrind/patches/patch-coregrind_m_syswrap_priv_syswrap_openbsd_h,v
retrieving revision 1.1
diff -u -p -r1.1 patch-coregrind_m_syswrap_priv_syswrap_openbsd_h
--- patches/patch-coregrind_m_syswrap_priv_syswrap_openbsd_h 23 Oct 2019 02:35:05 -0000 1.1
+++ patches/patch-coregrind_m_syswrap_priv_syswrap_openbsd_h 9 Dec 2019 05:30:18 -0000
@@ -1,5 +1,14 @@
--- coregrind/m_syswrap/priv_syswrap-openbsd.h.orig
+++ coregrind/m_syswrap/priv_syswrap-openbsd.h
+@@ -50,7 +50,7 @@
+ DECL_TEMPLATE(openbsd, sys_open);
+ DECL_TEMPLATE(openbsd, sys_close);
+ DECL_TEMPLATE(openbsd, sys_getentropy);
+-DECL_TEMPLATE(openbsd, sys___tfork);
++DECL_TEMPLATE(openbsd, sys___tfork_thread);
+ DECL_TEMPLATE(openbsd, sys_link);
+ DECL_TEMPLATE(openbsd, sys_unlink);
+ DECL_TEMPLATE(openbsd, sys_wait4);
@@ -144,6 +144,7 @@
DECL_TEMPLATE(openbsd, sys_pledge);
DECL_TEMPLATE(openbsd, sys_ppoll);
Index: patches/patch-coregrind_m_syswrap_syswrap_amd64_openbsd_c
===================================================================
RCS file: patches/patch-coregrind_m_syswrap_syswrap_amd64_openbsd_c
diff -N patches/patch-coregrind_m_syswrap_syswrap_amd64_openbsd_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-coregrind_m_syswrap_syswrap_amd64_openbsd_c 9 Dec 2019 05:30:18 -0000
@@ -0,0 +1,215 @@
+--- coregrind/m_syswrap/syswrap-amd64-openbsd.c.orig
++++ coregrind/m_syswrap/syswrap-amd64-openbsd.c
+@@ -120,6 +120,212 @@
+ #define PRE(name) DEFN_PRE_TEMPLATE(openbsd, name)
+ #define POST(name) DEFN_POST_TEMPLATE(openbsd, name)
+
++extern UWord do_syscall_tfork_amd64_openbsd (
++ Addr params,
++ UWord psize,
++ Addr startfunc,
++ Addr startarg);
++asm(
++".text\n"
++".globl do_syscall_tfork_amd64_openbsd\n"
++"do_syscall_tfork_amd64_openbsd:\n"
++ /* Copy %rdx to %r8 and %rcx to %r9. See:
++ - lib/libc/arch/amd64/sys/tfork_thread.S */
++" movq %rdx, %r8\n"
++" movq %rcx, %r9\n"
++" movq $8, %rax\n" /* syscall_no */
++" syscall\n"
++" jb 5f\n" /* error */
++ /*
++ * Check to see if we are in the parent or child
++ */
++" cmpl $0, %eax\n"
++" jz 4f\n" /* child */
++" jmp 5f\n" /* parent */
++ /* the retpoline we'll use to call the child's main */
++".align 16, 0xcc\n" /* _ALIGN_TRAPS */
++"1:\n" /* JMP_RETPOLINE(r8) --> */
++" call 3f\n"
++"2: pause\n"
++" lfence\n"
++" jmp 2b\n"
++".align 16, 0xcc\n" /* _ALIGN_TRAPS */
++"3: mov %r8,(%rsp)\n"
++" ret\n"
++ /* JMP_RETPOLINE(r8) <-- */
++ /*
++ * If we are in the child (new thread), then
++ * set-up the call to the internal subroutine. If it
++ * returns, then call __threxit.
++ */
++".align 16, 0xcc\n" /* _ALIGN_TRAPS */
++"4:\n"
++" movq %r9, %rdi\n"
++" call 1b\n"
++ /*
++ * Thread exit system call
++ */
++" movl $302, %eax\n" /* 302 == SYS___threxit */
++" xorl %edi, %edi\n"
++" syscall\n"
++ /*NOTREACHED*/
++"5:\n" /* parent or error */
++" ret\n"
++".previous\n"
++);
++
++static void setup_child ( ThreadArchState*, ThreadArchState* );
++
++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 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.
++
++ This function was implemented with reference to the syswrap-amd64-linux.c:
++ do_clone() function.
++ */
++static SysRes do_tfork_thread ( ThreadId ptid,
++ Addr params,
++ ULong psize,
++ Addr startfunc,
++ Addr startarg)
++{
++ 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;
++ struct __vki_tfork * tfork;
++
++ VG_(sigfillset)(&blockall);
++
++ vg_assert(params != 0);
++ vg_assert(psize > 0);
++ vg_assert(startfunc != 0);
++ 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 __tfork_thread 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 tfork->tf_stack for the new thread,
++ then it actually gets a copy of the parent's rsp.
++ */
++ setup_child( &ctst->arch, &ptst->arch );
++
++ /* Make sys___tfork appear to have returned Success(0) in the
++ child. */
++ ctst->arch.vex.guest_RAX = 0;
++
++ tfork = (struct __vki_tfork *)params;
++ vg_assert(tfork->tf_stack != 0);
++ ctst->arch.vex.guest_RSP = (ULong) tfork->tf_stack;
++ tfork->tf_stack = stack;
++
++ 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) ((Addr)tfork->tf_stack, 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 );
++
++ /* start the thread with everything blocked */
++#if 1 /* for debug by asou */
++ VG_(sigdelset)(&blockall, VKI_SIGTRAP);
++

No comments:

Post a Comment