[readelf] Handle .gdb_index section version 9
[binutils-gdb.git] / binutils / dwarf.c
index 4d90ccb3e0ebefd2106e2776ff0a1ec9c917c3c0..2f4bb30ffd344fb816e2bcfc7c9b999d45ed0125 100644 (file)
@@ -716,65 +716,10 @@ fetch_indexed_addr (uint64_t offset, uint32_t num_bytes)
   return byte_get (section->start + offset, num_bytes);
 }
 
-/* Fetch a value from a debug section that has been indexed by
-   something in another section.
-   Returns -1 if the value could not be found.  */
+/* This is for resolving DW_FORM_rnglistx and DW_FORM_loclistx.
 
-static uint64_t
-fetch_indexed_value (uint64_t idx,
-                    enum dwarf_section_display_enum sec_enum,
-                    uint64_t base_address)
-{
-  struct dwarf_section *section = &debug_displays [sec_enum].section;
-
-  if (section->start == NULL)
-    {
-      warn (_("Unable to locate %s section\n"), section->uncompressed_name);
-      return -1;
-    }
-
-  if (section->size < 4)
-    {
-      warn (_("Section %s is too small to contain an value indexed from another section!\n"),
-           section->name);
-      return -1;
-    }
-
-  uint32_t pointer_size, bias;
-
-  if (byte_get (section->start, 4) == 0xffffffff)
-    {
-      pointer_size = 8;
-      bias = 20;
-    }
-  else
-    {
-      pointer_size = 4;
-      bias = 12;
-    }
-
-  uint64_t offset = idx * pointer_size;
-
-  /* Offsets are biased by the size of the section header
-     or base address.  */
-  if (base_address)
-    offset += base_address;
-  else
-    offset += bias;
-
-  if (offset + pointer_size > section->size)
-    {
-      warn (_("Offset into section %s too big: %#" PRIx64 "\n"),
-           section->name, offset);
-      return -1;
-    }
-
-  return byte_get (section->start + offset, pointer_size);
-}
-
-/* Like fetch_indexed_value() but specifically for resolving DW_FORM_rnglistx and DW_FORM_loclistx.
-
-   The memory layout is: base_address points at a table of offsets, relative to the section start.
+   The memory layout is: base_address (taken from the CU's top DIE) points at a table of offsets,
+   relative to the section start.
    The table of offsets contains the offsets of objects of interest relative to the table of offsets.
    IDX is the index of the desired object in said table of offsets.
 
@@ -786,6 +731,7 @@ fetch_indexed_offset (uint64_t                         idx,
                      uint64_t                         base_address,
                      uint64_t                         offset_size)
 {
+  uint64_t offset_of_offset = base_address + idx * offset_size;
   struct dwarf_section *section = &debug_displays [sec_enum].section;
 
   if (section->start == NULL)
@@ -801,16 +747,14 @@ fetch_indexed_offset (uint64_t                         idx,
       return -1;
     }
 
-  base_address += idx * offset_size;
-
-  if (base_address + offset_size > section->size)
+  if (offset_of_offset + offset_size >= section->size)
     {
       warn (_("Offset of %#" PRIx64 " is too big for section %s\n"),
-           base_address, section->name);
+           offset_of_offset, section->name);
       return -1;
     }
   
-  return base_address + byte_get (section->start + base_address, offset_size);
+  return base_address + byte_get (section->start + offset_of_offset, offset_size);
 }
 
 /* FIXME:  There are better and more efficient ways to handle
@@ -2398,6 +2342,72 @@ display_discr_list (unsigned long form,
     printf (")(unsigned)");
 }
 
+static void
+display_lang (uint64_t uvalue)
+{
+  switch (uvalue)
+    {
+      /* Ordered by the numeric value of these constants.  */
+    case DW_LANG_C89:                  printf ("ANSI C"); break;
+    case DW_LANG_C:                    printf ("non-ANSI C"); break;
+    case DW_LANG_Ada83:                        printf ("Ada"); break;
+    case DW_LANG_C_plus_plus:          printf ("C++"); break;
+    case DW_LANG_Cobol74:              printf ("Cobol 74"); break;
+    case DW_LANG_Cobol85:              printf ("Cobol 85"); break;
+    case DW_LANG_Fortran77:            printf ("FORTRAN 77"); break;
+    case DW_LANG_Fortran90:            printf ("Fortran 90"); break;
+    case DW_LANG_Pascal83:             printf ("ANSI Pascal"); break;
+    case DW_LANG_Modula2:              printf ("Modula 2"); break;
+
+      /* DWARF 2.1 values.     */
+    case DW_LANG_Java:                 printf ("Java"); break;
+    case DW_LANG_C99:                  printf ("ANSI C99"); break;
+    case DW_LANG_Ada95:                        printf ("ADA 95"); break;
+    case DW_LANG_Fortran95:            printf ("Fortran 95"); break;
+
+      /* DWARF 3 values.  */
+    case DW_LANG_PLI:                  printf ("PLI"); break;
+    case DW_LANG_ObjC:                 printf ("Objective C"); break;
+    case DW_LANG_ObjC_plus_plus:       printf ("Objective C++"); break;
+    case DW_LANG_UPC:                  printf ("Unified Parallel C"); break;
+    case DW_LANG_D:                    printf ("D"); break;
+
+      /* DWARF 4 values.  */
+    case DW_LANG_Python:               printf ("Python"); break;
+
+      /* DWARF 5 values.  */
+    case DW_LANG_OpenCL:               printf ("OpenCL"); break;
+    case DW_LANG_Go:                   printf ("Go"); break;
+    case DW_LANG_Modula3:              printf ("Modula 3"); break;
+    case DW_LANG_Haskell:              printf ("Haskell"); break;
+    case DW_LANG_C_plus_plus_03:       printf ("C++03"); break;
+    case DW_LANG_C_plus_plus_11:       printf ("C++11"); break;
+    case DW_LANG_OCaml:                        printf ("OCaml"); break;
+    case DW_LANG_Rust:                 printf ("Rust"); break;
+    case DW_LANG_C11:                  printf ("C11"); break;
+    case DW_LANG_Swift:                        printf ("Swift"); break;
+    case DW_LANG_Julia:                        printf ("Julia"); break;
+    case DW_LANG_Dylan:                        printf ("Dylan"); break;
+    case DW_LANG_C_plus_plus_14:       printf ("C++14"); break;
+    case DW_LANG_Fortran03:            printf ("Fortran 03"); break;
+    case DW_LANG_Fortran08:            printf ("Fortran 08"); break;
+    case DW_LANG_RenderScript:         printf ("RenderScript"); break;
+
+      /* MIPS extension.  */
+    case DW_LANG_Mips_Assembler:       printf ("MIPS assembler"); break;
+
+      /* UPC extension.  */
+    case DW_LANG_Upc:                  printf ("Unified Parallel C"); break;
+
+    default:
+      if (uvalue >= DW_LANG_lo_user && uvalue <= DW_LANG_hi_user)
+       printf (_("implementation defined: %#" PRIx64 ""), uvalue);
+      else
+       printf (_("unknown: %#" PRIx64 ""), uvalue);
+      break;
+    }
+}
+
 static unsigned char *
 read_and_display_attr_value (unsigned long attribute,
                             unsigned long form,
@@ -2762,18 +2772,23 @@ read_and_display_attr_value (unsigned long attribute,
            {
              if (dwo)
                {
-                 idx = fetch_indexed_value (uvalue, loclists_dwo, 0);
+                 idx = fetch_indexed_offset (uvalue, loclists_dwo,
+                                             debug_info_p->loclists_base,
+                                             debug_info_p->offset_size);
                  if (idx != (uint64_t) -1)
                    idx += (offset_size == 8) ? 20 : 12;
                }
              else if (debug_info_p == NULL || dwarf_version > 4)
                {
-                 idx = fetch_indexed_value (uvalue, loclists, 0);
+                 idx = fetch_indexed_offset (uvalue, loclists,
+                                             debug_info_p->loclists_base,
+                                             debug_info_p->offset_size);
                }
              else
                {
                  /* We want to compute:
-                      idx = fetch_indexed_value (uvalue, loclists, debug_info_p->loclists_base);
+                      idx = fetch_indexed_value (uvalue, loclists,
+                                            debug_info_p->loclists_base);
                       idx += debug_info_p->loclists_base;
                      Fortunately we already have that sum cached in the
                      loc_offsets array.  */
@@ -2790,9 +2805,9 @@ read_and_display_attr_value (unsigned long attribute,
            {
              if (dwo)
                {
-                 idx = fetch_indexed_value (uvalue, rnglists, 0);
-                 if (idx != (uint64_t) -1)
-                   idx += (offset_size == 8) ? 20 : 12;
+                 idx = fetch_indexed_offset (uvalue, rnglists,
+                                             debug_info_p->rnglists_base,
+                                             debug_info_p->offset_size);
                }
              else
                {
@@ -2800,11 +2815,8 @@ read_and_display_attr_value (unsigned long attribute,
                    base = 0;
                  else
                    base = debug_info_p->rnglists_base;
-                 /* We do not have a cached value this time, so we perform the
-                    computation manually.  */
-                 idx = fetch_indexed_offset (uvalue, rnglists, base, debug_info_p->offset_size);
-                 if (idx != (uint64_t) -1)
-                   idx += base;
+                 idx = fetch_indexed_offset (uvalue, rnglists, base,
+                                             debug_info_p->offset_size);
                }
            }
          else
@@ -2927,7 +2939,9 @@ read_and_display_attr_value (unsigned long attribute,
                  debug_info_p->max_loc_offsets = lmax;
                }
              if (form == DW_FORM_loclistx)
-               uvalue = fetch_indexed_value (num, loclists, debug_info_p->loclists_base);
+               uvalue = fetch_indexed_offset (num, loclists,
+                                              debug_info_p->loclists_base,
+                                              debug_info_p->offset_size);
              else if (this_set != NULL)
                uvalue += this_set->section_offsets [DW_SECT_LOC];
 
@@ -2972,7 +2986,8 @@ read_and_display_attr_value (unsigned long attribute,
          if (need_base_address)
            {
              if (form == DW_FORM_addrx)
-               uvalue = fetch_indexed_addr (debug_info_p->addr_base + uvalue * pointer_size,
+               uvalue = fetch_indexed_addr (debug_info_p->addr_base
+                                            + uvalue * pointer_size,
                                             pointer_size);
 
              debug_info_p->base_address = uvalue;
@@ -2982,7 +2997,8 @@ read_and_display_attr_value (unsigned long attribute,
        case DW_AT_GNU_addr_base:
        case DW_AT_addr_base:
          debug_info_p->addr_base = uvalue;
-         /* Retrieved elsewhere so that it is in place by the time we read low_pc.  */
+         /* Retrieved elsewhere so that it is in
+            place by the time we read low_pc.  */
          break;
 
        case DW_AT_GNU_ranges_base:
@@ -3024,7 +3040,8 @@ read_and_display_attr_value (unsigned long attribute,
            switch (form)
              {
              case DW_FORM_strp:
-               add_dwo_name ((const char *) fetch_indirect_string (uvalue), cu_offset);
+               add_dwo_name ((const char *) fetch_indirect_string (uvalue),
+                             cu_offset);
                break;
              case DW_FORM_GNU_strp_alt:
                add_dwo_name ((const char *) fetch_alt_indirect_string (uvalue), cu_offset);
@@ -3035,7 +3052,8 @@ read_and_display_attr_value (unsigned long attribute,
              case DW_FORM_strx2:
              case DW_FORM_strx3:
              case DW_FORM_strx4:
-               add_dwo_name (fetch_indexed_string (uvalue, this_set, offset_size, false,
+               add_dwo_name (fetch_indexed_string (uvalue, this_set,
+                                                   offset_size, false,
                                                    debug_info_p->str_offsets_base),
                              cu_offset);
                break;
@@ -3158,61 +3176,9 @@ read_and_display_attr_value (unsigned long attribute,
       break;
 
     case DW_AT_language:
-      printf ("\t");
-      switch (uvalue)
-       {
-         /* Ordered by the numeric value of these constants.  */
-       case DW_LANG_C89:               printf ("(ANSI C)"); break;
-       case DW_LANG_C:                 printf ("(non-ANSI C)"); break;
-       case DW_LANG_Ada83:             printf ("(Ada)"); break;
-       case DW_LANG_C_plus_plus:       printf ("(C++)"); break;
-       case DW_LANG_Cobol74:           printf ("(Cobol 74)"); break;
-       case DW_LANG_Cobol85:           printf ("(Cobol 85)"); break;
-       case DW_LANG_Fortran77:         printf ("(FORTRAN 77)"); break;
-       case DW_LANG_Fortran90:         printf ("(Fortran 90)"); break;
-       case DW_LANG_Pascal83:          printf ("(ANSI Pascal)"); break;
-       case DW_LANG_Modula2:           printf ("(Modula 2)"); break;
-         /* DWARF 2.1 values.  */
-       case DW_LANG_Java:              printf ("(Java)"); break;
-       case DW_LANG_C99:               printf ("(ANSI C99)"); break;
-       case DW_LANG_Ada95:             printf ("(ADA 95)"); break;
-       case DW_LANG_Fortran95:         printf ("(Fortran 95)"); break;
-         /* DWARF 3 values.  */
-       case DW_LANG_PLI:               printf ("(PLI)"); break;
-       case DW_LANG_ObjC:              printf ("(Objective C)"); break;
-       case DW_LANG_ObjC_plus_plus:    printf ("(Objective C++)"); break;
-       case DW_LANG_UPC:               printf ("(Unified Parallel C)"); break;
-       case DW_LANG_D:                 printf ("(D)"); break;
-         /* DWARF 4 values.  */
-       case DW_LANG_Python:            printf ("(Python)"); break;
-         /* DWARF 5 values.  */
-       case DW_LANG_OpenCL:            printf ("(OpenCL)"); break;
-       case DW_LANG_Go:                printf ("(Go)"); break;
-       case DW_LANG_Modula3:           printf ("(Modula 3)"); break;
-       case DW_LANG_Haskell:           printf ("(Haskell)"); break;
-       case DW_LANG_C_plus_plus_03:    printf ("(C++03)"); break;
-       case DW_LANG_C_plus_plus_11:    printf ("(C++11)"); break;
-       case DW_LANG_OCaml:             printf ("(OCaml)"); break;
-       case DW_LANG_Rust:              printf ("(Rust)"); break;
-       case DW_LANG_C11:               printf ("(C11)"); break;
-       case DW_LANG_Swift:             printf ("(Swift)"); break;
-       case DW_LANG_Julia:             printf ("(Julia)"); break;
-       case DW_LANG_Dylan:             printf ("(Dylan)"); break;
-       case DW_LANG_C_plus_plus_14:    printf ("(C++14)"); break;
-       case DW_LANG_Fortran03:         printf ("(Fortran 03)"); break;
-       case DW_LANG_Fortran08:         printf ("(Fortran 08)"); break;
-       case DW_LANG_RenderScript:      printf ("(RenderScript)"); break;
-         /* MIPS extension.  */
-       case DW_LANG_Mips_Assembler:    printf ("(MIPS assembler)"); break;
-         /* UPC extension.  */
-       case DW_LANG_Upc:               printf ("(Unified Parallel C)"); break;
-       default:
-         if (uvalue >= DW_LANG_lo_user && uvalue <= DW_LANG_hi_user)
-           printf (_("(implementation defined: %#" PRIx64 ")"), uvalue);
-         else
-           printf (_("(unknown: %#" PRIx64 ")"), uvalue);
-         break;
-       }
+      printf ("\t(");
+      display_lang (uvalue);
+      printf (")");
       break;
 
     case DW_AT_encoding:
@@ -10786,29 +10752,31 @@ display_gdb_index (struct dwarf_section *section,
   unsigned char *start = section->start;
   uint32_t version;
   uint32_t cu_list_offset, tu_list_offset;
-  uint32_t address_table_offset, symbol_table_offset, constant_pool_offset;
+  uint32_t address_table_offset, symbol_table_offset, constant_pool_offset,
+    shortcut_table_offset;
   unsigned int cu_list_elements, tu_list_elements;
   unsigned int address_table_elements, symbol_table_slots;
   unsigned char *cu_list, *tu_list;
-  unsigned char *address_table, *symbol_table, *constant_pool;
+  unsigned char *address_table, *symbol_table, *shortcut_table, *constant_pool;
   unsigned int i;
 
   /* The documentation for the format of this file is in gdb/dwarf2read.c.  */
 
   introduce (section, false);
 
-  if (section->size < 6 * sizeof (uint32_t))
+  version = section->size < 4 ? 0 : byte_get_little_endian (start, 4);
+  size_t header_size = (version < 9 ? 6 : 7) * sizeof (uint32_t);
+  if (section->size < header_size)
     {
       warn (_("Truncated header in the %s section.\n"), section->name);
       return 0;
     }
 
-  version = byte_get_little_endian (start, 4);
   printf (_("Version %lu\n"), (unsigned long) version);
 
   /* Prior versions are obsolete, and future versions may not be
      backwards compatible.  */
-  if (version < 3 || version > 8)
+  if (version < 3 || version > 9)
     {
       warn (_("Unsupported version %lu.\n"), (unsigned long) version);
       return 0;
@@ -10830,17 +10798,23 @@ display_gdb_index (struct dwarf_section *section,
   tu_list_offset = byte_get_little_endian (start + 8, 4);
   address_table_offset = byte_get_little_endian (start + 12, 4);
   symbol_table_offset = byte_get_little_endian (start + 16, 4);
-  constant_pool_offset = byte_get_little_endian (start + 20, 4);
+  shortcut_table_offset = byte_get_little_endian (start + 20, 4);
+  if (version < 9)
+    constant_pool_offset = shortcut_table_offset;
+  else
+    constant_pool_offset = byte_get_little_endian (start + 24, 4);
 
   if (cu_list_offset > section->size
       || tu_list_offset > section->size
       || address_table_offset > section->size
       || symbol_table_offset > section->size
+      || shortcut_table_offset > section->size
       || constant_pool_offset > section->size
       || tu_list_offset < cu_list_offset
       || address_table_offset < tu_list_offset
       || symbol_table_offset < address_table_offset
-      || constant_pool_offset < symbol_table_offset)
+      || shortcut_table_offset < symbol_table_offset
+      || constant_pool_offset < shortcut_table_offset)
     {
       warn (_("Corrupt header in the %s section.\n"), section->name);
       return 0;
@@ -10849,12 +10823,13 @@ display_gdb_index (struct dwarf_section *section,
   cu_list_elements = (tu_list_offset - cu_list_offset) / 16;
   tu_list_elements = (address_table_offset - tu_list_offset) / 24;
   address_table_elements = (symbol_table_offset - address_table_offset) / 20;
-  symbol_table_slots = (constant_pool_offset - symbol_table_offset) / 8;
+  symbol_table_slots = (shortcut_table_offset - symbol_table_offset) / 8;
 
   cu_list = start + cu_list_offset;
   tu_list = start + tu_list_offset;
   address_table = start + address_table_offset;
   symbol_table = start + symbol_table_offset;
+  shortcut_table = start + shortcut_table_offset;
   constant_pool = start + constant_pool_offset;
 
   printf (_("\nCU table:\n"));
@@ -10966,6 +10941,33 @@ display_gdb_index (struct dwarf_section *section,
        }
     }
 
+  if (version >= 9)
+    {
+      printf (_("\nShortcut table:\n"));
+
+      if (shortcut_table_offset + 8 > constant_pool_offset)
+       {
+         warn (_("Corrupt shortcut table in the %s section.\n"), section->name);
+         return 0;
+       }
+
+      uint32_t lang = byte_get_little_endian (shortcut_table, 4);
+      printf (_("Language of main: "));
+      display_lang (lang);
+      printf ("\n");
+
+      uint32_t name_offset = byte_get_little_endian (shortcut_table + 4, 4);
+      printf (_("Name of main: "));
+      if (name_offset >= section->size - constant_pool_offset)
+       {
+         printf (_("<corrupt offset: %x>\n"), name_offset);
+         warn (_("Corrupt name offset of 0x%x found for name of main\n"),
+               name_offset);
+       }
+      else
+       printf ("%s\n", constant_pool + name_offset);
+    }
+
   return 1;
 }