From c7ffb795ba446b21809e7e5bd503968a2c103555 Mon Sep 17 00:00:00 2001
From: rupothar <rupesh.potharla@amd.com>
Date: Wed, 30 Mar 2022 15:38:19 +0530
Subject: [PATCH]  bfd/dwarf2: Add Support for DW_FORM_strx* and
 DW_FORM_addrx*.

 Made code changes implementing strx* and addrx* forms to the BFD library.

Change-Id: I441dcd47d2b4a0fd152a0fb0df511cf994cee3c2
---
 bfd/dwarf2.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 280 insertions(+), 22 deletions(-)

diff --git a/bfd/dwarf2.c b/bfd/dwarf2.c
index 9aa4e955a5e..f19df9c16c1 100644
--- a/bfd/dwarf2.c
+++ b/bfd/dwarf2.c
@@ -118,6 +118,18 @@ struct dwarf2_debug_file
   /* Length of the loaded .debug_str section.  */
   bfd_size_type dwarf_str_size;
 
+  /* Pointer to the .debug_str_offsets section loaded into memory.  */
+  bfd_byte *dwarf_str_offsets_buffer;
+
+  /* Length of the loaded .debug_str_offsets section.  */
+  bfd_size_type dwarf_str_offsets_size;
+
+  /* Pointer to the .debug_addr section loaded into memory.  */
+  bfd_byte *dwarf_addr_buffer;
+
+  /* Length of the loaded .debug_addr section.  */
+  bfd_size_type dwarf_addr_size;
+
   /* Pointer to the .debug_line_str section loaded into memory.  */
   bfd_byte *dwarf_line_str_buffer;
 
@@ -296,6 +308,12 @@ struct comp_unit
 
   /* TRUE if symbols are cached in hash table for faster lookup by name.  */
   bool cached;
+
+ /* Base address of debug_addr section.  */
+  unsigned long dwarf_addr_offset;
+
+  /* Base address of string offset table.  */
+  unsigned long dwarf_str_offset;
 };
 
 /* This data structure holds the information of an abbrev.  */
@@ -338,6 +356,8 @@ const struct dwarf_debug_section dwarf_debug_sections[] =
   { ".debug_static_vars",	".zdebug_static_vars" },
   { ".debug_str",		".zdebug_str", },
   { ".debug_str",		".zdebug_str", },
+  { ".debug_str_offsets",       ".zdebug_str_offsets", },
+  { ".debug_addr",              ".zdebug_addr", },
   { ".debug_line_str",		".zdebug_line_str", },
   { ".debug_types",		".zdebug_types" },
   /* GNU DWARF 1 extensions */
@@ -372,6 +392,8 @@ enum dwarf_debug_section_enum
   debug_static_vars,
   debug_str,
   debug_str_alt,
+  debug_str_offsets,
+  debug_addr,
   debug_line_str,
   debug_types,
   debug_sfnames,
@@ -1221,12 +1243,92 @@ is_int_form (const struct attribute *attr)
     }
 }
 
+/* Returns true if the form is strx[1-4].  */
+
+static inline bool
+is_strx_form(enum dwarf_form form)
+{
+  return (form == DW_FORM_strx
+          || form == DW_FORM_strx1
+          || form == DW_FORM_strx2
+          || form == DW_FORM_strx3
+          || form == DW_FORM_strx4);
+}
+
+/* Return true if the form is addrx[1-4].  */
+
+static inline bool
+is_addrx_form(enum dwarf_form form)
+{
+  return (form == DW_FORM_addrx
+          || form == DW_FORM_addrx1
+          || form == DW_FORM_addrx2
+          || form == DW_FORM_addrx3
+          || form == DW_FORM_addrx4);
+
+}
+
+/* Function to handle DW_FORM_addrx*.  */
+static int
+read_indexed_address (struct comp_unit *unit,
+                      unsigned long idx)
+{
+  struct dwarf2_debug *stash = unit->stash;
+  struct dwarf2_debug_file *file = unit->file;
+  unsigned long addr_base = unit->dwarf_addr_offset;
+  bfd_byte *info_ptr;
+
+  if (stash == NULL)
+      return 0;
+
+   if (! read_section (unit->abfd, &stash->debug_sections[debug_addr],
+                       file->syms, 0,
+                       &file->dwarf_addr_buffer, &file->dwarf_addr_size))
+      return 0;
+
+   info_ptr = (bfd_byte *)(file->dwarf_addr_buffer + addr_base
+              + idx * unit->offset_size);
+
+  if (unit->offset_size == 4)
+     return bfd_get_32 (unit->abfd, info_ptr);
+  else
+     return bfd_get_64 (unit->abfd, info_ptr);
+}
+
+/* Return the string for DW_FORM_strx*.  */
 static const char *
