/* Copyright 2007, Red Hat Inc. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) static __attribute__((__noreturn__)) void crash (void) { #if 0 void *array[0x100]; int count; #endif char command[256]; fputs (">>> CRASH START\n", stderr); #if 0 count = backtrace (array, ARRAY_SIZE (array)); backtrace_symbols_fd (array, count, STDERR_FILENO); #endif snprintf (command, sizeof (command), "echo -e \"bt\\nquit\"" " >/tmp/debugger.%d; gdb --batch -nx --command=/tmp/debugger.%d" " /proc/%d/exe %d >> CRASH FINISH\n", stderr); abort (); } struct ps_prochandle { pid_t pid; Dwfl *dwfl; }; static Dwfl *get_dwfl (struct ps_prochandle *proc_handle) { static char *debuginfo_path; static const Dwfl_Callbacks proc_callbacks = { .find_debuginfo = dwfl_standard_find_debuginfo, .debuginfo_path = &debuginfo_path, .find_elf = dwfl_linux_proc_find_elf, }; if (proc_handle->dwfl == NULL) { proc_handle->dwfl = dwfl_begin (&proc_callbacks); assert (proc_handle->dwfl != NULL); errno = 0; if (dwfl_linux_proc_report (proc_handle->dwfl, proc_handle->pid) != 0 || dwfl_report_end (proc_handle->dwfl, NULL, NULL) != 0) { fprintf (stderr, "dwfl reporting: %m\n"); dwfl_end (proc_handle->dwfl); proc_handle->dwfl = NULL; abort (); } } return proc_handle->dwfl; } /* Functions in this interface return one of these status codes. */ typedef enum { PS_OK, /* Generic "call succeeded". */ PS_ERR, /* Generic error. */ PS_BADPID, /* Bad process handle. */ PS_BADLID, /* Bad LWP identifier. */ PS_BADADDR, /* Bad address. */ PS_NOSYM, /* Could not find given symbol. */ PS_NOFREGS /* FPU register set not available for given LWP. */ } ps_err_e; ps_err_e ps_pdread (struct ps_prochandle *proc_handle, psaddr_t addr, void *buffer, size_t length) { pid_t pid = proc_handle->pid; char filename[64]; int fd; if (pid == getpid ()) { memcpy (buffer, addr, length); return PS_OK; } if (ptrace (PTRACE_ATTACH, pid, NULL, NULL) != 0) abort (); if (waitpid (pid, NULL, 0) != pid) abort (); snprintf (filename, sizeof (filename), "/proc/%ld/mem", (long) pid); fd = open (filename, O_RDONLY); assert (fd != -1); if (lseek64 (fd, (off64_t) addr, SEEK_SET) != (off64_t) addr) abort (); if (read (fd, buffer, length) != length) { fprintf (stderr, "read() error @0x%lx length %lu: %m\n", (unsigned long) addr, (unsigned long) length); abort (); } if (close (fd) != 0) abort (); if (ptrace (PTRACE_DETACH, pid, NULL, NULL) != 0) abort (); return PS_OK; } ps_err_e ps_pdwrite (struct ps_prochandle *proc_handle, psaddr_t addr, const void *buffer, size_t length) { crash (); } ps_err_e ps_lgetregs (struct ps_prochandle *proc_handle, lwpid_t lwp, prgregset_t regs) { crash (); } ps_err_e ps_lsetregs (struct ps_prochandle *proc_handle, lwpid_t lwp, const prgregset_t regs) { crash (); } ps_err_e ps_lgetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp, prfpregset_t *fpregs) { crash (); } ps_err_e ps_lsetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp, const prfpregset_t *fpregs) { crash (); } /* Return the PID of the process. */ pid_t ps_getpid (struct ps_prochandle *proc_handle) { return proc_handle->pid; } struct getmodules_callback_arg { const char *sym_name; psaddr_t *sym_addr; ps_err_e retval; }; static int getmodules_callback (Dwfl_Module *module, void **module_userdata_pointer, const char *module_name, Dwarf_Addr module_low_addr, void *arg) { struct getmodules_callback_arg *getmodules_callback_arg = arg; int sym_count, ndx; GElf_Sym sym; sym_count = dwfl_module_getsymtab (module); assert (sym_count >= 0); for (ndx = 0; ndx < sym_count; ndx++) { const char *name_got; name_got = dwfl_module_getsym (module, ndx, &sym, NULL); assert (name_got != NULL); if (strcmp (name_got, getmodules_callback_arg->sym_name) == 0) break; } if (ndx == sym_count) return DWARF_CB_OK; *getmodules_callback_arg->sym_addr = (psaddr_t) sym.st_value; getmodules_callback_arg->retval = PS_OK; return DWARF_CB_OK; } /* Look up the named symbol in the named DSO in the symbol tables associated with the process being debugged, filling in *SYM_ADDR with the corresponding run-time address. */ ps_err_e ps_pglobal_lookup (struct ps_prochandle *proc_handle, const char *object_name, const char *sym_name, psaddr_t *sym_addr) { Dwfl *dwfl = get_dwfl (proc_handle); struct getmodules_callback_arg getmodules_callback_arg; ptrdiff_t err_ptrdiff; /* FIXME: `object_name' ignored due to missing unresolving of shared libraries symbolic links. */ getmodules_callback_arg.sym_name = sym_name; getmodules_callback_arg.sym_addr = sym_addr; getmodules_callback_arg.retval = PS_NOSYM; err_ptrdiff = dwfl_getmodules (dwfl, getmodules_callback, &getmodules_callback_arg, 0); assert (err_ptrdiff == 0); return getmodules_callback_arg.retval; } #ifdef __x86_64__ ps_err_e ps_get_thread_area (const struct ps_prochandle *ph, lwpid_t lwpid, int idx, void **base) { long val; assert (idx == FS || idx == GS); if (ptrace (PTRACE_ATTACH, lwpid, NULL, NULL) != 0) abort (); if (waitpid (lwpid, NULL, 0) != lwpid) abort (); val = ptrace (PTRACE_ARCH_PRCTL, lwpid, base, (idx == FS ? ARCH_GET_FS : ARCH_GET_GS)); if (ptrace (PTRACE_DETACH, lwpid, NULL, NULL) != 0) abort (); if (val != 0) { fprintf (stderr, "PTRACE_ARCH_PRCTL (%s): %m\n", (idx == FS ? "FS" : "GS")); return PS_ERR; } return PS_OK; } #else #error "Unsupported ps_get_thread_area ()!" #endif static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data) { td_err_e err; td_thrinfo_t info; err = td_thr_get_info (th_p, &info); assert (err == TD_OK); printf ("LWP = %ld TID = 0x%lx\n", (long) info.ti_lid, info.ti_tid); return 0; } static void thread_test (td_thragent_t *thread_agent) { td_err_e err; err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); if (err != TD_OK) { fprintf (stderr, "err = %d\n", err); abort (); } } static void attach (pid_t pid) { td_err_e err; td_thragent_t *thread_agent; struct ps_prochandle proc_handle_local; err = td_init (); assert (err == TD_OK); proc_handle_local.pid = pid; proc_handle_local.dwfl = NULL; err = td_ta_new (&proc_handle_local, &thread_agent); assert (err == TD_OK || err == TD_NOLIBTHREAD); if (err == TD_NOLIBTHREAD) puts ("singlethreaded"); else { puts ("multithreaded"); thread_test (thread_agent); err = td_ta_delete (thread_agent); assert (err == TD_OK); } if (proc_handle_local.dwfl != NULL); dwfl_end (proc_handle_local.dwfl); } static void *start (void *arg) { sleep (1); return arg; } static __attribute__((__noreturn__)) void syntax (void) { puts ("threadtest {|multi|single}"); exit (EXIT_FAILURE); } int main (int argc, char **argv) { if (argc != 2) syntax (); if (strcmp (argv[1], "single") == 0) attach (getpid ()); else if (strcmp (argv[1], "multi") == 0) { void *handle; int (*pthread_create_pointer) (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); pthread_t thread; int i; /* Do not access pthread_create () directly as we would need to link `-lpthread' and we could never provide the `single' option. */ handle = dlopen ("libpthread.so.0", RTLD_LAZY); assert (handle != NULL); pthread_create_pointer = dlsym (handle, "pthread_create"); assert (pthread_create_pointer != NULL); i = (*pthread_create_pointer) (&thread, NULL, start, NULL); assert (i == 0); i = (*pthread_create_pointer) (&thread, NULL, start, NULL); assert (i == 0); attach (getpid ()); } else { char *endptr; long pidl = strtol (argv[1], &endptr, 0); if (pidl < 0 || (pid_t) pidl != pidl || (endptr != NULL && *endptr != 0)) syntax (); attach (pidl); } return EXIT_SUCCESS; }