This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


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

Finding dwarf2 abbrev_offsets after multi-object ld -r


This problem was first reported to me on mn10300-elf: after linking
together with -r a couple of object files with debugging info, the
linker would fail when loading the debugging information of this
object file, when attempting to find out the line number in which an
error that was about to be reported occurred.

The problem is that, on targets with RELA relocations, when multiple
object files are linked together into a single one, the abbrev offsets
of the comp units other than the first are non-zero, but this is not
reflected in the debugging info section data, only in its relocations,
that point to .debug_abbrev plus the offset.  Because we failed to
take the relocation into account, we always attempted to load the
abbrev table of the first translation unit, which would often fail to
make sense to other translation units.

These patches fix the problem in bfd and readelf, so that they now
load the correct abbrev table for each comp unit.  They were approved
by Nick Clifton.  I'm checking them in.

Index: bfd/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	* dwarf2.c (struct dwarf2_debug): Add sec, sec_info_ptr and syms.
	(find_rela_addend): New function.
	(parse_comp_unit): Call it to find the abbrev offset addend.
	(_bfd_dwarf2_find_nearest_line): Initialize and maintain the new
	members of dwarf2_debug as debugging information is read.

Index: bfd/dwarf2.c
===================================================================
RCS file: /cvs/src/src/bfd/dwarf2.c,v
retrieving revision 1.20
diff -u -p -r1.20 dwarf2.c
--- bfd/dwarf2.c 2001/05/21 11:52:42 1.20
+++ bfd/dwarf2.c 2001/08/08 09:56:15
@@ -96,6 +96,14 @@ struct dwarf2_debug
   /* Pointer to the end of the .debug_info section memory buffer.  */
   char* info_ptr_end;
 
+  /* Pointer to the section and address of the beginning of the
+     section.  */
+  asection* sec;
+  char* sec_info_ptr;
+
+  /* Pointer to the symbol table.  */
+  asymbol** syms;
+
   /* Pointer to the .debug_abbrev section loaded into memory.  */
   char* dwarf_abbrev_buffer;
 
@@ -1219,6 +1227,60 @@ scan_unit_for_functions (unit)
   return true;
 }
 
+/* Look for a RELA relocation to be applied on OFFSET of section SEC,
+   and return the addend if such a relocation is found.  Since this is
+   only used to find relocations referring to the .debug_abbrev
+   section, we make sure the relocation refers to this section, but
+   this is not strictly necessary, and it can probably be safely
+   removed if needed.  However, it is important to note that this
+   function only returns the addend, it doesn't serve the purpose of
+   applying a generic relocation.
+
+   If no suitable relocation is found, or if it is not a real RELA
+   relocation, this function returns 0.  */
+
+static bfd_vma
+find_rela_addend (abfd, sec, offset, syms)
+     bfd* abfd;
+     asection* sec;
+     bfd_size_type offset;
+     asymbol** syms;
+{
+  long reloc_size = bfd_get_reloc_upper_bound (abfd, sec);
+  arelent **relocs = NULL;
+  long reloc_count, relc;
+
+  if (reloc_size <= 0)
+    return 0;
+
+  relocs = (arelent **) bfd_malloc ((size_t) reloc_size);
+  if (relocs == NULL)
+    return 0;
+
+  reloc_count = bfd_canonicalize_reloc (abfd, sec, relocs, syms);
+
+  if (reloc_count <= 0)
+    {
+      free (relocs);
+      return 0;
+    }
+
+  for (relc = 0; relc < reloc_count; relc++)
+    if (relocs[relc]->address == offset
+	&& (*relocs[relc]->sym_ptr_ptr)->flags & BSF_SECTION_SYM
+	&& strcmp ((*relocs[relc]->sym_ptr_ptr)->name,
+		   ".debug_abbrev") == 0)
+      {
+	bfd_vma addend = (relocs[relc]->howto->partial_inplace
+			  ? 0 : relocs[relc]->addend);
+	free (relocs);
+	return addend;
+      }
+  
+  free (relocs);
+  return 0;
+}
+
 /* Parse a DWARF2 compilation unit starting at INFO_PTR.  This
    includes the compilation unit header that proceeds the DIE's, but
    does not include the length field that preceeds each compilation
@@ -1259,6 +1321,13 @@ parse_comp_unit (abfd, stash, unit_lengt
     abbrev_offset = read_4_bytes (abfd, info_ptr);
   else if (abbrev_length == 8)
     abbrev_offset = read_8_bytes (abfd, info_ptr);
+  /* The abbrev offset is generally a relocation pointing to
+     .debug_abbrev+offset.  On RELA targets, we have to find the
+     relocation and extract the addend to obtain the actual
+     abbrev_offset, so do it here.  */
+  abbrev_offset += find_rela_addend (abfd, stash->sec,
+				     info_ptr - stash->sec_info_ptr,
+				     stash->syms);
   info_ptr += abbrev_length;
   addr_size = read_1_byte (abfd, info_ptr);
   info_ptr += 1;
@@ -1498,7 +1567,7 @@ _bfd_dwarf2_find_nearest_line (abfd, sec
 			       addr_size, pinfo)
      bfd *abfd;
      asection *section;
