readelf bug exposed with change from 2003-12-29

Nick Clifton nickc@redhat.com
Wed Jan 7 18:50:00 GMT 2004


Hi Hans-Peter,

> Hi.  Since this change:
>
> 2003-12-29  Nick Clifton  <nickc@redhat.com>
>
> 	* readelf.c (debug_line_pointer_size): Replace with an array
>         called 'debug_line_pointer_sizes'.
>         (num_debug_line_pointer_sizes): New variable.
>         (display_debug_lines): Extract pointer size from the
>         debug_line_pointer_sizes array.
>         (display_debug_loc): Likewise.
>         (prescan_debug_info): Fill in the debug_line_pointer_sizes
>         array.
>
> I'm getting these errors:
>
> Running /home/hp/binutils/cvs_latest/src/gas/testsuite/gas/cris/cris.exp ...
> FAIL: gas/cris/rd-dw2-1

oops - sorry!

> Unfortunately I see no small and obvious fix.  Reinstating the
> fallback would just be a hack.

No the proper fix is to read the .debug_info section as and when
necessary.

I am going to apply the patch below to fix this problem.  The patch
also includes a small change to the rd-dw2-1.d cris test file so that
it expects the "(pointer size:  4)" output from readelf.

Cheers
        Nick

binutils/ChangeLog
2004-01-07  Nick Clifton  <nickc@redhat.com>

	* readelf.c (find_section): New function.  Locates a named
	section.
	(get_debug_line_pointer_sizes): New function:  Initialises the
	debug_line_pointer_sizes array.
        (display_debug_lines): Call get_debug_line_pointer_sizes.
        (display_debug_loc): Likewise.
        (load_debug_loc): Use find_section.
        (load_debug_str): Likewise.
        (display_debug_info): Likewise.
        (prescan_debug_info): Delete.
        (debug_displays): Remove prescan field.
        (process_section_contents): Do not perform prescans.

Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.228
diff -c -3 -p -r1.228 readelf.c
*** binutils/readelf.c	29 Dec 2003 14:22:07 -0000	1.228
--- binutils/readelf.c	7 Jan 2004 18:41:18 -0000
*************** process_extended_line_op (unsigned char 
*** 6271,6286 ****
    return len;
  }
  
  /* Size of pointers in the .debug_line section.  This information is not
     really present in that section.  It's obtained before dumping the debug
     sections by doing some pre-scan of the .debug_info section.  */
  static unsigned int * debug_line_pointer_sizes = NULL;
  static unsigned int   num_debug_line_pointer_sizes = 0;
  
  static int
  display_debug_lines (Elf_Internal_Shdr *section,
! 		     unsigned char * start,
! 		     FILE *file ATTRIBUTE_UNUSED)
  {
    unsigned char *hdrptr;
    DWARF2_Internal_LineInfo info;
--- 6271,6407 ----
    return len;
  }
  
+ /* Finds section NAME inside FILE and returns a
+    pointer to it, or NULL upon failure.  */
+ 
+ static Elf_Internal_Shdr *
+ find_section (const char * name)
+ {
+   Elf_Internal_Shdr *sec;
+   unsigned int i;
+   
+   for (i = elf_header.e_shnum, sec = section_headers + i - 1;
+        i; --i, --sec)
+     if (strcmp (SECTION_NAME (sec), name) == 0)
+       break;
+ 
+   if (i && sec && sec->sh_size != 0)
+     return sec;
+ 
+   return NULL;
+ }
+ 
  /* Size of pointers in the .debug_line section.  This information is not
     really present in that section.  It's obtained before dumping the debug
     sections by doing some pre-scan of the .debug_info section.  */
  static unsigned int * debug_line_pointer_sizes = NULL;
  static unsigned int   num_debug_line_pointer_sizes = 0;
  
