[2/3] Dwarf: load and parse the debug info of different target address sizes

Message ID 1390252922-25889-3-git-send-email-jean.pihet@linaro.org
State New
Headers show

Commit Message

Jean Pihet Jan. 20, 2014, 9:22 p.m.
When in compat mode, load and parse the dwarf debug info accordingly.

Tested dwarf local unwinding on ARMv8 (aka AARCH64) with ARMv7 and
ARMv8 binaries.

Signed-off-by: Jean Pihet <jean.pihet@linaro.org>
---
 src/dwarf/Gfde.c                |  25 ++++--
 src/dwarf/Gfind_proc_info-lsb.c | 194 +++++++++++++++++++++++++++++++---------
 2 files changed, 170 insertions(+), 49 deletions(-)

Patch

diff --git a/src/dwarf/Gfde.c b/src/dwarf/Gfde.c
index 8659624..81959d1 100644
--- a/src/dwarf/Gfde.c
+++ b/src/dwarf/Gfde.c
@@ -59,14 +59,23 @@  parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
   /* Pick appropriate default for FDE-encoding.  DWARF spec says
      start-IP (initial_location) and the code-size (address_range) are
      "address-unit sized constants".  The `R' augmentation can be used
-     to override this, but by default, we pick an address-sized unit
-     for fde_encoding.  */
-  switch (dwarf_addr_size (as))
-    {
-    case 4:	fde_encoding = DW_EH_PE_udata4; break;
-    case 8:	fde_encoding = DW_EH_PE_udata8; break;
-    default:	fde_encoding = DW_EH_PE_omit; break;
-    }
+     to override this, but by default, we pick the target binary address
+     size unit for fde_encoding.  */
+  switch (as->target_addr_size)
+  {
+  /* If defined at binary load time (e.g. from the ELF format) */
+  case TARGET_ADDR_SIZE_32:	fde_encoding = DW_EH_PE_udata4; break;
+  case TARGET_ADDR_SIZE_64:	fde_encoding = DW_EH_PE_udata8; break;
+  /* If not defined, use the current address size unit */
+  case TARGET_ADDR_SIZE_DEFAULT:
+  default:
+	switch (dwarf_addr_size (as))
+	{
+	case 4:    fde_encoding = DW_EH_PE_udata4; break;
+	case 8:    fde_encoding = DW_EH_PE_udata8; break;
+	default:   fde_encoding = DW_EH_PE_omit; break;
+	}
+  }
 
   dci->lsda_encoding = DW_EH_PE_omit;
   dci->handler = 0;
diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c
index f75bda2..8c35e58 100644
--- a/src/dwarf/Gfind_proc_info-lsb.c
+++ b/src/dwarf/Gfind_proc_info-lsb.c
@@ -81,36 +81,89 @@  linear_search (unw_addr_space_t as, unw_word_t ip,
 #endif /* !UNW_REMOTE_ONLY */
 
 #ifdef CONFIG_DEBUG_FRAME
-/* Load .debug_frame section from FILE.  Allocates and returns space
-   in *BUF, and sets *BUFSIZE to its size.  IS_LOCAL is 1 if using the
-   local process, in which case we can search the system debug file
-   directory; 0 for other address spaces, in which case we do not; or
-   -1 for recursive calls following .gnu_debuglink.  Returns 0 on
-   success, 1 on error.  Succeeds even if the file contains no
-   .debug_frame.  */
-/* XXX: Could use mmap; but elf_map_image keeps tons mapped in.  */
-
-static int
-load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
+static int load_debug_frame_Elf32(const char *file, FILE *f, char *linkbuf,
+				  size_t *linksize, char **buf, size_t *bufsize)
 {
-  FILE *f;
-  Elf_W (Ehdr) ehdr;
-  Elf_W (Half) shstrndx;
-  Elf_W (Shdr) *sec_hdrs = NULL;
+  Elf32_Ehdr ehdr;
+  Elf32_Shdr *sec_hdrs = NULL;
+  Elf32_Half shstrndx;
   char *stringtab = NULL;
   unsigned int i;
-  size_t linksize = 0;
-  char *linkbuf = NULL;
+
+  if (fseek(f, 0L, SEEK_SET))
+    goto file_error;
+  if (fread (&ehdr, sizeof(Elf32_Ehdr), 1, f) != 1)
+    goto file_error;
   
-  *buf = NULL;
-  *bufsize = 0;
+  shstrndx = ehdr.e_shstrndx;
   
-  f = fopen (file, "r");
+  Debug (4, "opened file '%s'. Section header at offset %d\n",
+         file, (int) ehdr.e_shoff);
+
+  fseek (f, ehdr.e_shoff, SEEK_SET);
+  sec_hdrs = calloc (ehdr.e_shnum, sizeof(Elf32_Shdr));
+  if (fread (sec_hdrs, sizeof(Elf32_Shdr), ehdr.e_shnum, f) != ehdr.e_shnum)
+    goto file_error;
   
-  if (!f)
-    return 1;
+  Debug (4, "loading string table of size %zd\n",
+	   sec_hdrs[shstrndx].sh_size);
+  stringtab = malloc (sec_hdrs[shstrndx].sh_size);
+  fseek (f, sec_hdrs[shstrndx].sh_offset, SEEK_SET);
+  if (fread (stringtab, 1, sec_hdrs[shstrndx].sh_size, f) != sec_hdrs[shstrndx].sh_size)
+    goto file_error;
   
-  if (fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f) != 1)
+  for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++)
+  {
+      char *secname = &stringtab[sec_hdrs[i].sh_name];
+
+      if (strcmp (secname, ".debug_frame") == 0)
+        {
+	  *bufsize = sec_hdrs[i].sh_size;
+	  *buf = malloc (*bufsize);
+
+	  fseek (f, sec_hdrs[i].sh_offset, SEEK_SET);
+	  if (fread (*buf, 1, *bufsize, f) != *bufsize)
+	    goto file_error;
+
+	  Debug (4, "read %zd bytes of .debug_frame from offset %d\n",
+		 *bufsize, sec_hdrs[i].sh_offset);
+	}
+      else if (strcmp (secname, ".gnu_debuglink") == 0)
+	{
+	  *linksize = sec_hdrs[i].sh_size;
+	  linkbuf = malloc(*linksize);
+
+	  fseek (f, sec_hdrs[i].sh_offset, SEEK_SET);
+	  if (fread (linkbuf, 1, *linksize, f) != *linksize)
+	    goto file_error;
+
+	  Debug (4, "read %d bytes of .gnu_debuglink from offset %d\n",
+		 (int) *linksize, sec_hdrs[i].sh_offset);
+	}
+  }
+
+  free(sec_hdrs);
+  free(stringtab);
+  return 0;
+
+file_error:
+  free(sec_hdrs);
+  free(stringtab);
+  return -1;
+}
+
+static int load_debug_frame_Elf64(const char *file, FILE *f, char *linkbuf,
+				  size_t *linksize, char **buf, size_t *bufsize)
+{
+  Elf64_Ehdr ehdr;
+  Elf64_Shdr *sec_hdrs = NULL;
+  Elf64_Half shstrndx;
+  char *stringtab = NULL;
+  unsigned int i;
+
+  if (fseek(f, 0L, SEEK_SET))
+    goto file_error;
+  if (fread (&ehdr, sizeof(Elf64_Ehdr), 1, f) != 1)
     goto file_error;
   
   shstrndx = ehdr.e_shstrndx;
@@ -119,8 +172,8 @@  load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
          file, (int) ehdr.e_shoff);
 
   fseek (f, ehdr.e_shoff, SEEK_SET);
-  sec_hdrs = calloc (ehdr.e_shnum, sizeof (Elf_W (Shdr)));
-  if (fread (sec_hdrs, sizeof (Elf_W (Shdr)), ehdr.e_shnum, f) != ehdr.e_shnum)
+  sec_hdrs = calloc (ehdr.e_shnum, sizeof(Elf64_Shdr));
+  if (fread (sec_hdrs, sizeof(Elf64_Shdr), ehdr.e_shnum, f) != ehdr.e_shnum)
     goto file_error;
   
   Debug (4, "loading string table of size %zd\n",
@@ -131,7 +184,7 @@  load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
     goto file_error;
   
   for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++)