-read_indexed_string (bfd_uint64_t idx ATTRIBUTE_UNUSED,
-		     struct comp_unit * unit ATTRIBUTE_UNUSED)
+read_indexed_string (struct comp_unit * unit,
+                     unsigned long idx)
 {
-  /* FIXME: Add support for indexed strings.  */
-  return "<indexed strings not yet supported>";
+  struct dwarf2_debug *stash = unit->stash;
+  struct dwarf2_debug_file *file = unit->file;
+  bfd_byte *info_ptr;
+  unsigned long str_offset;
+
+   if (stash == NULL)
+       return NULL;
+
+  if (! read_section (unit->abfd, &stash->debug_sections[debug_str],
+                      file->syms, 0,
+                      &file->dwarf_str_buffer, &file->dwarf_str_size))
+  return NULL;
+
+  if (! read_section (unit->abfd, &stash->debug_sections[debug_str_offsets],
+                      file->syms, 0,
+                      &file->dwarf_str_offsets_buffer,
+                      &file->dwarf_str_offsets_size))
+  return NULL;
+
+  info_ptr = (bfd_byte *)(file->dwarf_str_offsets_buffer
+             +  unit->dwarf_str_offset
+             + idx * unit->offset_size);
+
+  if (unit->offset_size == 4)
+     str_offset = bfd_get_32 (unit->abfd, info_ptr);
+  else
+     str_offset = bfd_get_64 (unit->abfd, info_ptr);
+
+  return (const char*) (file->dwarf_str_buffer + str_offset);
 }
 
 /* Read and fill in the value of attribute ATTR as described by FORM.
@@ -1330,24 +1432,19 @@ read_attribute_value (struct attribute *  attr,
       break;
     case DW_FORM_strx1:
       attr->u.val = read_1_byte (abfd, &info_ptr, info_ptr_end);
-      attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
       break;
     case DW_FORM_strx2:
       attr->u.val = read_2_bytes (abfd, &info_ptr, info_ptr_end);
-      attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
       break;
     case DW_FORM_strx3:
       attr->u.val = read_3_bytes (abfd, &info_ptr, info_ptr_end);
-      attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
       break;
     case DW_FORM_strx4:
       attr->u.val = read_4_bytes (abfd, &info_ptr, info_ptr_end);
-      attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
       break;
     case DW_FORM_strx:
       attr->u.val = _bfd_safe_read_leb128 (abfd, &info_ptr,
 					   false, info_ptr_end);
-      attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
       break;
     case DW_FORM_exprloc:
     case DW_FORM_block:
@@ -1486,6 +1583,7 @@ struct line_info_table
   unsigned int		num_files;
   unsigned int		num_dirs;
   unsigned int		num_sequences;
+  unsigned int          version;
   char *		comp_dir;
   char **		dirs;
   struct fileinfo*	files;
@@ -2115,6 +2213,23 @@ read_formatted_entries (struct comp_unit *unit, bfd_byte **bufp,
 	    return false;
 	  switch (form)
 	    {
+	    case DW_FORM_strx:
+	    case DW_FORM_strx1:
+	    case DW_FORM_strx2:
+	    case DW_FORM_strx3:
+	    case DW_FORM_strx4:
+	      attr.u.str = (char*) read_indexed_string(unit, attr.u.val);
+	      *stringp = attr.u.str;
+	      break;
+
+	    case DW_FORM_addrx:
+	    case DW_FORM_addrx1:
+	    case DW_FORM_addrx2:
+	    case DW_FORM_addrx3:
+	    case DW_FORM_addrx4:
+	      attr.u.val = read_indexed_address(unit, attr.u.val);
+	      break;
+
 	    case DW_FORM_string:
 	    case DW_FORM_line_strp:
 	      *stringp = attr.u.str;
@@ -2134,10 +2249,8 @@ read_formatted_entries (struct comp_unit *unit, bfd_byte **bufp,
 	    }
 	}
 
-      /* Skip the first "zero entry", which is the compilation dir/file.  */
-      if (datai != 0)
-	if (!callback (table, fe.name, fe.dir, fe.time, fe.size))
-	  return false;
+      if (!callback (table, fe.name, fe.dir, fe.time, fe.size))
+	return false;
     }
 
   *bufp = buf;
@@ -2302,6 +2415,7 @@ decode_line_info (struct comp_unit *unit)
   table->sequences = NULL;
 
   table->lcl_head = NULL;
+  table->version = lh.version;
 
   if (lh.version >= 5)
     {
@@ -2484,6 +2598,8 @@ decode_line_info (struct comp_unit *unit)
 		filenum = _bfd_safe_read_leb128 (abfd, &line_ptr,
 						 false, line_end);
 		free (filename);
+		if (table->version >= 5)
+		  filenum = filenum + 1;
 		filename = concat_filename (table, filenum);
 		break;
 	      }
