libunwind-ptrace -- ptrace() support in libunwind
#include <libunwind-ptrace.h>
unw_accessors_t _UPT_accessors;
void *_UPT_create(pid_t);
void _UPT_destroy(void *);
int _UPT_find_proc_info(unw_addr_space_t, unw_word_t,
unw_proc_info_t *, int, void *);
void _UPT_put_unwind_info(unw_addr_space_t, unw_proc_info_t *, void *);
int _UPT_get_dyn_info_list_addr(unw_addr_space_t, unw_word_t *, void *);
int _UPT_access_mem(unw_addr_space_t, unw_word_t, unw_word_t *, int, void *);
int _UPT_access_reg(unw_addr_space_t, unw_regnum_t, unw_word_t *, int, void
*);
int _UPT_access_fpreg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t *, int, void
*);
int _UPT_get_proc_name(unw_addr_space_t, unw_word_t, char *, size_t,
unw_word_t *, void *);
int _UPT_resume(unw_addr_space_t, unw_cursor_t *, void *);
The ptrace(2) system call makes it possible for a process to gain
access to the machine state and virtual memory of another process.
With the right set of callback routines, it is therefore possible to hook up
libunwind to another process via ptrace(2). While it's not very difficult to
do so directly, libunwind further facilitates this task by providing
ready-to-use callbacks for this purpose. The routines and variables
implementing this facility use a name prefix of _UPT, which is stands for
``unwind via ptrace''.
An application that wants to use the libunwind ptrace remote needs
to take the folowing steps.
- 1.
- Create a new libunwind address space that represents the target process.
This is done by calling unw_create_addr_space(). In many cases, the
application will simply want to pass the address of _UPT_accessors as the
first argument to this routine. Doing so will ensure that libunwind will
be able to properly unwind the target process.
- 2.
- Turn on ptrace mode on the target process, either by forking a new
process, invoking PTRACE_TRACEME, and then starting the target program
(via execve(2)), or by directly attaching to an already running process
(via PTRACE_ATTACH).
- 3.
- Once the process-ID (pid) of the target process is known, a UPT info
structure can be created by calling _UPT_create(), passing the pid of the
target process as the only argument.
- 4.
- The opaque pointer returned then needs to be passed as the ``argument''
pointer (third argument) to unw_init_remote().
In special circumstances, an application may prefer to use only
portions of the libunwind ptrace remote. For this reason, the individual
callback routines (_UPT_find_proc_info(), _UPT_put_unwind_info(), etc.) are
also available for direct use. Of course, the addresses of these routines
could also be picked up from _UPT_accessors, but doing so would prevent
static initialization. Also, when using _UPT_accessors, all the
callback routines will be linked into the application, even if they are
never actually called.
The _UPT_resume() routine can be used to resume execution of the
target process. It simply invokes ptrace(2) with a command value of
PTRACE_CONT.
When the application is done using libunwind on the target
process, _UPT_destroy() needs to be called, passing it the opaque pointer
that was returned by the call to _UPT_create(). This ensures that all memory
and other resources are freed up.
Since ptrace(2) works within a single machine only, the libunwind
ptrace remote by definition is not available in libunwind versions
configured for cross-unwinding.
The libunwind ptrace remote assumes that a single UPT info
structure is never shared between threads. Because of this, no explicit
locking is used. As long as only one thread uses a UPT info structure at any
given time, this facility is thread-safe.
_UPT_create() may return a NULL pointer if it fails to create the
UPT info structure for any reason. For the current implementation, the only
reason this call may fail is when the system is out of memory.
#include <libunwind-ptrace.h>
#include <stdio.h>
#include <stdlib.h>
int
main (int argc, char **argv)
{
if (argc != 2) {
fprintf (stderr, "usage: %s PID\n", argv[0]);
exit (EXIT_FAILURE);
}
char *endptr;
pid_t target_pid = strtoul (argv[1], &endptr, 10);
if (target_pid == 0 && argv[1] == endptr) {
fprintf (stderr, "usage: %s PID\n", argv[0]);
exit (EXIT_FAILURE);
}
unw_addr_space_t as = unw_create_addr_space (&_UPT_accessors, 0);
if (!as) {
fprintf (stderr, "unw_create_addr_space() failed");
exit (EXIT_FAILURE);
}
void *ui = _UPT_create (target_pid);
if (!ui) {
fprintf (stderr, "_UPT_create() failed");
exit (EXIT_FAILURE);
}
unw_cursor_t cursor;
int ret = unw_init_remote (&cursor, as, ui);
if (ret < 0) {
fprintf (stderr, "unw_init_remote() failed: ret=%d\n", ret);
exit (EXIT_FAILURE);
}
do {
unw_proc_info_t pi;
ret = unw_get_proc_info (&cursor, &pi);
if (ret == -UNW_ENOINFO) {
fprintf (stdout, "no info\n");
} else if (ret >= 0) {
printf ("\tproc=%#016lx-%#016lx\thandler=%#016lx lsda=%#016lx",
(long) pi.start_ip, (long) pi.end_ip,
(long) pi.handler, (long) pi.lsda);
}
ret = unw_step (&cursor);
} while (ret > 0);
if (ret < 0) {
fprintf (stderr, "unwind failed with ret=%d\n", ret);
exit (EXIT_FAILURE);
}
_UPT_destroy (ui);
unw_destroy_addr_space (as);
exit (EXIT_SUCCESS);
}
execve(2), libunwind(3libunwind), ptrace(2)
David Mosberger-Tang
Email: dmosberger@gmail.com
WWW: http://www.nongnu.org/libunwind/.