-    {
+  {
       char *secname = &stringtab[sec_hdrs[i].sh_name];
 
       if (strcmp (secname, ".debug_frame") == 0)
@@ -148,20 +201,71 @@  load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
 	}
       else if (strcmp (secname, ".gnu_debuglink") == 0)
 	{
-	  linksize = sec_hdrs[i].sh_size;
-	  linkbuf = malloc (linksize);
+	  *linksize = sec_hdrs[i].sh_size;
+	  linkbuf = malloc(*linksize);
 
 	  fseek (f, sec_hdrs[i].sh_offset, SEEK_SET);
-	  if (fread (linkbuf, 1, linksize, f) != linksize)
+	  if (fread (linkbuf, 1, *linksize, f) != *linksize)
 	    goto file_error;
 
-	  Debug (4, "read %zd bytes of .gnu_debuglink from offset %zd\n",
-		 linksize, sec_hdrs[i].sh_offset);
+	  Debug (4, "read %d bytes of .gnu_debuglink from offset %zd\n",
+		 (int) *linksize, sec_hdrs[i].sh_offset);
 	}
-    }
+  }
+
+  free(sec_hdrs);
+  free(stringtab);
+  return 0;
+
+file_error:
+  free(sec_hdrs);
+  free(stringtab);
+  return -1;
+}
+
+/* Load .debug_frame section from FILE.  Allocates and returns space
+   in *BUF, and sets *BUFSIZE to its size.  IS_LOCAL is 1 if using the
+   local process, in which case we can search the system debug file
+   directory; 0 for other address spaces, in which case we do not; or
+   -1 for recursive calls following .gnu_debuglink.  Returns 0 on
+   success, 1 on error.  Succeeds even if the file contains no
+   .debug_frame.  */
+/* XXX: Could use mmap; but elf_map_image keeps tons mapped in.  */
 
-  free (stringtab);
-  free (sec_hdrs);
+static int
+load_debug_frame (const char *file, char **buf, size_t *bufsize,
+		  unw_addr_space_t as, int is_local)
+{
+  FILE *f;
+  unsigned char e_ident[sizeof(((Elf32_Ehdr *)0)->e_ident)];
+  size_t linksize = 0;
+  char *linkbuf = NULL;
+  
+  *buf = NULL;
+  *bufsize = 0;
+  
+  f = fopen (file, "r");
+  
+  if (!f)
+    return 1;
+  
+  if (fread (&e_ident, sizeof(e_ident), 1, f) != 1)
+    goto file_error;
+ 
+  switch (e_ident[EI_CLASS]) {
+  case ELFCLASS32:
+	as->target_addr_size = TARGET_ADDR_SIZE_32;
+	load_debug_frame_Elf32(file, f, linkbuf, &linksize, buf, bufsize);
+	break;
+  case ELFCLASS64:
+	as->target_addr_size = TARGET_ADDR_SIZE_64;
+	load_debug_frame_Elf64(file, f, linkbuf, &linksize, buf, bufsize);
+  	break;
+  case ELFCLASSNONE:
+  default:
+	Debug (15, "Wrong ELF class 0x%02x\n", e_ident[EI_CLASS]);
+	goto file_error;
+  }
 
   fclose (f);
 
@@ -195,14 +299,14 @@  load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
       strcpy (newname, basedir);
       strcat (newname, "/");
       strcat (newname, linkbuf);
-      ret = load_debug_frame (newname, buf, bufsize, -1);
+      ret = load_debug_frame (newname, buf, bufsize, as, -1);
 
       if (ret == 1)
 	{
 	  strcpy (newname, basedir);
 	  strcat (newname, "/.debug/");
 	  strcat (newname, linkbuf);
-	  ret = load_debug_frame (newname, buf, bufsize, -1);
+	  ret = load_debug_frame (newname, buf, bufsize, as, -1);
 	}
 
       if (ret == 1 && is_local == 1)
@@ -211,20 +315,19 @@  load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
 	  strcat (newname, basedir);
 	  strcat (newname, "/");
 	  strcat (newname, linkbuf);
-	  ret = load_debug_frame (newname, buf, bufsize, -1);
+	  ret = load_debug_frame (newname, buf, bufsize, as, -1);
 	}
 
       free (basedir);
       free (newname);
     }
-  free (linkbuf);
+
+  free(linkbuf);
 
   return 0;
 
 /* An error reading image file. Release resources and return error code */
 file_error:
-  free(stringtab);
-  free(sec_hdrs);
   free(linkbuf);
   fclose(f);
 
@@ -303,7 +406,8 @@  locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname,
   else
     name = (char*) dlname;
 
-  err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space);
+  err = load_debug_frame (name, &buf, &bufsize, as,
+		  	  as == unw_local_addr_space);
   
   if (!err)
     {
@@ -851,6 +955,14 @@  dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
 #ifndef UNW_REMOTE_ONLY
       struct unw_debug_frame_list *fdesc = (void *) di->u.ti.table_data;
 
+      /*
+       * Set the target address size as found in the loaded debug binary.
+       * Note: in case of local unwinding the caller 'as' is set to
+       * unw_local_addr_space, cf. below. Let's assign the value to
+       * the caller 'as' before changing the value of 'as'.
+       */
+      as->target_addr_size = unw_local_addr_space->target_addr_size;
+
       /* UNW_INFO_FORMAT_TABLE (i.e. .debug_frame) is read from local address
          space.  Both the index and the unwind tables live in local memory, but
          the address space to check for properties like the address size and