@@ -3081,6 +3197,11 @@ find_abstract_instance (struct comp_unit *unit,
 	    {
 	      info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit,
 					 info_ptr, info_ptr_end);
+	      if (is_strx_form(attr.form))
+		attr.u.str = (char*) read_indexed_string(unit, attr.u.val);
+	      if (is_addrx_form(attr.form))
+		attr.u.val = read_indexed_address(unit, attr.u.val);
+
 	      if (info_ptr == NULL)
 		break;
 	      switch (attr.name)
@@ -3116,8 +3237,12 @@ find_abstract_instance (struct comp_unit *unit,
 		  if (!comp_unit_maybe_decode_line_info (unit))
 		    return false;
 		  if (is_int_form (&attr))
-		    *filename_ptr = concat_filename (unit->line_table,
-						     attr.u.val);
+		    {
+		      if (unit->line_table->version >=5)
+			attr.u.val = attr.u.val + 1;
+		      *filename_ptr = concat_filename (unit->line_table,
+						       attr.u.val);
+		    }
 		  break;
 		case DW_AT_decl_line:
 		  if (is_int_form (&attr))
@@ -3453,6 +3578,10 @@ scan_unit_for_symbols (struct comp_unit *unit)
 				     unit, info_ptr, info_ptr_end);
 	  if (info_ptr == NULL)
 	    goto fail;
+	  if (is_strx_form(attr.form))
+	    attr.u.str = (char*) read_indexed_string(unit, attr.u.val);
+	  if (is_addrx_form(attr.form))
+	    attr.u.val = read_indexed_address(unit, attr.u.val);
 	}
 
       if (abbrev->has_children)
@@ -3554,6 +3683,10 @@ scan_unit_for_symbols (struct comp_unit *unit)
 				     unit, info_ptr, info_ptr_end);
 	  if (info_ptr == NULL)
 	    goto fail;
