lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 20 Jan 2014 22:22:01 +0100
From:	Jean Pihet <jean.pihet@...aro.org>
To:	libunwind-devel <libunwind-devel@...gnu.org>
Cc:	Will Deacon <will.deacon@....com>, Jiri Olsa <jolsa@...hat.com>,
	patches@...aro.org, linux-arm-kernel@...ts.infradead.org,
	linux-kernel@...r.kernel.org, Jean Pihet <jean.pihet@...aro.org>
Subject: [PATCH 2/3] Dwarf: load and parse the debug info of different target address sizes

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@...aro.org>
---
 src/dwarf/Gfde.c                |  25 ++++--
 src/dwarf/Gfind_proc_info-lsb.c | 194 +++++++++++++++++++++++++++++++---------
 2 files changed, 170 insertions(+), 49 deletions(-)

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
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