This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch 2/2 v2] Fix loading core files without build-ids


Hi Mark,

patch updated to address the issues/regressions.


On Sun, 19 May 2013 18:38:56 +0200, Jan Kratochvil wrote:
> New dwfl_link_map_report does not report modules for which it cannot find its
> file on disk.  This is correct as one does not know the module boundaries just
> from a link map entry.  The module boundaries were found in such case by
> dwfl_segment_report_module and names filled in later by dwfl_link_map_report.
> 
> Recent Linux kernels now produce also core note NT_FILE but in such case one
> usually has also build-id headers already so this patch is not needed.
> 
> My current plan is:
> (1) Save filenames for non-existing files during initial dwfl_link_map_report.
> (2) Run dwfl_segment_report_module even if dwfl_link_map_report found something.
> (2b) dwfl_segment_report_module should omit conflicting modules.
> (3) Update found anonymous "[dso]" modules by the saved filenames.

Implemented.


This patch addresses the issues and use case (2) discussed below:

> On Sat, 18 May 2013 00:00:58 +0200, Mark Wielaard wrote:
> > So you don't sniff at all if dwfl_link_map_report () found
> > something. Would it hurt at this point to sniff?
> 
> That's the question.  Sniffing will always bring in possibly incorrect data.
> I believe it would be useful only in the case link_map (r_debug->r_map) is
> corrupted in the middle and some of the libraries do not get reported
> (as I have seen in real world core files from ABRT).  Maybe report_r_debug
> could report corrupted link_map - it currently internally checks
> 	++iterations < dwfl->lookup_elts
> but it should also check l_prev does match the previous entry like GDB does.
> 
> Still core files with corrupted link_map I find more a corner case and it
> could be added as an add-on patch IMO.

On Fri, 26 Apr 2013 20:57:39 +0200, Jan Kratochvil wrote:
> Not sure if one should try to map address-non-conflicting files from ELF
> headers, this patch does not do it.  (That could better fit case (2) below.)
[...]
> (2) I want to know all build-ids: eu-unstrip -n --core=xxx
>     Modules placement does not matter, excessive output does not matter,
>     I need to only install needed debuginfos.
> 
> This change will behave better in (1) but it may regress (2), for example in
> cases where link_map list is overwritten/corrupted in its middle.


Thanks,
Jan


commit 457b771b842a0f6d23118e4b39d93f6581fb9b41
Author: Jan Kratochvil <jan.kratochvil@redhat.com>
Date:   Thu May 23 20:19:16 2013 +0200

    Use DT_DEBUG library search first.
    
    libdwfl/
    2013-05-23  Jan Kratochvil  <jan.kratochvil@redhat.com>
    
    	* argp-std.c (parse_opt) <ARGP_KEY_SUCCESS> <opt->core> <opt->e>: Set
    	executable_for_core before calling dwfl_core_file_report.
    	* core-file.c (free_r_debug_info): New function.
    	(dwfl_core_file_report): Move raw segments reporting
    	lower.  New variable r_debug_info.
    	(dwfl_core_file_report) (report_module): New function.
    	(dwfl_core_file_report):  Call also report_module for EXEC_VADDR and
    	VDSO_L_LD.  Call free_r_debug_info in the end.  Return sum of LISTED
    	and SNIFFED.
    	* dwfl_module_build_id.c (check_notes): Move into
    	__libdwfl_find_elf_build_id.
    	(__libdwfl_find_build_id): Rename to ...
    	(__libdwfl_find_elf_build_id): ... here.  Add parameters build_id_bits,
    	build_id_elfaddr and build_id_len.  Verify MOD vs. ELF.
    	(__libdwfl_find_elf_build_id) (check_notes): Remove parameters mod and
    	set, rename data_vaddr to data_elfaddr.  Do not call found_build_id.
    	(__libdwfl_find_elf_build_id): Update the check_notes caller, do not
    	adjust its data_elfaddr parameter.
    	(__libdwfl_find_build_id): New wrapper of __libdwfl_find_elf_build_id.
    	* dwfl_segment_report_module.c (dwfl_segment_report_module): New
    	parameter r_debug_info.  New variable name_is_final.  Adjust addresses
    	according to R_DEBUG_INFO->MODULE.  Check conflicts against DWFL.
    	Do not overwise NAME by SONAME if NAME_IS_FINAL.
    	* libdwflP.h (__libdwfl_find_elf_build_id): New declaration.
    	(struct r_debug_info_module, struct r_debug_info): New definitions.
    	(dwfl_segment_report_module, dwfl_link_map_report): Add parameter
    	r_debug_info.
    	* link_map.c: Include fcntl.h.
    	(report_r_debug): Add parameter r_debug_info, describe them in the
    	function comment.  Delete dwfl_addrmodule call and its dependent code.
    	Verify build-id before calling dwfl_report_elf, also supply
    	executable_for_core to it and skip vDSO.  Report vdso_bias and
    	vdso_l_ld when found.  Clear exec_vaddr when found.  Store
    	r_debug_info->module info when appropriate.
    	(dwfl_link_map_report): Add parameter r_debug_info.
    	(dwfl_link_map_report) (consider_phdr): Add parameter offset.  Set
    	r_debug_info.exec_vaddr when found.
    	(dwfl_link_map_report): New variable in_ok.  Try to read IN from
    	EXECUTABLE_FOR_CORE.  Update consider_phdr caller parameters.  Update
    	report_r_debug caller parameters.
    
    tests/
    2013-05-23  Jan Kratochvil  <jan.kratochvil@redhat.com>
    
    	* Makefile.am (EXTRA_DIST): Add test-core-lib.so.bz2,
    	test-core.core.bz2 and test-core.exec.bz2.
    	* run-addrname-test.sh: New test for these files.
    	* run-unstrip-n.sh: Update expected output.  New test for these files.
    	* test-core-lib.so.bz2: New file.
    	* test-core.core.bz2: New file.
    	* test-core.exec.bz2: New file.
    
    Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com>

diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
index e54f720..c884390 100644
--- a/libdwfl/argp-std.c
+++ b/libdwfl/argp-std.c
@@ -295,6 +295,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
 	if (opt->core)
 	  {
+	    if (opt->e)
+	      dwfl->executable_for_core = strdup (opt->e);
+
 	    int fd = open64 (opt->core, O_RDONLY);
 	    if (fd < 0)
 	      {
@@ -330,9 +333,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
 			      _("No modules recognized in core file"));
 		return ENOENT;
 	      }
-
-	    if (opt->e)
-	      dwfl->executable_for_core = strdup (opt->e);
 	  }
 	else if (opt->e)
 	  {
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index ac2aa8c..324bc5c 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -381,6 +381,19 @@ dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
   return true;
 }
 
+/* Free the contents of R_DEBUG_INFO without the R_DEBUG_INFO memory itself.  */
+
+static void
+free_r_debug_info (struct r_debug_info *r_debug_info)
+{
+  while (r_debug_info->module != NULL)
+    {
+      struct r_debug_info_module *module = r_debug_info->module;
+      r_debug_info->module = module->next;
+      free (module);
+    }
+}
+
 int
 dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
 {
@@ -431,33 +444,85 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
   /* Now we have NT_AUXV contents.  From here on this processing could be
      used for a live process with auxv read from /proc.  */
 
+  struct r_debug_info r_debug_info;
+  memset (&r_debug_info, 0, sizeof r_debug_info);
   int listed = dwfl_link_map_report (dwfl, auxv, auxv_size,
-				     dwfl_elf_phdr_memory_callback, elf);
+				     dwfl_elf_phdr_memory_callback, elf,
+				     &r_debug_info);
+
+  /* Call dwfl_segment_report_module, update *NDXP when appropriate.  Return
+     true for a successfully reported module.  Otherwise return false and also
+     call free_r_debug_info.  */
+
+  inline bool report_module (int *ndxp, int *count, const char *name)
+  {
+    int seg = dwfl_segment_report_module (dwfl, *ndxp, name,
+					  &dwfl_elf_phdr_memory_callback, elf,
+					  core_file_read_eagerly, elf,
+					  &r_debug_info);
+    if (unlikely (seg < 0))
+      {
+	free_r_debug_info (&r_debug_info);
+	return false;
+      }
+    if (seg > *ndxp)
+      {
+	*ndxp = seg;
+	++*count;
+      }
+    else
+      ++*ndxp;
+    return true;
+  }
+
+  if (r_debug_info.vdso_l_ld != 0 || r_debug_info.exec_vaddr != 0)
+    for (ndx = 0; ndx < (int) phnum;)
+      {
+	GElf_Phdr phdr_mem;
+	GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem);
+	if (unlikely (phdr == NULL))
+	  {
+	    free_r_debug_info (&r_debug_info);
+	    __libdwfl_seterrno (DWFL_E_LIBELF);
+	    return -1;
+	  }
+	if (phdr->p_type == PT_LOAD && r_debug_info.exec_vaddr != 0
+	    && phdr->p_vaddr == r_debug_info.exec_vaddr)
+	  {
+	    if (unlikely (! report_module (&ndx, &listed, "[exe]")))
+	      return -1;
+	  }
+	else if (phdr->p_type == PT_LOAD && r_debug_info.vdso_l_ld != 0
+		 && phdr->p_filesz == phdr->p_memsz
+		 && phdr->p_vaddr <= r_debug_info.vdso_l_ld
+		 && r_debug_info.vdso_l_ld < phdr->p_vaddr + phdr->p_memsz)
+	  {
+	    /* FIXME: Use vdso_bias.  Currently dwfl_segment_report_module
+	       detects the bias on its own.  */
+	    if (unlikely (! report_module (&ndx, &listed, "[vdso]")))
+	      return -1;
+	  }
+	else
+	  ++ndx;
+      }
+
+  /* Now sniff segment contents for modules hinted by information gathered
+     from DT_DEBUG.  */
 
