[readelf] Handle .gdb_index section version 9
[binutils-gdb.git] / binutils / dwarf.c
index 87ce1541d1ce6e960c9ad8aef536b14e93de13e5..2f4bb30ffd344fb816e2bcfc7c9b999d45ed0125 100644 (file)
@@ -67,9 +67,9 @@ static debug_info *debug_information = NULL;
    DWO object files.  We use these structures to record these links.  */
 typedef enum dwo_type
 {
- DWO_NAME,
- DWO_DIR,
- DWO_ID
 DWO_NAME,
 DWO_DIR,
 DWO_ID
 } dwo_type;
 
 typedef struct dwo_info
@@ -659,14 +659,13 @@ fetch_indexed_string (uint64_t idx,
     return (dwo ? _("<no .debug_str.dwo section>")
                : _("<no .debug_str section>"));
 
-  index_offset = idx * offset_size;
-
-  if (this_set != NULL)
-    index_offset += this_set->section_offsets [DW_SECT_STR_OFFSETS];
-
-  index_offset += str_offsets_base;
-
-  if (index_offset + offset_size > index_section->size)
+  if (_mul_overflow (idx, offset_size, &index_offset)
+      || (this_set != NULL
+         && ((index_offset += this_set->section_offsets [DW_SECT_STR_OFFSETS])
+             < this_set->section_offsets [DW_SECT_STR_OFFSETS]))
+      || (index_offset += str_offsets_base) < str_offsets_base
+      || index_offset + offset_size < offset_size
+      || index_offset + offset_size > index_section->size)
     {
       warn (_("string index of %" PRIu64 " converts to an offset of %#" PRIx64
              " which is too big for section %s"),
@@ -675,11 +674,6 @@ fetch_indexed_string (uint64_t idx,
       return _("<string index too big>");
     }
 
-  /* FIXME: If we are being paranoid then we should also check to see if
-     IDX references an entry beyond the end of the string table pointed to
-     by STR_OFFSETS_BASE.  (Since there can be more than one string table
-     in a DWARF string section).  */
-
   str_offset = byte_get (index_section->start + index_offset, offset_size);
 
   str_offset -= str_section->address;
@@ -722,15 +716,22 @@ 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 (eg DW_FORM_loclistx or DW_FORM_rnglistx).
-   Returns -1 if the value could not be found.  */
+/* This is for resolving DW_FORM_rnglistx and DW_FORM_loclistx.
+
+   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.
+
+   This returns the offset of the desired object relative to the section start or -1 upon failure.  */
 
 static uint64_t
-fetch_indexed_value (uint64_t idx,
-                    enum dwarf_section_display_enum sec_enum,
-                    uint64_t base_address)
+fetch_indexed_offset (uint64_t                         idx,
+                     enum dwarf_section_display_enum  sec_enum,
+                     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)
@@ -746,36 +747,14 @@ fetch_indexed_value (uint64_t idx,
       return -1;
     }
 
-  uint32_t pointer_size, bias;
-
-  if (byte_get (section->start, 4) == 0xffffffff)
+  if (offset_of_offset + offset_size >= section->size)
     {
-      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);
+      warn (_("Offset of %#" PRIx64 " is too big for section %s\n"),
+           offset_of_offset, section->name);
       return -1;
     }
-
-  return byte_get (section->start + offset, pointer_size);
+  
+  return base_address + byte_get (section->start + offset_of_offset, offset_size);
 }
 
 /* FIXME:  There are better and more efficient ways to handle
@@ -2363,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,
@@ -2727,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)
+             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.  */
@@ -2755,9 +2805,9 @@ read_and_display_attr_value (unsigned long attribute,
            {
              if (dwo)
                {
-                 idx = fetch_indexed_value (uvalue, rnglists_dwo, 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
                {
@@ -2765,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_value (uvalue, rnglists, base);
-                 if (idx != (uint64_t) -1)
-                   idx += base;
+                 idx = fetch_indexed_offset (uvalue, rnglists, base,
+                                             debug_info_p->offset_size);
                }
            }
          else
@@ -2827,22 +2874,9 @@ read_and_display_attr_value (unsigned long attribute,
          debug_info_p->loclists_base = uvalue;
          break;
 
-       case DW_AT_rnglists_base:
-         if (debug_info_p->rnglists_base)
-           warn (_("CU @ %#" PRIx64 " has multiple rnglists_base values "
-                   "(%#" PRIx64 " and %#" PRIx64 ")"),
-                 debug_info_p->cu_offset,
-                 debug_info_p->rnglists_base, uvalue);
-         svalue = uvalue;
-         if (svalue < 0)
-           {
-             warn (_("CU @ %#" PRIx64 " has has a negative rnglists_base "
-                     "value of %#" PRIx64 " - treating as zero"),
-                   debug_info_p->cu_offset, svalue);
-             uvalue = 0;
-           }
-         debug_info_p->rnglists_base = uvalue;
-         break;
+       case DW_AT_rnglists_base:
+         /* Assignment to debug_info_p->rnglists_base is now elsewhere.  */
+         break;
 
        case DW_AT_str_offsets_base:
          if (debug_info_p->str_offsets_base)
@@ -2905,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];
 
@@ -2948,12 +2984,21 @@ read_and_display_attr_value (unsigned long attribute,
 
        case DW_AT_low_pc:
          if (need_base_address)
-           debug_info_p->base_address = uvalue;
+           {
+             if (form == DW_FORM_addrx)
+               uvalue = fetch_indexed_addr (debug_info_p->addr_base
+                                            + uvalue * pointer_size,
+                                            pointer_size);
+
+             debug_info_p->base_address = uvalue;
+           }
          break;
 
        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.  */
          break;
 
        case DW_AT_GNU_ranges_base:
@@ -2980,7 +3025,9 @@ read_and_display_attr_value (unsigned long attribute,
                }
 
              if (form == DW_FORM_rnglistx)
-               uvalue = fetch_indexed_value (uvalue, rnglists, 0);
+               uvalue = fetch_indexed_offset (uvalue, rnglists,
+                                              debug_info_p->rnglists_base,
+                                              debug_info_p->offset_size);
 
              debug_info_p->range_lists [num] = uvalue;
              debug_info_p->num_range_lists++;
@@ -2993,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);
@@ -3004,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;
@@ -3127,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:
@@ -3564,7 +3561,163 @@ free_debug_information (debug_info *ent)
       free (ent->have_frame_base);
     }
   if (ent->max_range_lists)
-    free (ent->range_lists);
+    {
+      free (ent->range_lists);
+    }
+}
+
+/* For look-ahead in attributes.  When you want to scan a DIE for one specific
+   attribute and ignore the rest.  */
+
+static unsigned char *
+skip_attribute (unsigned long    form,
+               unsigned char *  data,
+               unsigned char *  end,
+               uint64_t         pointer_size,
+               uint64_t         offset_size,
+               int              dwarf_version)
+{
+  uint64_t temp;
+  int64_t stemp;
+
+  switch (form)
+    {
+    case DW_FORM_ref_addr:
+      data += dwarf_version == 2 ? pointer_size : offset_size;
+      break;
+    case DW_FORM_addr:
+      data += pointer_size;
+      break;
+    case DW_FORM_strp_sup:
+    case DW_FORM_strp:
+    case DW_FORM_line_strp:
+    case DW_FORM_sec_offset:
+    case DW_FORM_GNU_ref_alt:
+    case DW_FORM_GNU_strp_alt:
+      data += offset_size;
+      break;
+    case DW_FORM_ref1:
+    case DW_FORM_flag:
+    case DW_FORM_data1:
+    case DW_FORM_strx1:
+    case DW_FORM_addrx1:      
+      data += 1;
+      break;
+    case DW_FORM_ref2:
+    case DW_FORM_data2:
+    case DW_FORM_strx2:
+    case DW_FORM_addrx2:      
+      data += 2;
+      break;
+    case DW_FORM_strx3:
+    case DW_FORM_addrx3:      
+      data += 3;
+      break;
+    case DW_FORM_ref_sup4:
+    case DW_FORM_ref4:
+    case DW_FORM_data4:
+    case DW_FORM_strx4:
+    case DW_FORM_addrx4:
+      data += 4;
+      break;
+    case DW_FORM_ref_sup8:
+    case DW_FORM_ref8:
+    case DW_FORM_data8:
+    case DW_FORM_ref_sig8:
+      data += 8;
+      break;
+    case DW_FORM_data16:      
+      data += 16;
+      break;
+    case DW_FORM_sdata:
+      READ_SLEB (stemp, data, end);
+      break;
+    case DW_FORM_GNU_str_index:
+    case DW_FORM_strx:
+    case DW_FORM_ref_udata:
+    case DW_FORM_udata:
+    case DW_FORM_GNU_addr_index:
+    case DW_FORM_addrx:
+    case DW_FORM_loclistx:
+    case DW_FORM_rnglistx:
+      READ_ULEB (temp, data, end);
+      break;
+
+    case DW_FORM_indirect:
+      while (form == DW_FORM_indirect)
+        READ_ULEB (form, data, end);
+      return skip_attribute (form, data, end, pointer_size, offset_size, dwarf_version);
+
+    case DW_FORM_string:
+      data += strnlen ((char *) data, end - data);    
+      break;
+    case DW_FORM_block:
+    case DW_FORM_exprloc:
+      READ_ULEB (temp, data, end);
+      data += temp;
+      break;
+    case DW_FORM_block1:
+      SAFE_BYTE_GET_AND_INC (temp, data, 1, end);
+      data += temp;
+      break;
+    case DW_FORM_block2:
+      SAFE_BYTE_GET_AND_INC (temp, data, 2, end);
+      data += temp;
+      break;
+    case DW_FORM_block4:
+      SAFE_BYTE_GET_AND_INC (temp, data, 4, end);
+      data += temp;
+      break;
+    case DW_FORM_implicit_const:
+    case DW_FORM_flag_present:
+      break;
+    default:
+      warn (_("Unexpected form in top DIE\n"));
+      break;
+    }
+  return data;
+}
+
+static void
+read_bases (abbrev_entry *   entry,
+           unsigned char *  data,
+           unsigned char *  end,
+           int64_t          pointer_size,
+           uint64_t         offset_size,
+           int              dwarf_version,
+           debug_info *     debug_info_p)
+{
+  abbrev_attr *attr;
+
+  for (attr = entry->first_attr;
+       attr && attr->attribute;
+       attr = attr->next)
+    {
+      uint64_t uvalue;
+
+      if (attr->attribute == DW_AT_rnglists_base)
+       {
+         if (attr->form == DW_FORM_sec_offset)
+           {
+             SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
+             debug_info_p->rnglists_base = uvalue;
+           }
+         else
+           warn (_("Unexpected form of DW_AT_rnglists_base in the top DIE\n"));
+       }
+      else if(attr->attribute == DW_AT_addr_base || attr->attribute == DW_AT_GNU_addr_base)
+       {
+         if (attr->form == DW_FORM_sec_offset)
+           {
+             SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
+             debug_info_p->addr_base = uvalue;
+           }
+         else
+           warn (_("Unexpected form of DW_AT_addr_base in the top DIE\n"));
+       }
+      else
+       data = skip_attribute(attr->form, data, end, pointer_size, offset_size, dwarf_version);
+    }
 }
 
 /* Process the contents of a .debug_info section.
@@ -3996,7 +4149,11 @@ process_debug_info (struct dwarf_section * section,
                    }
                }
              if (dwarf_start_die != 0 && level < saved_level)
-               return true;
+               {
+                 if (list != NULL)
+                   free_abbrev_list (list);
+                 return true;
+               }
              continue;
            }
 
@@ -4037,6 +4194,8 @@ process_debug_info (struct dwarf_section * section,
                }
              warn (_("DIE at offset %#lx refers to abbreviation number %lu which does not exist\n"),
                    die_offset, abbrev_number);
+             if (list != NULL)
+               free_abbrev_list (list);
              return false;
            }
 
@@ -4069,6 +4228,34 @@ process_debug_info (struct dwarf_section * section,
                  || (debug_info_p->num_loc_offsets
                      == debug_info_p->num_loc_views));
 
+         /* Look ahead so that the values of DW_AT_rnglists_base, DW_AT_[GNU_]addr_base
+            are available before attributes that reference them are parsed in the same DIE.
+            Only needed for the top DIE on DWARFv5+.
+            No simiar treatment for loclists_base because there should be no loclist
+            attributes in top DIE.  */
+         if (compunit.cu_version >= 5 && level == 0)
+           {
+             int64_t stemp;
+
+             read_bases (entry,
+                         tags,
+                         start,
+                         compunit.cu_pointer_size,
+                         offset_size,
+                         compunit.cu_version,
+                         debug_info_p);
+
+             /* This check was in place before, keep it.  */
+             stemp = debug_info_p->rnglists_base;
+             if (stemp < 0)
+               {
+                 warn (_("CU @ %#" PRIx64 " has has a negative rnglists_base "
+                         "value of %#" PRIx64 " - treating as zero"),
+                       debug_info_p->cu_offset, stemp);
+                 debug_info_p->rnglists_base = 0;
+               }
+           }
+
          for (attr = entry->first_attr;
               attr && attr->attribute;
               attr = attr->next)
@@ -4319,10 +4506,10 @@ display_formatted_table (unsigned char *data,
       printf (_("\n The %s is empty.\n"), table_name);
       return data;
     }
-  else if (data >= end)
+  else if (data >= end
+          || data_count > (size_t) (end - data))
     {
-      warn (_("%s: Corrupt entry count - expected %#" PRIx64
-             " but none found\n"), table_name, data_count);
+      warn (_("%s: Corrupt entry count %#" PRIx64 "\n"), table_name, data_count);
       return data;
     }
 
@@ -4676,7 +4863,7 @@ display_debug_lines_raw (struct dwarf_section *  section,
          while (data < end_of_sequence)
            {
              unsigned char op_code;
-             int64_t adv;
+             int adv;
              uint64_t uladv;
 
              printf ("  [0x%08tx]", data - start);
@@ -4723,7 +4910,7 @@ display_debug_lines_raw (struct dwarf_section *  section,
                    }
                  adv = (op_code % linfo.li_line_range) + linfo.li_line_base;
                  state_machine_regs.line += adv;
-                 printf (_(" and Line by %" PRId64 " to %d"),
+                 printf (_(" and Line by %d to %d"),
                          adv, state_machine_regs.line);
                  if (verbose_view || state_machine_regs.view)
                    printf (_(" (view %u)\n"), state_machine_regs.view);
@@ -4788,7 +4975,7 @@ display_debug_lines_raw (struct dwarf_section *  section,
                  case DW_LNS_advance_line:
                    READ_SLEB (adv, data, end);
                    state_machine_regs.line += adv;
-                   printf (_("  Advance Line by %" PRId64 " to %d\n"),
+                   printf (_("  Advance Line by %d to %d\n"),
                            adv, state_machine_regs.line);
                    break;
 
@@ -4808,7 +4995,7 @@ display_debug_lines_raw (struct dwarf_section *  section,
                  case DW_LNS_negate_stmt:
                    adv = state_machine_regs.is_stmt;
                    adv = ! adv;
-                   printf (_("  Set is_stmt to %" PRId64 "\n"), adv);
+                   printf (_("  Set is_stmt to %d\n"), adv);
                    state_machine_regs.is_stmt = adv;
                    break;
 
@@ -8033,15 +8220,9 @@ display_debug_rnglists_list (unsigned char * start,
                             unsigned int    pointer_size,
                             uint64_t        offset,
                             uint64_t        base_address,
-                            unsigned int    offset_size)
+                            uint64_t        addr_base)
 {
   unsigned char *next = start;
-  unsigned int debug_addr_section_hdr_len;
-
-  if (offset_size == 4)
-    debug_addr_section_hdr_len = 8;
-  else
-    debug_addr_section_hdr_len = 16;
 
   while (1)
     {
@@ -8071,24 +8252,24 @@ display_debug_rnglists_list (unsigned char * start,
          READ_ULEB (base_address, start, finish);
          print_hex (base_address, pointer_size);
          printf (_("(base address index) "));
-         base_address = fetch_indexed_addr ((base_address * pointer_size)
-                                            + debug_addr_section_hdr_len, pointer_size);
+         base_address = fetch_indexed_addr ((base_address * pointer_size) + addr_base,
+                                            pointer_size);
          print_hex (base_address, pointer_size);
          printf (_("(base address)\n"));
          break;
        case DW_RLE_startx_endx:
          READ_ULEB (begin, start, finish);
          READ_ULEB (end, start, finish);
-         begin = fetch_indexed_addr ((begin * pointer_size)
-                                     + debug_addr_section_hdr_len, pointer_size);
-         end   = fetch_indexed_addr ((begin * pointer_size)
-                                     + debug_addr_section_hdr_len, pointer_size);
+         begin = fetch_indexed_addr ((begin * pointer_size) + addr_base,
+                                     pointer_size);
+         end   = fetch_indexed_addr ((begin * pointer_size) + addr_base,
+                                     pointer_size);
          break;
        case DW_RLE_startx_length:
          READ_ULEB (begin, start, finish);
          READ_ULEB (length, start, finish);
-         begin = fetch_indexed_addr ((begin * pointer_size)
-                                     + debug_addr_section_hdr_len, pointer_size);
+         begin = fetch_indexed_addr ((begin * pointer_size) + addr_base,
+                                     pointer_size);
          end = begin + length;
          break;
        case DW_RLE_offset_pair:
@@ -8142,126 +8323,110 @@ display_debug_rnglists_list (unsigned char * start,
 }
 
 static int
-display_debug_rnglists (struct dwarf_section *section)
-{
-  unsigned char *start = section->start;
-  unsigned char *finish = start + section->size;
+display_debug_rnglists_unit_header (struct dwarf_section *  section,
+                                   uint64_t *              unit_offset,
+                                   unsigned char *         poffset_size)
+{
+  uint64_t        start_offset = *unit_offset;
+  unsigned char * p = section->start + start_offset;
+  unsigned char * finish = section->start + section->size;
+  uint64_t        initial_length;
+  unsigned char   segment_selector_size;
+  unsigned int    offset_entry_count;
+  unsigned int    i;
+  unsigned short  version;
+  unsigned char   address_size = 0;
+  unsigned char   offset_size;
 
-  while (start < finish)
-    {
-      unsigned char *table_start;
-      uint64_t offset = start - section->start;
-      unsigned char *end;
-      uint64_t initial_length;
-      unsigned char segment_selector_size;
-      unsigned int offset_entry_count;
-      unsigned int i;
-      unsigned short version;
-      unsigned char address_size = 0;
-      unsigned char offset_size;
+  /* Get and check the length of the block.  */
+  SAFE_BYTE_GET_AND_INC (initial_length, p, 4, finish);
 
-      /* Get and check the length of the block.  */
-      SAFE_BYTE_GET_AND_INC (initial_length, start, 4, finish);
+  if (initial_length == 0xffffffff)
+    {
+      /* This section is 64-bit DWARF 3.  */
+      SAFE_BYTE_GET_AND_INC (initial_length, p, 8, finish);
+      *poffset_size = offset_size = 8;
+    }
+  else
+    *poffset_size = offset_size = 4;
 
-      if (initial_length == 0xffffffff)
-       {
-         /* This section is 64-bit DWARF 3.  */
-         SAFE_BYTE_GET_AND_INC (initial_length, start, 8, finish);
-         offset_size = 8;
-       }
+  if (initial_length > (size_t) (finish - p))
+    {
+      /* If the length field has a relocation against it, then we should
+        not complain if it is inaccurate (and probably negative).
+        It is copied from .debug_line handling code.  */
+      if (reloc_at (section, (p - section->start) - offset_size))
+       initial_length = finish - p;
       else
-       offset_size = 4;
-
-      if (initial_length > (size_t) (finish - start))
-       {
-         /* If the length field has a relocation against it, then we should
-            not complain if it is inaccurate (and probably negative).
-            It is copied from .debug_line handling code.  */
-         if (reloc_at (section, (start - section->start) - offset_size))
-           initial_length = finish - start;
-         else
-           {
-             warn (_("The length field (%#" PRIx64
-                     ") in the debug_rnglists header is wrong"
-                     " - the section is too small\n"),
-                   initial_length);
-             return 0;
-           }
-       }
-
-      end = start + initial_length;
-
-      /* Get the other fields in the header.  */
-      SAFE_BYTE_GET_AND_INC (version, start, 2, finish);
-      SAFE_BYTE_GET_AND_INC (address_size, start, 1, finish);
-      SAFE_BYTE_GET_AND_INC (segment_selector_size, start, 1, finish);
-      SAFE_BYTE_GET_AND_INC (offset_entry_count, start, 4, finish);
-
-      printf (_(" Table at Offset: %#" PRIx64 ":\n"), offset);
-      printf (_("  Length:          %#" PRIx64 "\n"), initial_length);
-      printf (_("  DWARF version:   %u\n"), version);
-      printf (_("  Address size:    %u\n"), address_size);
-      printf (_("  Segment size:    %u\n"), segment_selector_size);
-      printf (_("  Offset entries:  %u\n"), offset_entry_count);
-
-      /* Check the fields.  */
-      if (segment_selector_size != 0)
        {
-         warn (_("The %s section contains "
-                 "unsupported segment selector size: %d.\n"),
-               section->name, segment_selector_size);
+         warn (_("The length field (%#" PRIx64
+                 ") in the debug_rnglists header is wrong"
+                 " - the section is too small\n"),
+               initial_length);
          return 0;
        }
+    }
 
-      if (version < 5)
-       {
-         warn (_("Only DWARF version 5+ debug_rnglists info "
-                 "is currently supported.\n"));
-         return 0;
-       }
+  /* Report the next unit offset to the caller.  */
+  *unit_offset = (p - section->start) + initial_length;
 
-      table_start = start;
+  /* Get the other fields in the header.  */
+  SAFE_BYTE_GET_AND_INC (version, p, 2, finish);
+  SAFE_BYTE_GET_AND_INC (address_size, p, 1, finish);
+  SAFE_BYTE_GET_AND_INC (segment_selector_size, p, 1, finish);
+  SAFE_BYTE_GET_AND_INC (offset_entry_count, p, 4, finish);
 
-      if (offset_entry_count != 0)
-       {
-         printf (_("\n   Offsets starting at %#tx:\n"),
-                 start - section->start);
+  printf (_(" Table at Offset: %#" PRIx64 ":\n"), start_offset);
+  printf (_("  Length:          %#" PRIx64 "\n"), initial_length);
+  printf (_("  DWARF version:   %u\n"), version);
+  printf (_("  Address size:    %u\n"), address_size);
+  printf (_("  Segment size:    %u\n"), segment_selector_size);
+  printf (_("  Offset entries:  %u\n"), offset_entry_count);
 
-         for (i = 0; i < offset_entry_count; i++)
-           {
-             uint64_t entry;
+  /* Check the fields.  */
+  if (segment_selector_size != 0)
+    {
+      warn (_("The %s section contains "
+             "unsupported segment selector size: %d.\n"),
+           section->name, segment_selector_size);
+      return 0;
+    }
 
-             SAFE_BYTE_GET_AND_INC (entry, start, offset_size, finish);
-             printf (_("    [%6u] %#" PRIx64 "\n"), i, entry);
-           }
-       }
-      else
-       offset_entry_count = 1;
+  if (version < 5)
+    {
+      warn (_("Only DWARF version 5+ debug_rnglists info "
+             "is currently supported.\n"));
+      return 0;
+    }
+
+  if (offset_entry_count != 0)
+    {
+      printf (_("\n   Offsets starting at %#tx:\n"), p - section->start);
 
       for (i = 0; i < offset_entry_count; i++)
        {
-         uint64_t indx = start - table_start;
+         uint64_t entry;
 
-         offset = start - section->start;
-         printf (_("\n  Offset: %#" PRIx64 ", Index: %#" PRIx64 "\n"),
-                 offset, indx);
-         printf (_("    Offset   Begin    End\n"));
-         start = display_debug_rnglists_list
-           (start, end, address_size, offset, 0, offset_size);
-         if (start >= end)
-           break;
+         SAFE_BYTE_GET_AND_INC (entry, p, offset_size, finish);
+         printf (_("    [%6u] %#" PRIx64 "\n"), i, entry);
        }
-
-      start = end;
-
-      if (start < finish)
-       putchar ('\n');
     }
 
-  putchar ('\n');
   return 1;
 }
 
+static bool
+is_range_list_for_this_section (bool is_rnglists, unsigned int version)
+{
+  if (is_rnglists && version > 4)
+    return true;
+
+  if (! is_rnglists && version < 5)
+    return true;
+
+  return false;
+}
+
 static int
 display_debug_ranges (struct dwarf_section *section,
                      void *file ATTRIBUTE_UNUSED)
@@ -8274,10 +8439,10 @@ display_debug_ranges (struct dwarf_section *section,
   unsigned int num_range_list, i;
   struct range_entry *range_entries;
   struct range_entry *range_entry_fill;
-  int is_rnglists = strstr (section->name, "debug_rnglists") != NULL;
-  /* Initialize it due to a false compiler warning.  */
-  unsigned char address_size = 0;
+  bool is_rnglists = strstr (section->name, "debug_rnglists") != NULL;
   uint64_t last_offset = 0;
+  uint64_t next_rnglists_cu_offset = 0;
+  unsigned char offset_size;
 
   if (bytes == 0)
     {
@@ -8287,9 +8452,6 @@ display_debug_ranges (struct dwarf_section *section,
 
   introduce (section, false);
   
-  if (is_rnglists)
-    return display_debug_rnglists (section);
-
   if (load_debug_info (file) == 0)
     {
       warn (_("Unable to load/parse the .debug_info section, so cannot interpret the %s section.\n"),
@@ -8299,19 +8461,18 @@ display_debug_ranges (struct dwarf_section *section,
 
   num_range_list = 0;
   for (i = 0; i < num_debug_info_entries; i++)
-    num_range_list += debug_information [i].num_range_lists;
+    if (is_range_list_for_this_section (is_rnglists, debug_information [i].dwarf_version))
+      num_range_list += debug_information [i].num_range_lists;
 
   if (num_range_list == 0)
     {
       /* This can happen when the file was compiled with -gsplit-debug
         which removes references to range lists from the primary .o file.  */
-      printf (_("No range lists in .debug_info section.\n"));
+      printf (_("No range lists referenced by .debug_info section.\n"));
       return 1;
     }
 
-  range_entries = (struct range_entry *)
-      xmalloc (sizeof (*range_entries) * num_range_list);
-  range_entry_fill = range_entries;
+  range_entry_fill = range_entries = XNEWVEC (struct range_entry, num_range_list);
 
   for (i = 0; i < num_debug_info_entries; i++)
     {
@@ -8320,12 +8481,18 @@ display_debug_ranges (struct dwarf_section *section,
 
       for (j = 0; j < debug_info_p->num_range_lists; j++)
        {
-         range_entry_fill->ranges_offset = debug_info_p->range_lists[j];
-         range_entry_fill->debug_info_p = debug_info_p;
-         range_entry_fill++;
+         if (is_range_list_for_this_section (is_rnglists, debug_info_p->dwarf_version))
+           {
+             range_entry_fill->ranges_offset = debug_info_p->range_lists[j];
+             range_entry_fill->debug_info_p = debug_info_p;
+             range_entry_fill++;
+           }
        }
     }
 
+  assert (range_entry_fill >= range_entries);
+  assert (num_range_list >= (unsigned int)(range_entry_fill - range_entries));
+  num_range_list = range_entry_fill - range_entries;
   qsort (range_entries, num_range_list, sizeof (*range_entries),
         range_entry_compar);
 
@@ -8334,7 +8501,8 @@ display_debug_ranges (struct dwarf_section *section,
          section->name, range_entries[0].ranges_offset);
 
   putchar ('\n');
-  printf (_("    Offset   Begin    End\n"));
+  if (!is_rnglists)
+    printf (_("    Offset   Begin    End\n"));
 
   for (i = 0; i < num_range_list; i++)
     {
@@ -8345,7 +8513,7 @@ display_debug_ranges (struct dwarf_section *section,
       unsigned char *next;
       uint64_t base_address;
 
-      pointer_size = (is_rnglists ? address_size : debug_info_p->pointer_size);
+      pointer_size = debug_info_p->pointer_size;
       offset = range_entry->ranges_offset;
       base_address = debug_info_p->base_address;
 
@@ -8364,7 +8532,15 @@ display_debug_ranges (struct dwarf_section *section,
          continue;
        }
 
-      next = section_begin + offset + debug_info_p->rnglists_base;
+      /* If we've moved on to the next compile unit in the rnglists section - dump the unit header(s).  */
+      if (is_rnglists && next_rnglists_cu_offset < offset)
+       {
+         while (next_rnglists_cu_offset < offset)
+           display_debug_rnglists_unit_header (section, &next_rnglists_cu_offset, &offset_size);
+         printf (_("    Offset   Begin    End\n"));
+       }
+
+      next = section_begin + offset; /* Offset is from the section start, the base has already been added.  */
       
       /* If multiple DWARF entities reference the same range then we will
         have multiple entries in the `range_entries' list for the same
@@ -8392,9 +8568,19 @@ display_debug_ranges (struct dwarf_section *section,
       start = next;
       last_start = next;
 
-      display_debug_ranges_list
-       (start, finish, pointer_size, offset, base_address);
+      if (is_rnglists)
+        display_debug_rnglists_list
+         (start, finish, pointer_size, offset, base_address, debug_info_p->addr_base);
+      else
+        display_debug_ranges_list
+         (start, finish, pointer_size, offset, base_address);
     }
+
+  /* Display trailing empty (or unreferenced) compile units, if any.  */
+  if (is_rnglists)
+    while (next_rnglists_cu_offset < section->size)
+      display_debug_rnglists_unit_header (section, &next_rnglists_cu_offset, &offset_size);
+
   putchar ('\n');
 
   free (range_entries);
@@ -9829,12 +10015,14 @@ display_debug_frames (struct dwarf_section *section,
                    {
                      warn (_("Invalid column number in saved frame state\n"));
                      fc->ncols = 0;
-                     break;
                    }
-                 memcpy (fc->col_type, rs->col_type,
-                         rs->ncols * sizeof (*rs->col_type));
-                 memcpy (fc->col_offset, rs->col_offset,
-                         rs->ncols * sizeof (*rs->col_offset));
+                 else
+                   {
+                     memcpy (fc->col_type, rs->col_type,
+                             rs->ncols * sizeof (*rs->col_type));
+                     memcpy (fc->col_offset, rs->col_offset,
+                             rs->ncols * sizeof (*rs->col_offset));
+                   }
                  free (rs->col_type);
                  free (rs->col_offset);
                  free (rs);
@@ -10564,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;
@@ -10608,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;
@@ -10627,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"));
@@ -10744,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;
 }