libdwfl: Add dwfl_core_file_attach and dwfl_linux_proc_attach.
[platform/upstream/elfutils.git] / libdwfl / dwfl_segment_report_module.c
index d454ccb..fd967e9 100644 (file)
@@ -37,6 +37,7 @@
 #include <sys/param.h>
 #include <alloca.h>
 #include <endian.h>
+#include <unistd.h>
 
 
 /* A good size for the initial read from memory, if it's not too costly.
@@ -426,7 +427,10 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
   /* We must have seen the segment covering offset 0, or else the ELF
      header we read at START was not produced by these program headers.  */
   if (unlikely (!found_bias))
-    return finish ();
+    {
+      free (build_id);
+      return finish ();
+    }
 
   /* Now we know enough to report a module for sure: its bounds.  */
   module_start += bias;
@@ -462,18 +466,67 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
              bias += fixup;
              if (module->name[0] != '\0')
                {
-                 name = module->name;
+                 name = basename (module->name);
                  name_is_final = true;
                }
              break;
            }
        }
 
-  /* Ignore this found module if it would conflict in address space with any
-     already existing module of DWFL.  */
-  for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
-    if (module_end > mod->low_addr && module_start < mod->high_addr)
-      return finish ();
+  if (r_debug_info != NULL)
+    {
+      bool skip_this_module = false;
+      for (struct r_debug_info_module *module = r_debug_info->module;
+          module != NULL; module = module->next)
+       if ((module_end > module->start && module_start < module->end)
+           || dyn_vaddr == module->l_ld)
+         {
+           bool close_elf = false;
+           if (! module->disk_file_has_build_id && build_id_len > 0)
+             {
+               /* Module found in segments with build-id is more reliable
+                  than a module found via DT_DEBUG on disk without any
+                  build-id.   */
+               if (module->elf != NULL)
+                 close_elf = true;
+             }
+           if (module->elf != NULL
+               && module->disk_file_has_build_id && build_id_len > 0)
+             {
+               const void *elf_build_id;
+               GElf_Addr elf_build_id_elfaddr;
+               int elf_build_id_len;
+
+               if (__libdwfl_find_elf_build_id (NULL, module->elf,
+                                                &elf_build_id,
+                                                &elf_build_id_elfaddr,
+                                                &elf_build_id_len) > 0)
+                 {
+                   if (build_id_len != (size_t) elf_build_id_len
+                       || memcmp (build_id, elf_build_id, build_id_len) != 0)
+                     close_elf = true;
+                 }
+             }
+           if (close_elf)
+             {
+               elf_end (module->elf);
+               close (module->fd);
+               module->elf = NULL;
+               module->fd = -1;
+             }
+           if (module->elf != NULL)
+             {
+               /* Ignore this found module if it would conflict in address
+                  space with any already existing module of DWFL.  */
+               skip_this_module = true;
+             }
+         }
+      if (skip_this_module)
+       {
+         free (build_id);
+         return finish ();
+       }
+    }
 
   /* Our return value now says to skip the segments contained
      within the module.  */