-  /* Now sniff segment contents for modules.  */
   int sniffed = 0;
-      ndx = 0;
-      do
-	{
-	  int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
-						&dwfl_elf_phdr_memory_callback, elf,
-						core_file_read_eagerly, elf);
-	  if (unlikely (seg < 0))
-	    return seg;
-	  if (seg > ndx)
-	    {
-	      ndx = seg;
-	      ++sniffed;
-	    }
-	  else
-	    ++ndx;
-	}
-      while (ndx < (int) phnum);
+  ndx = 0;
+  do
+    {
+      if (unlikely (! report_module (&ndx, &sniffed, NULL) ))
+	return -1;
+    }
+  while (ndx < (int) phnum);
+
+  free_r_debug_info (&r_debug_info);
 
   /* We return the number of modules we found if we found any.
      If we found none, we return -1 instead of 0 if there was an
-     error rather than just nothing found.  If link_map handling
-     failed, we still have the sniffed modules.  */
-  return sniffed == 0 || listed > sniffed ? listed : sniffed;
+     error rather than just nothing found.  */
+  return sniffed || listed >= 0 ? listed + sniffed : listed;
 }
 INTDEF (dwfl_core_file_report)
diff --git a/libdwfl/dwfl_module_build_id.c b/libdwfl/dwfl_module_build_id.c
index 98cb9f4..cae007b 100644
--- a/libdwfl/dwfl_module_build_id.c
+++ b/libdwfl/dwfl_module_build_id.c
@@ -56,10 +56,20 @@ found_build_id (Dwfl_Module *mod, bool set,
 
 int
 internal_function
-__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+__libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf,
+			     const void **build_id_bits,
+			     GElf_Addr *build_id_elfaddr, int *build_id_len)
 {
-  int
-  check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr)
+  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+  if (unlikely (ehdr == NULL))
+    {
+      __libdwfl_seterrno (DWFL_E_LIBELF);
+      return -1;
+    }
+  // MOD->E_TYPE is zero here.
+  assert (ehdr->e_type != ET_REL || mod != NULL);
+
+  int check_notes (Elf_Data *data, GElf_Addr data_elfaddr)
   {
     size_t pos = 0;
     GElf_Nhdr nhdr;
@@ -69,10 +79,13 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
       if (nhdr.n_type == NT_GNU_BUILD_ID
 	  && nhdr.n_namesz == sizeof "GNU" && !memcmp (data->d_buf + name_pos,
 						       "GNU", sizeof "GNU"))
-	return found_build_id (mod, set,
-			       data->d_buf + desc_pos, nhdr.n_descsz,
-			       data_vaddr == NO_VADDR ? 0
-			       : data_vaddr + desc_pos);
+	{
+	  *build_id_bits = data->d_buf + desc_pos;
+	  *build_id_elfaddr = (data_elfaddr == NO_VADDR
+			       ? 0 : data_elfaddr + desc_pos);
+	  *build_id_len = nhdr.n_descsz;
+	  return 1;
+	}
     return 0;
   }
 