+ /* Locate and scan the .debug_info section in the file and record the pointer
+    sizes for the compilation units in it.  Usually an executable will have
+    just one pointer size, but this is not guaranteed, and so we try not to
+    make any assumptions.  Returns zero upon failure, or the number of
+    compilation units upon success.  */
+ 
+ static unsigned int
+ get_debug_line_pointer_sizes (FILE * file)
+ {
+   Elf_Internal_Shdr * section;
+   unsigned char *     start;
+   unsigned char *     end;
+   unsigned char *     begin;
+   unsigned long       length;
+   unsigned int        num_units;
+   unsigned int        unit;
+ 
+   section = find_section (".debug_info");
+   if (section == NULL)
+     return 0;
+ 
+   length = section->sh_size;
+   start = get_data (NULL, file, section->sh_offset, section->sh_size,
+ 		    _("extracting pointer sizes from .debug_info section"));
+   if (start == NULL)
+     return 0;
+ 
+   end = start + section->sh_size;
+   /* First scan the section to get the number of comp units.  */
+   for (begin = start, num_units = 0; begin < end; num_units++)
+     {
+       /* Read the first 4 bytes.  For a 32-bit DWARF section, this will
+ 	 be the length.  For a 64-bit DWARF section, it'll be the escape
+ 	 code 0xffffffff followed by an 8 byte length.  */
+       length = byte_get (begin, 4);
+ 
+       if (length == 0xffffffff)
+ 	{
+ 	  length = byte_get (begin + 4, 8);
+ 	  begin += length + 12;
+ 	}
+       else
+ 	begin += length + 4;
+     }
+ 
+   if (num_units == 0)
+     {
+       error (_("No comp units in .debug_info section ?"));
+       free (start);
+       return 0;
+     }
+ 
+   /* Then allocate an array to hold the pointer sizes.  */
+   debug_line_pointer_sizes = malloc (num_units * sizeof * debug_line_pointer_sizes);
+   if (debug_line_pointer_sizes == NULL)
+     {
+       error (_("Not enough memory for a pointer size array of %u entries"),
+ 	     num_units);
+       free (start);
+       return 0;
+     }
+ 
+   /* Populate the array.  */
+   for (begin = start, unit = 0; begin < end; unit++)
+     {
+       length = byte_get (begin, 4);
+       if (length == 0xffffffff)
+ 	{
+ 	  /* For 64-bit DWARF, the 1-byte address_size field is 22 bytes
+ 	     from the start of the section.  This is computed as follows:
+ 
+ 	     unit_length:         12 bytes
+ 	     version:              2 bytes
+ 	     debug_abbrev_offset:  8 bytes
+ 	     -----------------------------
+ 	     Total:               22 bytes  */
+ 
+ 	  debug_line_pointer_sizes [unit] = byte_get (begin + 22, 1);
+ 	  length = byte_get (begin + 4, 8);
+ 	  begin += length + 12;
+ 	}
+       else
+ 	{
+ 	  /* For 32-bit DWARF, the 1-byte address_size field is 10 bytes from
+ 	     the start of the section:
+ 	     
+ 	     unit_length:          4 bytes
+ 	     version:              2 bytes
+ 	     debug_abbrev_offset:  4 bytes
+ 	     -----------------------------
+ 	     Total:               10 bytes  */
+ 
+ 	  debug_line_pointer_sizes [unit] = byte_get (begin + 10, 1);
+ 	  begin += length + 4;
+ 	}
+     }
+ 
+   free (start);
+   num_debug_line_pointer_sizes = num_units;
+   return num_units;
+ }
+ 
  static int
  display_debug_lines (Elf_Internal_Shdr *section,
! 		     unsigned char *start, FILE *file)
  {
    unsigned char *hdrptr;
    DWARF2_Internal_LineInfo info;
*************** display_debug_lines (Elf_Internal_Shdr *
*** 6296,6301 ****
--- 6417,6425 ----
    printf (_("\nDump of debug contents of section %s:\n\n"),
  	  SECTION_NAME (section));
  
+   if (num_debug_line_pointer_sizes == 0)
+     get_debug_line_pointer_sizes (file);
+ 
    while (data < end)
      {
        unsigned int pointer_size;
*************** static void
*** 7506,7525 ****
  load_debug_loc (FILE *file)
  {
    Elf_Internal_Shdr *sec;
-   unsigned int i;
  
    /* If it is already loaded, do nothing.  */
    if (debug_loc_contents != NULL)
      return;
  
    /* Locate the .debug_loc section.  */
!   for (i = 0, sec = section_headers;
!        i < elf_header.e_shnum;
!        i++, sec++)
!     if (strcmp (SECTION_NAME (sec), ".debug_loc") == 0)
!       break;
! 
!   if (i == elf_header.e_shnum || sec->sh_size == 0)
      return;
  
    debug_loc_size = sec->sh_size;
--- 7630,7643 ----
  load_debug_loc (FILE *file)
  {
    Elf_Internal_Shdr *sec;
  
    /* If it is already loaded, do nothing.  */
    if (debug_loc_contents != NULL)
      return;
  
    /* Locate the .debug_loc section.  */
!   sec = find_section (".debug_loc");
!   if (sec == NULL)
      return;
  
    debug_loc_size = sec->sh_size;
*************** free_debug_loc (void)
*** 7542,7549 ****
  
  static int
  display_debug_loc (Elf_Internal_Shdr *section,
! 		   unsigned char *start,
! 		   FILE *file ATTRIBUTE_UNUSED)
  {
    unsigned char *section_end;
    unsigned long bytes;
--- 7660,7666 ----
  
  static int
  display_debug_loc (Elf_Internal_Shdr *section,
! 		   unsigned char *start, FILE *file)
  {
    unsigned char *section_end;
    unsigned long bytes;
*************** display_debug_loc (Elf_Internal_Shdr *se
*** 7561,7566 ****
--- 7678,7686 ----
        return 0;
      }
  
+   if (num_debug_line_pointer_sizes == 0)
+     get_debug_line_pointer_sizes (file);
+ 
    printf (_("Contents of the .debug_loc section:\n\n"));
    printf (_("\n    Offset   Begin    End      Expression\n"));
  
*************** static void
*** 7625,7644 ****
  load_debug_str (FILE *file)
  {
    Elf_Internal_Shdr *sec;
-   unsigned int i;
  
    /* If it is already loaded, do nothing.  */
    if (debug_str_contents != NULL)
      return;
  
    /* Locate the .debug_str section.  */
!   for (i = 0, sec = section_headers;
!        i < elf_header.e_shnum;
!        i++, sec++)
!     if (strcmp (SECTION_NAME (sec), ".debug_str") == 0)
!       break;
! 
!   if (i == elf_header.e_shnum || sec->sh_size == 0)
      return;
  
    debug_str_size = sec->sh_size;
--- 7745,7758 ----
  load_debug_str (FILE *file)
  {
    Elf_Internal_Shdr *sec;
  
    /* If it is already loaded, do nothing.  */
    if (debug_str_contents != NULL)
      return;
  
    /* Locate the .debug_str section.  */
!   sec = find_section (".debug_str");
!   if (sec == NULL)
      return;
  
    debug_str_size = sec->sh_size;
*************** display_debug_info (Elf_Internal_Shdr *s
*** 8100,8106 ****
        unsigned char *hdrptr;
        unsigned char *cu_abbrev_offset_ptr;
        unsigned char *tags;
-       unsigned int i;
        int level;
        unsigned long cu_offset;
        int offset_size;
--- 8214,8219 ----
*************** display_debug_info (Elf_Internal_Shdr *s
*** 8224,8236 ****
  	unsigned char *begin;
  
  	/* Locate the .debug_abbrev section and process it.  */
! 	for (i = 0, sec = section_headers;
! 	     i < elf_header.e_shnum;
! 	     i++, sec++)
! 	  if (strcmp (SECTION_NAME (sec), ".debug_abbrev") == 0)
! 	    break;
! 
! 	if (i == elf_header.e_shnum || sec->sh_size == 0)
  	  {
  	    warn (_("Unable to locate .debug_abbrev section!\n"));
  	    return 0;
--- 8337,8344 ----
  	unsigned char *begin;
  
  	/* Locate the .debug_abbrev section and process it.  */
! 	sec = find_section (".debug_abbrev");
! 	if (sec == NULL)
  	  {
  	    warn (_("Unable to locate .debug_abbrev section!\n"));
  	    return 0;
*************** display_debug_not_supported (Elf_Interna
*** 9194,9314 ****
    return 1;
  }
  
! /* Pre-scan the .debug_info section to record the pointer sizes for the
!    compilation units.  Usually an executable will have just one pointer
!    size, but this is not guaranteed, and so we try not to make any
!    assumptions.  Returns zero upon failure, or the number of compilation
!    units upon success.  */
! 
! static unsigned int
! prescan_debug_info (Elf_Internal_Shdr *section, unsigned char *start,
! 		    FILE *file ATTRIBUTE_UNUSED)
! {
!   unsigned char *begin;
!   unsigned char *end = start + section->sh_size;
!   unsigned long  length;
!   unsigned int   num_units;
!   unsigned int   unit;
!     
!   /* First scan the section to compute the number of comp units.  */
!   for (begin = start, num_units = 0; begin < end; num_units++)
!     {
!       /* Read the first 4 bytes.  For a 32-bit DWARF section, this will
! 	 be the length.  For a 64-bit DWARF section, it'll be the escape
! 	 code 0xffffffff followed by an 8 byte length.  */
!       length = byte_get (begin, 4);
! 
!       if (length == 0xffffffff)
! 	{
! 	  length = byte_get (begin + 4, 8);
! 	  begin += length + 12;
! 	}
!       else
! 	begin += length + 4;
!     }
! 
!   if (num_units == 0)
!     {
!       error (_("No comp units in .debug_info section ?"));
!       return 0;
!     }
! 
!   /* Then allocate an array to hold the pointer sizes.  */
!   debug_line_pointer_sizes = malloc (num_units * sizeof * debug_line_pointer_sizes);
!   if (debug_line_pointer_sizes == NULL)
!     {
!       error (_("Not enough memory for a pointer size array of %u entries"),
! 	     num_units);
!       return 0;
!     }
! 
!   /* Populate the array.  */
!   for (begin = start, unit = 0; begin < end; unit++)
!     {
!       length = byte_get (begin, 4);
!       if (length == 0xffffffff)
! 	{
! 	  /* For 64-bit DWARF, the 1-byte address_size field is 22 bytes
! 	     from the start of the section.  This is computed as follows:
! 
! 	     unit_length:         12 bytes
! 	     version:              2 bytes
! 	     debug_abbrev_offset:  8 bytes
! 	     -----------------------------
! 	     Total:               22 bytes  */
! 
! 	  debug_line_pointer_sizes [unit] = byte_get (begin + 22, 1);
! 	  length = byte_get (begin + 4, 8);
! 	  begin += length + 12;
! 	}
!       else
! 	{
! 	  /* For 32-bit DWARF, the 1-byte address_size field is 10 bytes from
! 	     the start of the section:
! 	     
! 	     unit_length:          4 bytes
! 	     version:              2 bytes
! 	     debug_abbrev_offset:  4 bytes
! 	     -----------------------------
! 	     Total:               10 bytes  */
! 
! 	  debug_line_pointer_sizes [unit] = byte_get (begin + 10, 1);
! 	  begin += length + 4;
! 	}
!     }
! 
!   num_debug_line_pointer_sizes = num_units;
!   return num_units;
! }
! 
! /* A structure containing the name of a debug section and a pointer
!    to a function that can decode it.  The third field is a prescan
!    function to be run over the section before displaying any of the
!    sections.  */
  struct
  {
    const char *const name;
    int (*display) (Elf_Internal_Shdr *, unsigned char *, FILE *);
-   int (*prescan) (Elf_Internal_Shdr *, unsigned char *, FILE *);
  }
  debug_displays[] =
  {
!   { ".debug_abbrev",		display_debug_abbrev, NULL },
!   { ".debug_aranges",		display_debug_aranges, NULL },
!   { ".debug_frame",		display_debug_frames, NULL },
!   { ".debug_info",		display_debug_info, prescan_debug_info },
!   { ".debug_line",		display_debug_lines, NULL },
!   { ".debug_pubnames",		display_debug_pubnames, NULL },
!   { ".eh_frame",		display_debug_frames, NULL },
!   { ".debug_macinfo",		display_debug_macinfo, NULL },
!   { ".debug_str",		display_debug_str, NULL },
!   { ".debug_loc",		display_debug_loc, NULL },
!   { ".debug_pubtypes",		display_debug_not_supported, NULL },
!   { ".debug_ranges",		display_debug_not_supported, NULL },
!   { ".debug_static_func",	display_debug_not_supported, NULL },
!   { ".debug_static_vars",	display_debug_not_supported, NULL },
!   { ".debug_types",		display_debug_not_supported, NULL },
!   { ".debug_weaknames",		display_debug_not_supported, NULL }
  };
  
  static int
--- 9302,9332 ----
    return 1;
  }
  
! /* A structure containing the name of a debug section
!    and a pointer to a function that can decode it.  */
  struct
  {
    const char *const name;
    int (*display) (Elf_Internal_Shdr *, unsigned char *, FILE *);
  }
  debug_displays[] =
  {
!   { ".debug_abbrev",		display_debug_abbrev },
!   { ".debug_aranges",		display_debug_aranges },
!   { ".debug_frame",		display_debug_frames },
!   { ".debug_info",		display_debug_info },
!   { ".debug_line",		display_debug_lines },
!   { ".debug_pubnames",		display_debug_pubnames },
!   { ".eh_frame",		display_debug_frames },
!   { ".debug_macinfo",		display_debug_macinfo },
!   { ".debug_str",		display_debug_str },
!   { ".debug_loc",		display_debug_loc },
!   { ".debug_pubtypes",		display_debug_not_supported },
!   { ".debug_ranges",		display_debug_not_supported },
!   { ".debug_static_func",	display_debug_not_supported },
!   { ".debug_static_vars",	display_debug_not_supported },
!   { ".debug_types",		display_debug_not_supported },
!   { ".debug_weaknames",		display_debug_not_supported }
  };
  
  static int
*************** process_section_contents (FILE *file)
*** 9362,9403 ****
  
    if (! do_dump)
      return 1;
- 
-   /* Pre-scan the debug sections to find some debug information not
-      present in some of them.  For the .debug_line, we must find out the
-      size of address (specified in .debug_info and .debug_aranges).  */
-   for (i = 0, section = section_headers;
-        i < elf_header.e_shnum && i < num_dump_sects;
-        i++, section++)
-     {
-       char *name = SECTION_NAME (section);
-       int j;
- 
-       if (section->sh_size == 0)
- 	continue;
- 
-       /* See if there is some pre-scan operation for this section.  */
-       for (j = NUM_ELEM (debug_displays); j--;)
- 	if (strcmp (debug_displays[j].name, name) == 0)
- 	  {
- 	    if (debug_displays[j].prescan != NULL)
- 	      {
- 		bfd_size_type length;
- 		unsigned char *start;
- 
- 		length = section->sh_size;
- 		start = get_data (NULL, file, section->sh_offset, length,
- 				  _("debug section data"));
- 		if (!start)
- 		  return 0;
- 
- 		debug_displays[j].prescan (section, start, file);
- 		free (start);
- 	      }
- 
- 	    break;
- 	  }
-     }
  
    for (i = 0, section = section_headers;
         i < elf_header.e_shnum && i < num_dump_sects;
--- 9380,9385 ----


gas/testsuite/ChangeLog
2004-01-07  Nick Clifton  <nickc@redhat.com>

	* gas/cris/rd-dw2-1.d: Expect a pointer size from readelf.

Index: gas/testsuite/gas/cris/rd-dw2-1.d
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/cris/rd-dw2-1.d,v
retrieving revision 1.3
diff -c -3 -p -r1.3 rd-dw2-1.d
*** gas/testsuite/gas/cris/rd-dw2-1.d	15 Jun 2003 23:46:37 -0000	1.3
--- gas/testsuite/gas/cris/rd-dw2-1.d	7 Jan 2004 18:41:18 -0000
*************** Dump of debug contents of section \.debu
*** 14,19 ****
--- 14,20 ----
    Line Base:                   -5
    Line Range:                  14
    Opcode Base:                 10
+   \(Pointer size:               4\)
  
   Opcodes:
    Opcode 1 has 0 args
                



More information about the Binutils mailing list