+	  if (is_strx_form(attr.form))
+	    attr.u.str = (char*) read_indexed_string(unit, attr.u.val);
+	  if (is_addrx_form(attr.form))
+	    attr.u.val = read_indexed_address(unit, attr.u.val);
 
 	  if (func)
 	    {
@@ -3561,8 +3694,12 @@ scan_unit_for_symbols (struct comp_unit *unit)
 		{
 		case DW_AT_call_file:
 		  if (is_int_form (&attr))
-		    func->caller_file = concat_filename (unit->line_table,
-							 attr.u.val);
+		  {
+		  if (unit->line_table->version >= 5)
+		    attr.u.val = attr.u.val + 1;
+		  func->caller_file = concat_filename (unit->line_table,
+						       attr.u.val);
+		  }
 		  break;
 
 		case DW_AT_call_line:
@@ -3624,8 +3761,12 @@ scan_unit_for_symbols (struct comp_unit *unit)
 
 		case DW_AT_decl_file:
 		  if (is_int_form (&attr))
-		    func->file = concat_filename (unit->line_table,
-						  attr.u.val);
+		    {
+		      if (unit->line_table->version >= 5)
+		        attr.u.val = attr.u.val + 1;
+		      func->file = concat_filename (unit->line_table,
+						    attr.u.val);
+		    }
 		  break;
 
 		case DW_AT_decl_line:
@@ -3675,8 +3816,12 @@ scan_unit_for_symbols (struct comp_unit *unit)
 
 		case DW_AT_decl_file:
 		  if (is_int_form (&attr))
-		    var->file = concat_filename (unit->line_table,
-						 attr.u.val);
+		    {
+		      if (unit->line_table->version >=5)
+		        attr.u.val = attr.u.val + 1;
+		      var->file = concat_filename (unit->line_table,
+					           attr.u.val);
+		    }
 		  break;
 
 		case DW_AT_decl_line:
@@ -3750,6 +3895,78 @@ scan_unit_for_symbols (struct comp_unit *unit)
   return false;
 }
 
+/* read the attributes of the form strx and addrx.  */
+static void reread_attribute (
+                struct comp_unit *    unit,
+                struct attribute attr,
+                bfd_vma *low_pc,
+                bfd_vma *high_pc,
+                bool *high_pc_relative,
+		bool compunit)
+{
+
+  if (is_strx_form(attr.form))
+    attr.u.str = (char*) read_indexed_string(unit, attr.u.val);
+  if (is_addrx_form(attr.form))
+    attr.u.val = read_indexed_address(unit, attr.u.val);
+
+  switch (attr.name)
+  {
+    case DW_AT_stmt_list:
+      unit->stmtlist = 1;
+      unit->line_offset = attr.u.val;
+      break;
+
+    case DW_AT_name:
+      if (is_str_form (&attr))
+        unit->name = attr.u.str;
+      break;
+
+    case DW_AT_low_pc:
+      *low_pc = attr.u.val;
+      if (compunit)
+        unit->base_address = *low_pc;
+      break;
+
+    case DW_AT_high_pc:
+      *high_pc = attr.u.val;
+      *high_pc_relative = attr.form != DW_FORM_addr;
+      break;
+
+    case DW_AT_ranges:
+      if (!read_rangelist (unit, &unit->arange, attr.u.val))
+        return;
+      break;
+
+    case DW_AT_comp_dir:
+    {
+      char *comp_dir = attr.u.str;
+
+      if (! is_str_form (&attr))
+      {
+         _bfd_error_handler
+          (_("DWARF error: DW_AT_comp_dir attribute encountered with a non-string form"));
+         comp_dir = NULL;
+      }
+
+      if (comp_dir)
+      {
+        char *cp = strchr (comp_dir, ':');
+
+        if (cp && cp != comp_dir && cp[-1] == '.' && cp[1] == '/')
+          comp_dir = cp + 1;
+      }
+        unit->comp_dir = comp_dir;
+        break;
+    }
+
+      case DW_AT_language:
+        unit->lang = attr.u.val;
+      default:
+        break;
+   }
+}
+
 /* Parse a DWARF2 compilation unit starting at INFO_PTR.  UNIT_LENGTH
    includes the compilation unit header that proceeds the DIE's, but
    does not include the length field that precedes each compilation
@@ -3783,6 +4000,9 @@ parse_comp_unit (struct dwarf2_debug *stash,
   bfd *abfd = file->bfd_ptr;
   bool high_pc_relative = false;
   enum dwarf_unit_type unit_type;
+  struct attribute *str_addrp = NULL;
+  unsigned int count = 0;
+  bool compunit_flag = false;
 
   version = read_2_bytes (abfd, &info_ptr, end_ptr);
   if (version < 2 || version > 5)
@@ -3887,12 +4107,29 @@ parse_comp_unit (struct dwarf2_debug *stash,
   unit->file = file;
   unit->info_ptr_unit = info_ptr_unit;
 
+  if (abbrev->tag == DW_TAG_compile_unit)
+    compunit_flag = true;
+
   for (i = 0; i < abbrev->num_attrs; ++i)
     {
       info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr, end_ptr);
       if (info_ptr == NULL)
 	return NULL;
 
+      /* Identify attributes of the form strx and addrx store them in
+	an array and process them again.  */
+      if (is_strx_form(attr.form) ||
+          is_addrx_form(attr.form))
+      {
+	str_addrp = (struct attribute *) bfd_realloc(str_addrp,
+			(count + 1) * sizeof(struct attribute));
+	str_addrp[count].name = attr.name;
+	str_addrp[count].form = (enum dwarf_form) attr.form;
+	str_addrp[count].u.val = attr.u.val;
+	count++;
+	continue;
+      }
+
       /* Store the data if it is of an attribute we want to keep in a
 	 partial symbol table.  */
       switch (attr.name)
@@ -3917,7 +4154,7 @@ parse_comp_unit (struct dwarf2_debug *stash,
 	      /* If the compilation unit DIE has a DW_AT_low_pc attribute,
 		 this is the base address to use when reading location
 		 lists or range lists.  */
-	      if (abbrev->tag == DW_TAG_compile_unit)
+	      if (compunit_flag)
 		unit->base_address = low_pc;
 	    }
 	  break;
@@ -3966,10 +4203,27 @@ parse_comp_unit (struct dwarf2_debug *stash,
 	    unit->lang = attr.u.val;
 	  break;
 
+	case DW_AT_addr_base:
+	  unit->dwarf_addr_offset = attr.u.val;
+	  break;
+
+	case DW_AT_str_offsets_base:
+	  unit->dwarf_str_offset = attr.u.val;
+	  break;
+
 	default:
 	  break;
 	}
     }
+
+  for (i = 0; i < count; ++i)
+    {
+	struct attribute attr1 = str_addrp[i];
+	reread_attribute( unit, attr1, &low_pc, &high_pc,
+			  &high_pc_relative, compunit_flag);
+
+    }
+
   if (high_pc_relative)
     high_pc += low_pc;
   if (high_pc != 0)
@@ -3979,6 +4233,10 @@ parse_comp_unit (struct dwarf2_debug *stash,
     }
 
   unit->first_child_die_ptr = info_ptr;
+
+  if (str_addrp != NULL)
+    free(str_addrp);
+
   return unit;
 }
 
-- 
2.17.1