@@ -84,11 +97,8 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
   if (scn == NULL)
     {
       /* No sections, have to look for phdrs.  */
-      GElf_Ehdr ehdr_mem;
-      GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
       size_t phnum;
-      if (unlikely (ehdr == NULL)
-	  || unlikely (elf_getphdrnum (elf, &phnum) != 0))
+      if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
 	{
 	  __libdwfl_seterrno (DWFL_E_LIBELF);
 	  return -1;
@@ -98,12 +108,11 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
 	  GElf_Phdr phdr_mem;
 	  GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
 	  if (likely (phdr != NULL) && phdr->p_type == PT_NOTE)
-	    result = check_notes (mod, set,
-				  elf_getdata_rawchunk (elf,
+	    result = check_notes (elf_getdata_rawchunk (elf,
 							phdr->p_offset,
 							phdr->p_filesz,
 							ELF_T_NHDR),
-				  dwfl_adjusted_address (mod, phdr->p_vaddr));
+				  phdr->p_vaddr);
 	}
     }
   else
@@ -117,12 +126,12 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
 	    GElf_Addr vaddr = 0;
 	    if (!(shdr->sh_flags & SHF_ALLOC))
 	      vaddr = NO_VADDR;
-	    else if (mod->e_type != ET_REL)
-	      vaddr = dwfl_adjusted_address (mod, shdr->sh_addr);
+	    else if (mod == NULL || ehdr->e_type != ET_REL)
+	      vaddr = shdr->sh_addr;
 	    else if (__libdwfl_relocate_value (mod, elf, &shstrndx,
 					       elf_ndxscn (scn), &vaddr))
 	      vaddr = NO_VADDR;
-	    result = check_notes (mod, set, elf_getdata (scn, NULL), vaddr);
+	    result = check_notes (elf_getdata (scn, NULL), vaddr);
 	  }
       }
     while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);
@@ -131,6 +140,24 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
 }
 
 int
+internal_function
+__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+{
+  const void *build_id_bits;
+  GElf_Addr build_id_elfaddr;
+  int build_id_len;
+
+  int result = __libdwfl_find_elf_build_id (mod, elf, &build_id_bits,
+					    &build_id_elfaddr, &build_id_len);
+  if (result <= 0)
+    return result;
+
+  GElf_Addr build_id_vaddr = build_id_elfaddr + (build_id_elfaddr != 0
+						 ? mod->main_bias : 0);
+  return found_build_id (mod, set, build_id_bits, build_id_len, build_id_vaddr);
+}
+
+int
 dwfl_module_build_id (Dwfl_Module *mod,
 		      const unsigned char **bits, GElf_Addr *vaddr)
 {
diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c
index 7cf7499..e2d0337 100644
--- a/libdwfl/dwfl_segment_report_module.c
+++ b/libdwfl/dwfl_segment_report_module.c
@@ -83,7 +83,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
 			    Dwfl_Memory_Callback *memory_callback,
 			    void *memory_callback_arg,
 			    Dwfl_Module_Callback *read_eagerly,
-			    void *read_eagerly_arg)
+			    void *read_eagerly_arg,
+			    const struct r_debug_info *r_debug_info)
 {
   size_t segment = ndx;
 
@@ -433,6 +434,43 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
 
   dyn_vaddr += bias;
 
+  /* NAME found from link map has precedence over DT_SONAME possibly read
+     below.  */
+  bool name_is_final = false;
+
+  /* Try to match up DYN_VADDR against L_LD as found in link map.
+     Segments sniffing may guess invalid address as the first read-only memory
+     mapping may not be dumped to the core file (if ELF headers are not dumped)
+     and the ELF header is dumped first with the read/write mapping of the same
+     file at higher addresses.  */
+  if (r_debug_info != NULL)
+    for (const struct r_debug_info_module *module = r_debug_info->module;
+	 module != NULL; module = module->next)
+      if (module_start <= module->l_ld && module->l_ld < module_end)
+	{
+	  GElf_Addr fixup = module->l_ld - dyn_vaddr;
+	  if (module_start + fixup <= module->l_ld
+	      && module->l_ld < module_end + fixup)
+	    {
+	      module_start += fixup;
+	      module_end += fixup;
+	      dyn_vaddr += fixup;
+	      bias += fixup;
+	      if (module->name[0] != '\0')
+		{
+		  name = 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 ();
+
   /* Our return value now says to skip the segments contained
      within the module.  */
   ndx = addr_segndx (dwfl, segment, module_end, true);
@@ -518,7 +556,7 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
 
   void *soname = NULL;
   size_t soname_size = 0;
-  if (dynstrsz != 0 && dynstr_vaddr != 0)
+  if (! name_is_final && dynstrsz != 0 && dynstr_vaddr != 0)
     {
       /* We know the bounds of the .dynstr section.
 
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 37a9dff..1307bdb 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -362,6 +362,16 @@ extern Dwfl_Error __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr,
 extern Dwfl_Error __libdwfl_cu_getsrclines (struct dwfl_cu *cu)
   internal_function;
 
+/* Look in ELF for an NT_GNU_BUILD_ID note.  Store it to BUILD_ID_BITS,
+   its vaddr in ELF to BUILD_ID_VADDR (it is unrelocated, even if MOD is not
+   NULL) and store length to BUILD_ID_LEN.  Returns -1 for errors, 1 if it was
+   stored and 0 if no note is found.  MOD may be NULL, MOD must be non-NULL
+   only if ELF is ET_REL.  */
+extern int __libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf,
+					const void **build_id_bits,
+					GElf_Addr *build_id_elfaddr,
+					int *build_id_len);
+
 /* Look in ELF for an NT_GNU_BUILD_ID note.  If SET is true, store it
    in MOD and return its length.  If SET is false, instead compare it
    to that stored in MOD and return 2 if they match, 1 if they do not.
@@ -439,13 +449,35 @@ typedef bool Dwfl_Module_Callback (Dwfl_Module *mod, void **userdata,
 				   GElf_Off whole, GElf_Off contiguous,
 				   void *arg, Elf **elfp);
 
+/* One shared library (or executable) info from DT_DEBUG link map.  */
+struct r_debug_info_module
+{
+  struct r_debug_info_module *next;
+  GElf_Addr l_ld;
+  char name[0];
+};
+
+/* Information gathered from DT_DEBUG by dwfl_link_map_report hinted to
+   dwfl_segment_report_module.  */
+struct r_debug_info
+{
+  /* Found l_addr and l_ld fields for vDSO.  */
+  GElf_Addr vdso_bias, vdso_l_ld;
+
+  /* Found memory address of start of the executable.  */
+  GElf_Addr exec_vaddr;
+
+  struct r_debug_info_module *module;
+};
+
 /* ...
  */
 extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
 				       Dwfl_Memory_Callback *memory_callback,
 				       void *memory_callback_arg,
 				       Dwfl_Module_Callback *read_eagerly,
-				       void *read_eagerly_arg);
+				       void *read_eagerly_arg,
+				       const struct r_debug_info *r_debug_info);
 
 /* Report a module for entry in the dynamic linker's struct link_map list.
    For each link_map entry, if an existing module resides at its address,
@@ -459,10 +491,14 @@ extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
    only find where to begin if the correct executable file was
    previously reported and preloaded as with dwfl_report_elf.
 
+   Fill in R_DEBUG_INFO if it is not NULL.  It should be cleared by the
+   caller, this function does not touch fields it does not need to modify.
+
    Returns the number of modules found, or -1 for errors.  */
 extern int dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 				 Dwfl_Memory_Callback *memory_callback,
-				 void *memory_callback_arg);
+				 void *memory_callback_arg,
+				 struct r_debug_info *r_debug_info);
 
 
 /* Avoid PLT entries.  */
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
index 5dbf17e..c1bd60a 100644
--- a/libdwfl/link_map.c
+++ b/libdwfl/link_map.c
@@ -33,6 +33,7 @@
 
 #include <byteswap.h>
 #include <endian.h>
