DWARF: Read constant-class addresses correctly
[external/binutils.git] / gdb / dwarf2read.c
index 8c20f15..8a850f1 100644 (file)
@@ -1942,6 +1942,36 @@ byte_swap (offset_type value)
 #define MAYBE_SWAP(V) (V)
 #endif /* WORDS_BIGENDIAN */
 
+/* Read the given attribute value as an address, taking the attribute's
+   form into account.  */
+
+static CORE_ADDR
+attr_value_as_address (struct attribute *attr)
+{
+  CORE_ADDR addr;
+
+  if (attr->form != DW_FORM_addr && attr->form != DW_FORM_GNU_addr_index)
+    {
+      /* Aside from a few clearly defined exceptions, attributes that
+        contain an address must always be in DW_FORM_addr form.
+        Unfortunately, some compilers happen to be violating this
+        requirement by encoding addresses using other forms, such
+        as DW_FORM_data4 for example.  For those broken compilers,
+        we try to do our best, without any guarantee of success,
+        to interpret the address correctly.  It would also be nice
+        to generate a complaint, but that would require us to maintain
+        a list of legitimate cases where a non-address form is allowed,
+        as well as update callers to pass in at least the CU's DWARF
+        version.  This is more overhead than what we're willing to
+        expand for a pretty rare case.  */
+      addr = DW_UNSND (attr);
+    }
+  else
+    addr = DW_ADDR (attr);
+
+  return addr;
+}
+
 /* The suffix for an index file.  */
 #define INDEX_SUFFIX ".gdb-index"
 
@@ -4204,7 +4234,7 @@ dwarf2_find_base_address (struct die_info *die, struct dwarf2_cu *cu)
   attr = dwarf2_attr (die, DW_AT_entry_pc, cu);
   if (attr)
     {
-      cu->base_address = DW_ADDR (attr);
+      cu->base_address = attr_value_as_address (attr);
       cu->base_known = 1;
     }
   else
@@ -4212,7 +4242,7 @@ dwarf2_find_base_address (struct die_info *die, struct dwarf2_cu *cu)
       attr = dwarf2_attr (die, DW_AT_low_pc, cu);
       if (attr)
        {
-         cu->base_address = DW_ADDR (attr);
+         cu->base_address = attr_value_as_address (attr);
          cu->base_known = 1;
        }
     }
@@ -11260,7 +11290,7 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu)
                 die->offset.sect_off, objfile_name (objfile));
       return;
     }
-  pc = DW_ADDR (attr) + baseaddr;
+  pc = attr_value_as_address (attr) + baseaddr;
 
   if (cu->call_site_htab == NULL)
     cu->call_site_htab = htab_create_alloc_ex (16, core_addr_hash, core_addr_eq,
@@ -11701,12 +11731,10 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
       attr = dwarf2_attr (die, DW_AT_low_pc, cu);
       if (attr)
         {
-         low = DW_ADDR (attr);
-         if (attr_high->form == DW_FORM_addr
-             || attr_high->form == DW_FORM_GNU_addr_index)
-           high = DW_ADDR (attr_high);
-         else
-           high = low + DW_UNSND (attr_high);
+         low = attr_value_as_address (attr);
+         high = attr_value_as_address (attr_high);
+         if (cu->header.version >= 4 && attr_form_is_constant (attr_high))
+           high += low;
        }
       else
        /* Found high w/o low attribute.  */
@@ -11872,13 +11900,11 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
       attr = dwarf2_attr (die, DW_AT_low_pc, cu);
       if (attr)
         {
-          CORE_ADDR low = DW_ADDR (attr);
-         CORE_ADDR high;
-         if (attr_high->form == DW_FORM_addr
-             || attr_high->form == DW_FORM_GNU_addr_index)
-           high = DW_ADDR (attr_high);
-         else
-           high = low + DW_UNSND (attr_high);
+          CORE_ADDR low = attr_value_as_address (attr);
+         CORE_ADDR high = attr_value_as_address (attr_high);
+
+         if (cu->header.version >= 4 && attr_form_is_constant (attr_high))
+           high += low;
 
           record_block_range (block, baseaddr + low, baseaddr + high - 1);
         }
@@ -15336,18 +15362,13 @@ read_partial_die (const struct die_reader_specs *reader,
          break;
        case DW_AT_low_pc:
          has_low_pc_attr = 1;
-         part_die->lowpc = DW_ADDR (&attr);
+         part_die->lowpc = attr_value_as_address (&attr);
          break;
        case DW_AT_high_pc:
          has_high_pc_attr = 1;
-         if (attr.form == DW_FORM_addr
-             || attr.form == DW_FORM_GNU_addr_index)
-           part_die->highpc = DW_ADDR (&attr);
-         else
-           {
-             high_pc_relative = 1;
-             part_die->highpc = DW_UNSND (&attr);
-           }
+         part_die->highpc = attr_value_as_address (&attr);
+         if (cu->header.version >= 4 && attr_form_is_constant (&attr))
+               high_pc_relative = 1;
          break;
        case DW_AT_location:
           /* Support the .debug_loc offsets.  */
@@ -17560,9 +17581,8 @@ new_symbol_full (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
        case DW_TAG_label:
          attr = dwarf2_attr (die, DW_AT_low_pc, cu);
          if (attr)
-           {
-             SYMBOL_VALUE_ADDRESS (sym) = DW_ADDR (attr) + baseaddr;
-           }
+           SYMBOL_VALUE_ADDRESS (sym)
+             = attr_value_as_address (attr) + baseaddr;
          SYMBOL_TYPE (sym) = objfile_type (objfile)->builtin_core_addr;
          SYMBOL_DOMAIN (sym) = LABEL_DOMAIN;
          SYMBOL_ACLASS_INDEX (sym) = LOC_LABEL;