+2014-03-04 Mark Wielaard <mjw@redhat.com>
+
+ * libdwflP.h (struct __libdwfl_pid_arg): Moved here and renamed from
+ linux-pid-attach.c (struct pid_arg).
+ (__libdwfl_get_pid_arg): New internal function declaration.
+ (__libdwfl_ptrace_attach): Likewise.
+ (__libdwfl_ptrace_detach): Likewise.
+ * dwfl_frame.c (dwfl_attach_state): Add "(deleted)" files to the
+ special exception modules that cannot be checked at this point.
+ * linux-pid-attach.c (struct pid_arg): Moved to libdwflP.h
+ (ptrace_attach): Renamed to...
+ (__libdwfl_ptrace_attach): New internal function.
+ (__libdwfl_ptrace_detach): Likewise. Extracted from ...
+ (pid_thread_detach): Call __libdwfl_ptrace_detach now.
+ (__libdwfl_get_pid_arg): New internal function.
+ * linux-proc-maps.c (dwfl_linux_proc_find_elf): Check if special
+ module name contains "(deleted)" and dwfl_pid gives an attached
+ pid. If pid is set and try to (re)use ptrace attach state of
+ process before reading memory.
+
2014-03-03 Mark Wielaard <mjw@redhat.com>
* elf-from-memory.c (elf_from_remote_memory): Take pagesize as
ebl = NULL;
for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
{
- /* Reading of the vDSO module may fail as /proc/PID/mem is unreadable
- without PTRACE_ATTACH and we may not be PTRACE_ATTACH-ed now.
- MOD would not be re-read later to unwind it when we are already
- PTRACE_ATTACH-ed to PID. */
- if (strncmp (mod->name, "[vdso: ", 7) == 0)
+ /* Reading of the vDSO or (deleted) modules may fail as
+ /proc/PID/mem is unreadable without PTRACE_ATTACH and
+ we may not be PTRACE_ATTACH-ed now. MOD would not be
+ re-read later to unwind it when we are already
+ PTRACE_ATTACH-ed to PID. This happens when this function
+ is called from dwfl_linux_proc_attach with elf == NULL.
+ __libdwfl_module_getebl will call __libdwfl_getelf which
+ will call the find_elf callback. */
+ if (strncmp (mod->name, "[vdso: ", 7) == 0
+ || strcmp (strrchr (mod->name, ' ') ?: "",
+ " (deleted)") == 0)
continue;
Dwfl_Error error = __libdwfl_module_getebl (mod);
if (error != DWFL_E_NOERROR)
/* Internal definitions for libdwfl.
- Copyright (C) 2005-2013 Red Hat, Inc.
+ Copyright (C) 2005-2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
#include <libdwfl.h>
#include <libebl.h>
#include <assert.h>
+#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
};
+/* Structure used for keeping track of ptrace attaching a thread.
+ Shared by linux-pid-attach and linux-proc-maps. If it has been setup
+ then get the instance through __libdwfl_get_pid_arg. */
+struct __libdwfl_pid_arg
+{
+ DIR *dir;
+ /* It is 0 if not used. */
+ pid_t tid_attached;
+ /* Valid only if TID_ATTACHED is not zero. */
+ bool tid_was_stopped;
+ /* True if threads are ptrace stopped by caller. */
+ bool assume_ptrace_stopped;
+};
+
+/* If DWfl is not NULL and a Dwfl_Process has been setup that has
+ Dwfl_Thread_Callbacks set to pid_thread_callbacks, then return the
+ callbacks_arg, which will be a struct __libdwfl_pid_arg. Otherwise
+ returns NULL. */
+extern struct __libdwfl_pid_arg *__libdwfl_get_pid_arg (Dwfl *dwfl)
+ internal_function;
+
+/* Makes sure the given tid is attached. On success returns true and
+ sets tid_was_stopped. */
+extern bool __libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
+ internal_function;
+
+/* Detaches a tid that was attached through
+ __libdwfl_ptrace_attach. Must be given the tid_was_stopped as set
+ by __libdwfl_ptrace_attach. */
+extern void __libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped)
+ internal_function;
+
+
/* Internal wrapper for old dwfl_module_getsym and new dwfl_module_getsym_info.
adjust_st_value set to true returns adjusted SYM st_value, set to false
it will not adjust SYM at all, but does match against resolved *ADDR. */
/* Get Dwarf Frame state for target live PID process.
- Copyright (C) 2013 Red Hat, Inc.
+ Copyright (C) 2013, 2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
# define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
-struct pid_arg
-{
- DIR *dir;
- /* It is 0 if not used. */
- pid_t tid_attached;
- /* Valid only if TID_ATTACHED is not zero. */
- bool tid_was_stopped;
- /* True if threads are ptrace stopped by caller. */
- bool assume_ptrace_stopped;
-};
static bool
linux_proc_pid_is_stopped (pid_t pid)
return retval;
}
-static bool
-ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
+bool
+internal_function
+__libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
{
if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
{
static bool
pid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
{
- struct pid_arg *pid_arg = arg;
+ struct __libdwfl_pid_arg *pid_arg = arg;
pid_t tid = pid_arg->tid_attached;
assert (tid > 0);
Dwfl_Process *process = dwfl->process;
pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
void **thread_argp)
{
- struct pid_arg *pid_arg = dwfl_arg;
+ struct __libdwfl_pid_arg *pid_arg = dwfl_arg;
struct dirent *dirent;
/* Start fresh on first traversal. */
if (*thread_argp == NULL)
static bool
pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
{
- struct pid_arg *pid_arg = thread_arg;
+ struct __libdwfl_pid_arg *pid_arg = thread_arg;
assert (pid_arg->tid_attached == 0);
pid_t tid = INTUSE(dwfl_thread_tid) (thread);
if (! pid_arg->assume_ptrace_stopped
- && ! ptrace_attach (tid, &pid_arg->tid_was_stopped))
+ && ! __libdwfl_ptrace_attach (tid, &pid_arg->tid_was_stopped))
return false;
pid_arg->tid_attached = tid;
Dwfl_Process *process = thread->process;
static void
pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
{
- struct pid_arg *pid_arg = dwfl_arg;
+ struct __libdwfl_pid_arg *pid_arg = dwfl_arg;
closedir (pid_arg->dir);
free (pid_arg);
}
+void
+internal_function
+__libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped)
+{
+ /* This handling is needed only on older Linux kernels such as
+ 2.6.32-358.23.2.el6.ppc64. Later kernels such as
+ 3.11.7-200.fc19.x86_64 remember the T (stopped) state
+ themselves and no longer need to pass SIGSTOP during
+ PTRACE_DETACH. */
+ ptrace (PTRACE_DETACH, tid, NULL,
+ (void *) (intptr_t) (tid_was_stopped ? SIGSTOP : 0));
+}
+
static void
pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
{
- struct pid_arg *pid_arg = thread_arg;
+ struct __libdwfl_pid_arg *pid_arg = thread_arg;
pid_t tid = INTUSE(dwfl_thread_tid) (thread);
assert (pid_arg->tid_attached == tid);
pid_arg->tid_attached = 0;
if (! pid_arg->assume_ptrace_stopped)
- {
- /* This handling is needed only on older Linux kernels such as
- 2.6.32-358.23.2.el6.ppc64. Later kernels such as
- 3.11.7-200.fc19.x86_64 remember the T (stopped) state
- themselves and no longer need to pass SIGSTOP during
- PTRACE_DETACH. */
- ptrace (PTRACE_DETACH, tid, NULL,
- (void *) (intptr_t) (pid_arg->tid_was_stopped ? SIGSTOP : 0));
- }
+ __libdwfl_ptrace_detach (tid, pid_arg->tid_was_stopped);
}
static const Dwfl_Thread_Callbacks pid_thread_callbacks =
DIR *dir = opendir (dirname);
if (dir == NULL)
return errno;
- struct pid_arg *pid_arg = malloc (sizeof *pid_arg);
+ struct __libdwfl_pid_arg *pid_arg = malloc (sizeof *pid_arg);
if (pid_arg == NULL)
{
closedir (dir);
return 0;
}
INTDEF (dwfl_linux_proc_attach)
+
+struct __libdwfl_pid_arg *
+internal_function
+__libdwfl_get_pid_arg (Dwfl *dwfl)
+{
+ if (dwfl != NULL && dwfl->process != NULL
+ && dwfl->process->callbacks == &pid_thread_callbacks)
+ return (struct __libdwfl_pid_arg *) dwfl->process->callbacks_arg;
+
+ return NULL;
+}
const char *module_name, Dwarf_Addr base,
char **file_name, Elf **elfp)
{
+ int pid = -1;
if (module_name[0] == '/')
{
/* When this callback is used together with dwfl_linux_proc_report
then we might see mappings of special character devices. Make
sure we only open and return regular files. Special devices
- might hang on open or read. */
+ might hang on open or read. (deleted) files are super special.
+ The image might come from memory if we are attached. */
struct stat sb;
if (stat (module_name, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG)
- return -1;
+ {
+ if (strcmp (strrchr (module_name, ' ') ?: "", " (deleted)") == 0)
+ pid = INTUSE(dwfl_pid) (mod->dwfl);
+ else
+ return -1;
+ }
- int fd = open64 (module_name, O_RDONLY);
- if (fd >= 0)
+ if (pid == -1)
{
- *file_name = strdup (module_name);
- if (*file_name == NULL)
+ int fd = open64 (module_name, O_RDONLY);
+ if (fd >= 0)
{
- close (fd);
- return ENOMEM;
+ *file_name = strdup (module_name);
+ if (*file_name == NULL)
+ {
+ close (fd);
+ return ENOMEM;
+ }
}
+ return fd;
}
- return fd;
}
- int pid;
- if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
+ if (pid != -1 || sscanf (module_name, "[vdso: %d]", &pid) == 1)
{
/* Special case for in-memory ELF image. */
+ bool detach = false;
+ bool tid_was_stopped = false;
+ struct __libdwfl_pid_arg *pid_arg = __libdwfl_get_pid_arg (mod->dwfl);
+ if (pid_arg != NULL && ! pid_arg->assume_ptrace_stopped)
+ {
+ /* If any thread is already attached we are fine. Read
+ through that thread. It doesn't have to be the main
+ thread pid. */
+ pid_t tid = pid_arg->tid_attached;
+ if (tid != 0)
+ pid = tid;
+ else
+ detach = __libdwfl_ptrace_attach (pid, &tid_was_stopped);
+ }
+
char *fname;
if (asprintf (&fname, PROCMEMFMT, pid) < 0)
- return -1;
+ goto detach;
int fd = open64 (fname, O_RDONLY);
free (fname);
if (fd < 0)
- return -1;
+ goto detach;
*elfp = elf_from_remote_memory (base, getpagesize (), NULL,
&read_proc_memory, &fd);
close (fd);
*file_name = NULL;
+
+ detach:
+ if (detach)
+ __libdwfl_ptrace_detach (pid, tid_was_stopped);
return -1;
}