+#include <fcntl.h>
 
 /* This element is always provided and always has a constant value.
    This makes it an easy thing to scan for to discern the format.  */
@@ -222,7 +223,8 @@ addrsize (uint_fast8_t elfclass)
 }
 
 /* Report a module for each struct link_map in the linked list at r_map
-   in the struct r_debug at R_DEBUG_VADDR.
+   in the struct r_debug at R_DEBUG_VADDR.  For r_debug_info description
+   see dwfl_link_map_report in libdwflP.h.
 
    For each link_map entry, if an existing module resides at its address,
    this just modifies that module's name and suggested file name.  If
@@ -234,7 +236,8 @@ static int
 report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
 		Dwfl *dwfl, GElf_Addr r_debug_vaddr,
 		Dwfl_Memory_Callback *memory_callback,
-		void *memory_callback_arg)
+		void *memory_callback_arg,
+		struct r_debug_info *r_debug_info)
 {
   /* Skip r_version, to aligned r_map field.  */
   GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass);
@@ -352,9 +355,108 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
       /* We have to find the file's phdrs to compute along with l_addr
 	 what its runtime address boundaries are.  */
 
-      // XXX hook for sysroot
-      mod = INTUSE(dwfl_report_elf) (dwfl, basename (name),
-				     name, -1, l_addr, true);
+      Dwfl_Module *mod = NULL;
+      if (iterations == 1 && dwfl->executable_for_core != NULL)
+	{
+	  /* Find the main executable.
+	     FIXME: Try to also find it via build-id.  */
+	  name = dwfl->executable_for_core;
+	}
+      if (iterations != 2 && name != NULL)
+	{
+	  /* Find a shared library or main executable.  Do not try to
+	     find a file for vDSO (where ITERATIONS equals 2).  */
+
+	  /* This code is mostly inlined dwfl_report_elf.  */
+	  // XXX hook for sysroot
+	  int fd = open64 (name, O_RDONLY);
+	  if (fd >= 0)
+	    {
+	      Elf *elf;
+	      Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
+	      if (error == DWFL_E_NOERROR)
+		{
+		  const void *build_id_bits;
+		  GElf_Addr build_id_elfaddr;
+		  int build_id_len;
+		  bool valid = true;
+
+		  /* FIXME: Bias L_ADDR should be computed from the prelink
+		     state in memory (when the file got loaded), not against
+		     the current on-disk file state as is computed below.
+
+		     This verification gives false positive if in-core ELF had
+		     build-id but on-disk ELF does not have any.  But we cannot
+		     reliably find ELF header and/or the ELF build id just from
+		     the link map (and checking core segments is also not
+		     reliable).  */
+
+		  if (__libdwfl_find_elf_build_id (NULL, elf, &build_id_bits,
+						   &build_id_elfaddr,
+						   &build_id_len) > 0
+		      && build_id_elfaddr != 0)
+		    {
+		      GElf_Addr build_id_vaddr = build_id_elfaddr + l_addr;
+		      release_buffer (0);
+		      int segndx = INTUSE(dwfl_addrsegment) (dwfl,
+							     build_id_vaddr,
+							     NULL);
+		      if (! (*memory_callback) (dwfl, segndx,
+						&buffer, &buffer_available,
+						build_id_vaddr, build_id_len,
+						memory_callback_arg)
+			  || memcmp (build_id_bits, buffer, build_id_len) != 0)
+			{
+			  /* File has valid build-id which cannot be verified
+			     in memory.  */
+			  valid = false;
+			}
+		    }
+
+		  if (valid)
+		    // XXX hook for sysroot
+		    mod = __libdwfl_report_elf (dwfl, basename (name), name,
+						fd, elf, l_addr, true, true);
+		  if (mod == NULL)
+		    {
+		      elf_end (elf);
+		      close (fd);
+		    }
+		}
+	    }
+	}
+      if (mod != NULL && iterations == 1)
+	{
+	  /* Cancel reporting of raw segments for the main executable as
+	     its file has been found now.  */
+	  if (r_debug_info != NULL)
+	    r_debug_info->exec_vaddr = 0;
+	}
+      if (iterations == 2)
+	{
+	  /* vDSO is only reported to the caller, it is never searched
+	     for on the disk.  */
+	  assert (mod == NULL);
+	  if (r_debug_info != NULL)
+	    {
+	      r_debug_info->vdso_bias = l_addr;
+	      r_debug_info->vdso_l_ld = l_ld;
+	    }
+	}
+      if (r_debug_info != NULL && mod == NULL)
+	{
+	  /* Save link map information about valid shared library (or
+	     executable) which has not been found on disk.  */
+	  const char *base = name == NULL ? "" : basename (name);
+	  struct r_debug_info_module *module;
+	  module = malloc (sizeof (*module) + strlen (base) + 1);
+	  if (module == NULL)
+	    return release_buffer (result);
+	  module->l_ld = l_ld;
+	  strcpy (module->name, base);
+	  module->next = r_debug_info->module;
+	  r_debug_info->module = module;
+	}
 
       if (mod != NULL)
 	{
@@ -570,7 +672,8 @@ find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry,
 int
 dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 		      Dwfl_Memory_Callback *memory_callback,
-		      void *memory_callback_arg)
+		      void *memory_callback_arg,
+		      struct r_debug_info *r_debug_info)
 {
   GElf_Addr r_debug_vaddr = 0;
 
@@ -632,8 +735,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
       GElf_Xword dyn_filesz = 0;
       GElf_Addr dyn_bias = (GElf_Addr) -1;
 
-      inline bool consider_phdr (GElf_Word type,
-				 GElf_Addr vaddr, GElf_Xword filesz)
+      inline bool consider_phdr (GElf_Word type, GElf_Addr vaddr,
+				 GElf_Xword filesz, Elf32_Off offset)
       {
 	switch (type)
 	  {
@@ -644,6 +747,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 		    == (phdr & (dwfl->segment_align - 1))))
 	      {
 		dyn_bias = phdr - vaddr;
+		if (r_debug_info != NULL)
+		  r_debug_info->exec_vaddr = phdr - offset;
 		return dyn_vaddr != 0;
 	      }
 	    break;
@@ -668,8 +773,65 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 	      .d_size = phnum * phent,
 	      .d_buf = NULL
 	    };
-	  if ((*memory_callback) (dwfl, phdr_segndx, &in.d_buf, &in.d_size,
-				  phdr, phnum * phent, memory_callback_arg))
+	  bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf,
+					   &in.d_size, phdr, phnum * phent,
+					   memory_callback_arg);
+	  if (! in_ok && dwfl->executable_for_core != NULL)
+	    {
+	      /* AUXV -> PHDR -> DYNAMIC
+		 Both AUXV and DYNAMIC should be always present in a core file.
+		 PHDR may be missing in core file, try to read it from
+		 EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
+		 core file.  */
+
+	      int fd = open (dwfl->executable_for_core, O_RDONLY);
+	      Elf *elf;
+	      Dwfl_Error error = DWFL_E_ERRNO;
+	      if (fd != -1)
+		error = __libdw_open_file (&fd, &elf, true, false);
+	      if (error != DWFL_E_NOERROR)
+		{
+		  __libdwfl_seterrno (error);
+		  return false;
+		}
+	      GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+	      if (ehdr == NULL)
+		{
+		  elf_end (elf);
+		  close (fd);
+		  __libdwfl_seterrno (DWFL_E_LIBELF);
+		  return false;
+		}
+	      if (ehdr->e_phnum != phnum || ehdr->e_phentsize != phent)
+		{
+		  elf_end (elf);
+		  close (fd);
+		  __libdwfl_seterrno (DWFL_E_BADELF);
+		  return false;
+		}
+	      off_t off = ehdr->e_phoff;
+	      assert (in.d_buf == NULL);
+	      assert (in.d_size == phnum * phent);
+	      in.d_buf = malloc (in.d_size);
+	      if (unlikely (in.d_buf == NULL))
+		{
+		  elf_end (elf);
+		  close (fd);
+		  __libdwfl_seterrno (DWFL_E_NOMEM);
+		  return false;
+		}
+	      ssize_t nread = pread_retry (fd, in.d_buf, in.d_size, off);
+	      elf_end (elf);
+	      close (fd);
+	      if (nread != (ssize_t) in.d_size)
+		{
+		  free (in.d_buf);
+		  __libdwfl_seterrno (DWFL_E_ERRNO);
+		  return false;
+		}
+	      in_ok = true;
+	    }
+	  if (in_ok)
 	    {
 	      union
 	      {
@@ -700,7 +862,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 		      for (size_t i = 0; i < phnum; ++i)
 			if (consider_phdr (u->p32[i].p_type,
 					   u->p32[i].p_vaddr,
-					   u->p32[i].p_filesz))
+					   u->p32[i].p_filesz,
+					   u->p32[i].p_offset))
 			  break;
 		    }
 		  else
@@ -708,7 +871,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 		      for (size_t i = 0; i < phnum; ++i)
 			if (consider_phdr (u->p64[i].p_type,
 					   u->p64[i].p_vaddr,
-					   u->p64[i].p_filesz))
+					   u->p64[i].p_filesz,
+					   u->p64[i].p_offset))
 			  break;
 		    }
 		}
@@ -831,6 +995,6 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 
   /* Now we can follow the dynamic linker's library list.  */
   return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr,
-			 &integrated_memory_callback, &mcb);
+			 &integrated_memory_callback, &mcb, r_debug_info);
 }
 INTDEF (dwfl_link_map_report)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6327edb..2d819c5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -199,7 +199,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     testfile70.core.bz2 testfile70.exec.bz2 \
 	     run-dwfllines.sh run-dwfl-report-elf-align.sh \
 	     testfile-dwfl-report-elf-align-shlib.so.bz2 \
-	     testfilenolines
+	     testfilenolines test-core-lib.so.bz2 test-core.core.bz2 \
+	     test-core.exec.bz2
 
 if USE_VALGRIND
 valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh
index 9351fb2..8624074 100755
--- a/tests/run-addrname-test.sh
+++ b/tests/run-addrname-test.sh
@@ -316,4 +316,10 @@ main+0x9
 ??:0
 EOF
 
+testfiles test-core-lib.so test-core.core test-core.exec
+testrun_compare ${abs_top_builddir}/src/addr2line -S -e test-core.exec --core=test-core.core 0x7f67f2aaf619 <<\EOF
+libfunc+0x9
+??:0
+EOF
+
 exit 0
diff --git a/tests/run-unstrip-n.sh b/tests/run-unstrip-n.sh
index 9c2b43b..b35d064 100755
--- a/tests/run-unstrip-n.sh
+++ b/tests/run-unstrip-n.sh
@@ -36,19 +36,34 @@ testfiles testcore-rtlib testcore-rtlib-ppc
 testrun_compare ${abs_top_builddir}/src/unstrip -n --core=testcore-rtlib <<\EOF
 0x8048000+0x2000 f1c600bc36cb91bf01f9a63a634ecb79aa4c3199(a)0x8048178 . - [exe]
 0xf77d6000+0x1000 676560b1b765cde9c2e53f134f4ee354ea894747(a)0xf77d6210 . - linux-gate.so.1
-0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37(a)0xf77b3164 /lib/librt.so.1 - librt.so.1
-0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de(a)0xf7603184 /lib/libc.so.6 - libc.so.6
-0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148(a)0xf75e9164 /lib/libpthread.so.0 - libpthread.so.0
-0xf77d7000+0x21000 6d2cb32650054f1c176d01d48713a4a5e5e84c1a(a)0xf77d7124 /lib/ld-linux.so.2 - ld-linux.so.2
+0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148(a)0xf75e9164 - - libpthread.so.0
+0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de(a)0xf7603184 - - libc.so.6
+0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37(a)0xf77b3164 - - librt.so.1
+0xf77d7000+0x21000 6d2cb32650054f1c176d01d48713a4a5e5e84c1a(a)0xf77d7124 - - ld-linux.so.2
 EOF
 
 testrun_compare ${abs_top_builddir}/src/unstrip -n --core=testcore-rtlib-ppc <<\EOF
-0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5(a)0x10000174 . - [exe]
 0x100000+0x10000 708b900b05176964512a6b0fe90c2a0c9d73d726(a)0x100334 . - linux-vdso32.so.1
-0xfd50000+0x30000 3f7d21508470322d2f47acddc20ab10516edba99(a)0xfd50164 /lib/librt.so.1 - librt.so.1
-0xfdf0000+0x1c0000 edf3dd232e09d01b90683889bd16b9406c52d4de(a)0xfdf0184 /lib/libc.so.6 - libc.so.6
-0xfdb0000+0x40000 f6ee91d4c629bc7dacc10534cb30056914e7e0b5(a)0xfdb0164 /lib/libpthread.so.0 - libpthread.so.0
-0xffb0000+0x50000 edec437a85026a1cf8cda94003706202733130c1(a)0xffb0124 /lib/ld.so.1 - ld.so.1
+0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5(a)0x10000174 . - [exe]
+0xfd50000+0x30000 3f7d21508470322d2f47acddc20ab10516edba99(a)0xfd50164 . - librt.so.1
+0xfdb0000+0x40000 f6ee91d4c629bc7dacc10534cb30056914e7e0b5(a)0xfdb0164 - - libpthread.so.0
+0xfdf0000+0x1c0000 edf3dd232e09d01b90683889bd16b9406c52d4de(a)0xfdf0184 - - libc.so.6
+0xffb0000+0x50000 edec437a85026a1cf8cda94003706202733130c1(a)0xffb0124 - - ld.so.1
+EOF
+
+# FAIL was 0x7f67f2caf000 for test-core-lib.so .
+# /lib64/libc.so.6 and /lib64/ld-linux-x86-64.so.2 from link map
+# do not have ELF header stored in the core file.
+# ELF headers in the core file:
+# Offset   VirtAddr          
+# 0x014000 0x00007f67f2caf000 ./test-core-lib.so
+# 0x03a000 0x00007fff1596c000 linux-vdso.so.1
+testfiles test-core.core test-core.exec
+rm -f test-core-lib.so
+testrun_compare ${abs_top_builddir}/src/unstrip -n -e test-core.exec --core=test-core.core <<\EOF
+0x400000+0x202038 - test-core.exec - test-core.exec
+0x7fff1596c000+0x1000 a9cf37f53897b5468ee018655760be61b8633d3c(a)0x7fff1596c340 . - linux-vdso.so.1
+0x7f67f2aaf000+0x202000 - . - test-core-lib.so
 EOF
 
 test_cleanup
diff --git a/tests/test-core-lib.so.bz2 b/tests/test-core-lib.so.bz2
new file mode 100755
index 0000000..bb2da88
Binary files /dev/null and b/tests/test-core-lib.so.bz2 differ
diff --git a/tests/test-core.core.bz2 b/tests/test-core.core.bz2
new file mode 100644
index 0000000..4d4346b
Binary files /dev/null and b/tests/test-core.core.bz2 differ
diff --git a/tests/test-core.exec.bz2 b/tests/test-core.exec.bz2
new file mode 100755
index 0000000..49ce551
Binary files /dev/null and b/tests/test-core.exec.bz2 differ

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]