-     asymbol **symbols ATTRIBUTE_UNUSED;
+     asymbol **symbols;
      bfd_vma offset;
      const char **filename_ptr;
      const char **functionname_ptr;
@@ -1583,8 +1652,12 @@ _bfd_dwarf2_find_nearest_line (abfd, sec
 
 	  stash->info_ptr_end = stash->info_ptr + start + size;
 	}
+
+      BFD_ASSERT (stash->info_ptr_end == stash->info_ptr + total_size);
 
-      BFD_ASSERT (stash->info_ptr_end = stash->info_ptr + total_size);
+      stash->sec = find_debug_info (abfd, NULL);
+      stash->sec_info_ptr = stash->info_ptr;
+      stash->syms = symbols;
     }
 
   /* FIXME: There is a problem with the contents of the
@@ -1631,6 +1704,13 @@ _bfd_dwarf2_find_nearest_line (abfd, sec
         {
 	  each = parse_comp_unit (abfd, stash, length, addr_size);
 	  stash->info_ptr += length;
+
+	  if ((bfd_vma) (stash->info_ptr - stash->sec_info_ptr)
+	      == stash->sec->_raw_size)
+	    {
+	      stash->sec = find_debug_info (abfd, stash->sec);
+	      stash->sec_info_ptr = stash->info_ptr;
+	    }
 
 	  if (each)
 	    {
Index: binutils/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	* readelf.c (display_debug_info): Add to abbrev_offset the addend
	of any RELA relocation for the abbrev_offset memory location that
	refers to the .debug_abbrev section symbol.

Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.109
diff -u -p -r1.109 readelf.c
--- binutils/readelf.c 2001/07/05 07:49:05 1.109
+++ binutils/readelf.c 2001/08/04 12:08:11
@@ -7026,6 +7026,7 @@ display_debug_info (section, start, file
     {
       DWARF2_External_CompUnit * external;
       DWARF2_Internal_CompUnit   compunit;
+      Elf32_Internal_Shdr *      relsec;
       unsigned char *            tags;
       int                        i;
       int			 level;
@@ -7037,6 +7038,68 @@ display_debug_info (section, start, file
       compunit.cu_version       = BYTE_GET (external->cu_version);
       compunit.cu_abbrev_offset = BYTE_GET (external->cu_abbrev_offset);
       compunit.cu_pointer_size  = BYTE_GET (external->cu_pointer_size);
+
+      /* Check for RELA relocations in the abbrev_offset address, and
+         apply them.  */
+      for (relsec = section_headers;
+	   relsec < section_headers + elf_header.e_shnum;
+	   ++relsec)
+	{
+	  unsigned long nrelas, nsyms;
+	  Elf_Internal_Rela *rela, *rp;
+	  Elf32_Internal_Shdr *symsec;
+	  Elf_Internal_Sym *symtab;
+	  Elf_Internal_Sym *sym;
+
+	  if (relsec->sh_type != SHT_RELA
+	      || section_headers + relsec->sh_info != section)
+	    continue;
+
+	  if (!slurp_rela_relocs (file, relsec->sh_offset, relsec->sh_size,
+				  & rela, & nrelas))
+	    return 0;
+
+	  symsec = section_headers + relsec->sh_link;
+	  nsyms = symsec->sh_size / symsec->sh_entsize;
+	  symtab = GET_ELF_SYMBOLS (file, symsec->sh_offset, nsyms);
+
+	  for (rp = rela; rp < rela + nrelas; ++rp)
+	    {
+	      if (rp->r_offset
+		  != (bfd_vma) ((unsigned char *) &external->cu_abbrev_offset
+				- section_begin))
+		continue;
+	      
+	      if (is_32bit_elf)
+		{
+		  sym = symtab + ELF32_R_SYM (rp->r_info);
+
+		  if (ELF32_ST_TYPE (sym->st_info) != STT_SECTION)
+		    {
+		      warn (_("Skipping unexpected symbol type %u"),
+			    ELF32_ST_TYPE (sym->st_info));
+		      continue;
+		    }
+		}
+	      else
+		{
+		  sym = symtab + ELF64_R_SYM (rp->r_info);
+
+		  if (ELF64_ST_TYPE (sym->st_info) != STT_SECTION)
+		    {
+		      warn (_("Skipping unexpected symbol type %u"),
+			    ELF64_ST_TYPE (sym->st_info));
+		      continue;
+		    }
+		}
+
+	      compunit.cu_abbrev_offset += rp->r_addend;
+	      break;
+	    }
+
+	  free (rela);
+	  break;
+	}
 
       tags = start + sizeof (* external);
       cu_offset = start - section_begin;

-- 
Alexandre Oliva   Enjoy Guarana', see http://www.ic.unicamp.br/~oliva/
Red Hat GCC Developer                  aoliva@{cygnus.com, redhat.com}
CS PhD student at IC-Unicamp        oliva@{lsd.ic.unicamp.br, gnu.org}
Free Software Evangelist    *Please* write to mailing lists, not to me

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