Skip to content

Instantly share code, notes, and snippets.

@kostikbel
Created November 9, 2020 11:43
Show Gist options
  • Save kostikbel/d2894a187ab1e16f6fb1cddf39f7b969 to your computer and use it in GitHub Desktop.
Save kostikbel/d2894a187ab1e16f6fb1cddf39f7b969 to your computer and use it in GitHub Desktop.
/* $Id: thread-dbreg-test-pt2.c,v 1.7 2020/11/09 11:42:26 kostik Exp kostik $ */
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <machine/reg.h>
#include <assert.h>
#include <err.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
volatile int g_val;
volatile int g_val2;
static void *
thread_func(void* foo __unused) {
return (NULL);
}
static void
waitdbg(pid_t pid, int sig)
{
pid_t waited;
int nthr, ret;
waited = waitpid(pid, &ret, 0);
printf("wait: pid=%d, waited=%d, ret=%x\n", pid, waited, ret);
assert(waited == pid);
if (sig < 0) {
assert(WIFEXITED(ret));
return;
}
assert(WSTOPSIG(ret) == sig);
nthr = ptrace(PT_GETNUMLWPS, pid, NULL, 0);
if (nthr == -1)
err(1, "PT_GETNUMLWPS");
lwpid_t ids[nthr];
ret = ptrace(PT_GETLWPLIST, pid, (caddr_t)&ids, nthr);
if (ret == -1)
err(1, "PT_GETLWPLIST");
printf("threads:");
for (int i = 0; i < nthr; i++)
printf(" %d", ids[i]);
printf("\n");
}
int
main(void)
{
int ret;
int pid = fork();
assert(pid != -1);
if (pid == 0) {
pthread_t t2;
ret = ptrace(PT_TRACE_ME, 0, NULL, 0);
assert(ret != -1);
// 2. wait for parent to set dbregs
raise(SIGSTOP);
// 4. start new thread and stop immediately afterwards
ret = pthread_create(&t2, NULL, thread_func, NULL);
assert(ret == 0);
raise(SIGSTOP);
printf("thread started\n");
ret = pthread_join(t2, NULL);
assert(ret == 0);
printf("thread joined\n");
_exit(0);
}
// 1. wait for the child to start
waitdbg(pid, SIGSTOP);
struct ptrace_lwpinfo info;
ret = ptrace(PT_LWPINFO, pid, (caddr_t)&info, sizeof(info));
assert(ret != -1);
assert(info.pl_flags == PL_FLAG_SI);
printf("tid = %d, SIGSTOP\n", info.pl_lwpid);
// 3. set event mask & dbregs
int ev = PTRACE_LWP;
ret = ptrace(PT_SET_EVENT_MASK, pid, (caddr_t)&ev, sizeof(ev));
assert(ret != -1);
struct dbreg db;
ret = ptrace(PT_GETDBREGS, info.pl_lwpid, (caddr_t)&db, 0);
assert(ret != -1);
db.dr[0] = (uintptr_t)&g_val;
db.dr[7] = 0x303;
ret = ptrace(PT_SETDBREGS, info.pl_lwpid, (caddr_t)&db, 0);
assert(ret != -1);
printf("set DR0=%p (&g_val) on tid=%d\n", (void*)db.dr[0], info.pl_lwpid);
ret = ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
assert(ret != -1);
// 5. we should get SIGSTOP first, change dbregs
waitdbg(pid, SIGSTOP);
ret = ptrace(PT_LWPINFO, pid, (caddr_t)&info, sizeof(info));
assert(ret != -1);
assert(info.pl_flags == PL_FLAG_SI);
printf("tid = %d, SIGSTOP\n", info.pl_lwpid);
ret = ptrace(PT_GETDBREGS, info.pl_lwpid, (caddr_t)&db, 0);
assert(ret != -1);
db.dr[0] = (uintptr_t)&g_val2;
db.dr[7] = 0x303;
ret = ptrace(PT_SETDBREGS, info.pl_lwpid, (caddr_t)&db, 0);
assert(ret != -1);
printf("set DR0=%p (&g_val2) on tid=%d\n", (void*)db.dr[0], info.pl_lwpid);
ret = ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
assert(ret != -1);
// 6. now we should get the new thread event
waitdbg(pid, SIGTRAP);
ret = ptrace(PT_LWPINFO, pid, (caddr_t)&info, sizeof(info));
assert(ret != -1);
assert(info.pl_flags & PL_FLAG_BORN);
printf("tid = %d, SIGTRAP w/ PL_FLAG_BORN\n", info.pl_lwpid);
ret = ptrace(PT_GETDBREGS, info.pl_lwpid, (caddr_t)&db, 0);
assert(ret != -1);
printf("dr0=%p, g_val=%p, g_val2=%p\n", (void*)db.dr[0], &g_val, &g_val2);
ret = ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
assert(ret != -1);
// thread exited
waitdbg(pid, SIGTRAP);
ret = ptrace(PT_LWPINFO, pid, (caddr_t)&info, sizeof(info));
assert(ret != -1);
assert(info.pl_flags & PL_FLAG_EXITED);
printf("tid = %d, SIGTRAP w/ PL_FLAG_EXITED\n", info.pl_lwpid);
ret = ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
assert(ret != -1);
waitdbg(pid, -1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment