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 v4] Fix loading core files without build-ids


On Mon, 27 May 2013 16:05:46 +0200, Mark Wielaard wrote:
> Since it doesn't actually free the struct itself, lets call it
> clear_r_debug_info to avoid confusion.

Done.


> Reporting the full file names was arguably wrong to begin with. I don't
> think we should restore that, but if people do feel this is a regression we
> could add a guesser to unstrip.c itself (but I don't think we should).

The full pathnames were clearly a bug for this patchset.

But when we talk about it in general, as a new patchset, I rather find a bug
dropping the path, when core file already contains the absolute pathname why
to hide it from the user?

But when we talk about some possible further extensions we should rather
forget about the whole link map and just use NT_FILE.  Or one can just already
display the full pathnames easily (not in eu-readelf):
$ readelf -n test-core.core
                 Start                 End         Page Offset
    0x0000000000400000  0x0000000000402000  0x0000000000000000
        /home/jkratoch/redhat/elfutils-libregr/test-core-main
    0x00000037ef400000  0x00000037ef420000  0x0000000000000000
        /usr/lib64/ld-2.16.so


> Document that count gets updated on success.

The report_module helper is gone now.


> The clearing of r_debug_info on failure is a nice because it prevents
> accidental memory leaks, but also confused me on first read of the patch
> (because I forgot the comment when reading the later code).

The report_module helper is gone now.


> >    /* 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;
> 
> What about the case of sniffed == 0 && listed == 0?
> Should that return -1?

It did not return -1 before so why we should now?  There may exist valid core
file which just does not contain any ELF module (=does not contain any
identifiable ELF module).

A different case is when there is some real unexpected failure processing the
data structures, in such case the function still returns -1, either be an
early return -1 call or due to LISTED == -1 (from report_r_debug()
returning -1).


> > +  /* 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;
[...]
> fixup might need a comment.

added:
          /* L_LD read from link map must be right while DYN_VADDR is unsafe.
             Therefore subtract DYN_VADDR and add L_LD to get a possibly
             corrective displacement for all addresses computed so far.  */


> It wasn't immediately clear to me. The fixup is the difference between the
> PT_LOAD address that the link-map know is the correct one and the PT_DYNAMIC
> address (of which there can be only one). And module_start points to the
> PT_LOAD address that was just gotten from the phdrs. Right?

Right.


> Actually now that I do understand, that is kind of what the comment above
> already says. I just didn't immediately made the connection with the
> offset/fixup being static. Is fixup always positive?

At least in the supplied testcase it is negative.

In fact I did not spent time thinking if it is positive or negative,
I was following the logic which address is safe and which unsafe above.


> BTW. Why does link-map set module->name to the empty string when no name
> (NULL) was found? Just convenience like here, so no NULL check is
> needed, or is the empty name significant?

'name' is a GCC zero-length array, I am sorry but I cannot make it NULL:
	struct r_debug_info_module
	{ 
	[...]
	  char name[0];
	};


> > diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
> > @@ -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;
> > +	}
> 
> To solve that FIXME we should do somewhat the same as is done below with
> the "inlined dwfl_report_elf" snippet? And we already do try to open the
> executable if we cannot read the phdrs. Maybe this can be factored out a
> bit?

In fact when we do not have any executable filename or we failed to open the
executable filename the only thing we can do is currently already done by the
second pass of dwfl_segment_report_module calls with the r_debug_info hints.

This code was there before I implemented the second pass even after the link
map pass succeeded (found >= 1 shared libraries).

So I have removed the FIXME comment and simplified it all a bit.


> > +      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).  */
> 
> I couldn't find where the iteration == 1 => exe, iteration == 2 => vDSO
> comes from. Shouldn't the second check be iterations > 2 ?

As discussed on #elfutils this ITERATIONS == 2 condition was unsafe, while
currently vDSO is the 2nd entry with glibc at least with older or non-Linux
core files 2nd entry is a regular shared library.

One could make some name-based exceptions ("linux-vdso.so.1") like in
	http://pkgs.fedoraproject.org/cgit/gdb.git/tree/gdb-glibc-vdso-workaround.patch

I found also this case is now obsoleted by the second
dwfl_segment_report_module pass, finding the vDSO did not do anything else
than just to call dwfl_segment_report_module for its segment.


