]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdwfl: dwfl_linux_proc_find_elf use elf_from_remote_memory for (deleted).
authorMark Wielaard <mjw@redhat.com>
Tue, 4 Mar 2014 10:27:15 +0000 (11:27 +0100)
committerMark Wielaard <mjw@redhat.com>
Mon, 10 Mar 2014 20:29:00 +0000 (21:29 +0100)
If a module has a "(deleted)" main ELF file, then try to read it from
remote memory if the Dwfl has process state attached by reusing the ptrace
mechanism from linux-pid-attach.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
libdwfl/ChangeLog
libdwfl/dwfl_frame.c
libdwfl/libdwflP.h
libdwfl/linux-pid-attach.c
libdwfl/linux-proc-maps.c

index e37139356399fdc2598e3b46dab533d308339dbc..12938f35aa5c3cb36b33616cebda15925d52d9fd 100644 (file)
@@ -1,3 +1,23 @@
+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
index e45cf14c888e52e7b96cf465b593388e57387e1c..fd0b9aeb29a7741340b09228ae52e598941da92c 100644 (file)
@@ -157,11 +157,17 @@ dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid,
       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)
index 710e69923c94e6e382edceaabd488cf0ac3a7640..3a28ac7ddb54f31adfa89fe5264858746d230d75 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
@@ -35,6 +35,7 @@
 #include <libdwfl.h>
 #include <libebl.h>
 #include <assert.h>
+#include <dirent.h>
 #include <errno.h>
 #include <stdbool.h>
 #include <stdlib.h>
@@ -387,6 +388,39 @@ struct dwfl_arange
 };
 
 
+/* 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. */
index 58d6942fe7cf43e5351467674e66895d4b31eaaf..6be578bbc9127ebca6b53a53df1bfc6c3893cf3d 100644 (file)
@@ -1,5 +1,5 @@
 /* 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)
@@ -72,8 +62,9 @@ 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)
     {
@@ -121,7 +112,7 @@ ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
 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;
@@ -164,7 +155,7 @@ static pid_t
 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)
@@ -238,11 +229,11 @@ pid_thread_state_registers_cb (int firstreg, unsigned nregs,
 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;
@@ -254,28 +245,33 @@ pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
 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 =
@@ -328,7 +324,7 @@ dwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid, bool assume_ptrace_stopped)
   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);
@@ -347,3 +343,14 @@ dwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid, bool assume_ptrace_stopped)
   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;
+}
index 3384403c88facc292189734c483a563d6fd0e2e9..b6620ac5564247483b5db4ee19d41f34c7a61349 100644 (file)
@@ -339,42 +339,66 @@ dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
                          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);
@@ -382,6 +406,10 @@ dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
       close (fd);
 
       *file_name = NULL;
+
+    detach:
+      if (detach)
+       __libdwfl_ptrace_detach (pid, tid_was_stopped);
       return -1;
     }