> > +      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;
> > +	}
> 
> Should this last if condition have iterations > 2 && ... ?

No.

> Or do you want the executable and vdso also in the r_debug_info?

vDSO does not matter and executable should be in r_debug_info.


> This looks right. But what a lot of extra work just so some parts can be
> left out of a core file. And we better hope the given executable really
> matches the core file.

Fun is this all is now worthless - only for backward compatibility which has
discutable value for core files - as it has been obsoleted by NT_FILE;
probably, not sure, NT_FILE contains the list of any mapped files, not just
the files really opened as shared libraries.

But NT_FILE discussion is outside of the scope of this patch.


Attaching significantly simplified patch.


Thanks,
Jan



commit cd584cc822842862923670ebc44d40bf55485a69
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-28  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 (clear_r_debug_info): New function.
    	(dwfl_core_file_report): Move raw segments reporting lower.  New
    	variable r_debug_info, pass it to dwfl_segment_report_module.  Call
    	clear_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 overwrite 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 it 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.  Store r_debug_info->module info when
    	appropriate.
    	(dwfl_link_map_report): Add parameter r_debug_info.  New variable
    	in_ok.  Try to read IN from EXECUTABLE_FOR_CORE.  Update report_r_debug
    	caller parameters.
    
    tests/
    2013-05-28  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..d5e340c 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
+clear_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,43 @@ 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);
+
+  /* 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
+  ndx = 0;
+  do
+    {
+      int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
+					    &dwfl_elf_phdr_memory_callback, elf,
+					    core_file_read_eagerly, elf,
+					    &r_debug_info);
+      if (unlikely (seg < 0))
 	{
-	  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;
+	  clear_r_debug_info (&r_debug_info);
+	  return seg;
+	}
+      if (seg > ndx)
+	{
+	  ndx = seg;
+	  ++sniffed;
 	}
-      while (ndx < (int) phnum);
+      else
+	++ndx;
+    }
+  while (ndx < (int) phnum);
+
+  clear_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..d454ccb 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,47 @@ 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)
+	{
+	  /* L_LD read from link map must be right while DYN_VADDR is unsafe.
+	     Therefore subtract DYN_VADDR and add L_LD to get a possibly
+	     corrective displacement for all addresses computed so far.  */
+	  GElf_Addr fixup = module->l_ld - dyn_vaddr;
+	  if ((fixup & (dwfl->segment_align - 1)) == 0
+	      && 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 +560,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..69e6e12 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,29 @@ 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
+{
+  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 +485,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..e752a5d 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);
@@ -349,12 +352,84 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
       if (name != NULL && name[0] == '\0')
 	name = NULL;
 
-      /* We have to find the file's phdrs to compute along with l_addr
-	 what its runtime address boundaries are.  */
+      if (iterations == 1 && dwfl->executable_for_core != NULL)
+	name = dwfl->executable_for_core;
 
-      // XXX hook for sysroot
-      mod = INTUSE(dwfl_report_elf) (dwfl, basename (name),
-				     name, -1, l_addr, true);
+      Dwfl_Module *mod = NULL;
+      if (name != NULL)
+	{
+	  /* 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 (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 +645,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;
 
@@ -668,8 +744,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
 	      {
@@ -831,6 +964,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..6ede4c7 100755
--- a/tests/run-unstrip-n.sh
+++ b/tests/run-unstrip-n.sh
@@ -35,20 +35,35 @@ testfiles testcore-rtlib testcore-rtlib-ppc
 
 testrun_compare ${abs_top_builddir}/src/unstrip -n --core=testcore-rtlib <<\EOF
 0x8048000+0x2000 f1c600bc36cb91bf01f9a63a634ecb79aa4c3199(a)0x8048178 . - [exe]
+0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148(a)0xf75e9164 - - libpthread.so.0
+0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de(a)0xf7603184 - - libc.so.6
+0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37(a)0xf77b3164 - - librt.so.1
 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
+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
+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
+0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5(a)0x10000174 . - [exe]
+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
+0x7f67f2aaf000+0x202000 - . - test-core-lib.so
+0x7fff1596c000+0x1000 a9cf37f53897b5468ee018655760be61b8633d3c(a)0x7fff1596c340 . - linux-vdso.so.1
 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]