This is the mail archive of the frysk@sourceware.org mailing list for the frysk project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch] Add debug_frame parsing


Hi,

This patch widens out libunwind interface to accept either eh_frame or
debug_frame tables and adds support for parsing debug_frame data. It
currently does a linear search through the data to find the right fde
and corresponding cie by making the right adjustments. Although it does
find the right fde covering the ip ranges, it doesn't always run the cfi
program correctly. So for now the default is still the default and
debug_frame is only used as fallback when eh_frame data cannot be found
(that can be changed in a simple if statement, see the FIXME in UnwindH
- that will however give a couple of test failures). It also fixes a
couple of libunwind issues found when working on this (direct usage of
local memory space, error values not being negative and unw_word being
64bit != dwarf 64 format is always in use).

frysk-imports/libunwind/ChangeLog
2008-01-17  Mark Wielaard  <mwielaard@redhat.com>

    * include/dwarf.h (dwarf_extract_proc_info_from_fde): Pass
    table_start.
    * src/dwarf/Gfde.c (is_cie_id): Removed.
    (parse_cie): Accept debug and eh cie ids.
    (dwarf_extract_proc_info_from_fde): Accept table_start. Calculate
    correct cie_addr. Fix error reporting.
    * src/dwarf/Gfind_proc_info-lsb.c (linear_search): Don't depend
    on local address space. Pass table start for fde parsing.
    (dwarf_search_unwind_table): Handle debug_frame by linear search.
    * src/mi/Gget_unwind_table.c (get_frame_table, get_debug_table):
    new functions.
    (unw_get_unwind_table): Call either get_frame_table or
    get_debug_table depending on format.

frysk-sys/lib/unwind/ChangeLog
2008-01-17  Mark Wielaard  <mwielaard@redhat.com>

    * cni/UnwindH.hxx (get_eh_frame_hdr_addr): Find and return
    debug_frame address and set pi->format.
    (createProcInfoFromElfImage): Handle either debug_frame or
    eh_frame addresses.

Tested on x86_64 and x86 (fedora and centos, which both have full
eh_frame coverage, so admittedly most of the new code paths aren't hit
yet).

Cheers,

Mark
diff --git a/frysk-imports/libunwind/include/dwarf.h b/frysk-imports/libunwind/include/dwarf.h
index 47896f6..cf8a74a 100644
--- a/frysk-imports/libunwind/include/dwarf.h
+++ b/frysk-imports/libunwind/include/dwarf.h
@@ -362,6 +362,7 @@ extern int dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr,
 			    int *is_register);
 extern int dwarf_extract_proc_info_from_fde (unw_addr_space_t as,
 					     unw_accessors_t *a,
+					     unw_word_t table_start,
 					     unw_word_t *fde_addr,
 					     unw_proc_info_t *pi,
 					     int need_unwind_info,
diff --git a/frysk-imports/libunwind/src/dwarf/Gfde.c b/frysk-imports/libunwind/src/dwarf/Gfde.c
index 11a6433..de2b193 100644
--- a/frysk-imports/libunwind/src/dwarf/Gfde.c
+++ b/frysk-imports/libunwind/src/dwarf/Gfde.c
@@ -1,6 +1,8 @@
 /* libunwind - a platform-independent unwind library
    Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.
 	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+   Copyright (c) 2008 Red Hat, Inc.
+	Contributed by Mark Wielaard <mwielaard@redhat.com>
 
 This file is part of libunwind.
 
@@ -25,15 +27,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 
 #include "dwarf_i.h"
 
-static inline int
-is_cie_id (unw_word_t val)
-{
-  /* DWARF spec says CIE_id is 0xffffffff (for 32-bit ELF) or
-     0xffffffffffffffff (for 64-bit ELF).  However, the GNU toolchain
-     uses 0.  */
-  return (val == 0 || val == - (unw_word_t) 1);
-}
-
 /* Note: we don't need to keep track of more than the first four
    characters of the augmentation string, because we (a) ignore any
    augmentation string contents once we find an unrecognized character
@@ -80,7 +73,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
       if ((ret = dwarf_readu32 (as, a, &addr, &cie_id, arg)) < 0)
 	return ret;
       /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */
-      if (cie_id != 0)
+      if (cie_id != 0 && cie_id != 0xffffffff)
 	{
 	  Debug (1, "Unexpected CIE id %x\n", cie_id);
 	  return -UNW_EINVAL;
@@ -99,7 +92,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
 	return ret;
       /* DWARF says CIE id should be 0xffffffffffffffff, but in
 	 .eh_frame, it's 0 */
-      if (cie_id != 0)
+      if (cie_id != 0 && cie_id != 0xffffffffffffffff)
 	{
 	  Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id);
 	  return -UNW_EINVAL;
@@ -217,7 +210,8 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
 
 HIDDEN int
 dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
-				  unw_word_t *addrp, unw_proc_info_t *pi,
+				  unw_word_t table_start, unw_word_t *addrp,
+				  unw_proc_info_t *pi,
 				  int need_unwind_info,
 				  void *arg)
 {
@@ -252,7 +246,12 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
       if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
 	return ret;
 
-      if (is_cie_id (cie_offset))
+      /* DWARF spec says CIE_id is 0xffffffff (for 32-bit ELF) or
+	 0xffffffffffffffff (for 64-bit ELF).  However, the GNU toolchain
+	 uses 0.  */
+      if ((pi->format != UNW_INFO_FORMAT_TABLE && cie_offset == 0)
+	  || (pi->format == UNW_INFO_FORMAT_TABLE
+	      && cie_offset == 0xffffffff))
 	/* ignore CIEs (happens during linear searches) */
 	return 0;
 
@@ -260,7 +259,10 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
 	 .debug_frame-relative offset, but the GCC-generated .eh_frame
 	 sections instead store a "pcrelative" offset, which is just
 	 as fine as it's self-contained.  */
-      cie_addr = cie_offset_addr - cie_offset;
+      if (pi->format == UNW_INFO_FORMAT_TABLE)
+	cie_addr = table_start + cie_offset;
+      else
+	cie_addr = cie_offset_addr - cie_offset;
     }
   else
     {
@@ -277,7 +279,12 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
       if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
 	return ret;
 
-      if (is_cie_id (cie_offset))
+      /* DWARF spec says CIE_id is 0xffffffff (for 32-bit ELF) or
+	 0xffffffffffffffff (for 64-bit ELF).  However, the GNU toolchain
+	 uses 0.  */
+      if ((pi->format != UNW_INFO_FORMAT_TABLE && cie_offset == 0)
+          || (pi->format == UNW_INFO_FORMAT_TABLE
+              && cie_offset == 0xffffffffffffffff))
 	/* ignore CIEs (happens during linear searches) */
 	return 0;
 
@@ -285,7 +292,10 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
 	 .debug_frame-relative offset, but the GCC-generated .eh_frame
 	 sections instead store a "pcrelative" offset, which is just
 	 as fine as it's self-contained.  */
-      cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset);
+      if (pi->format == UNW_INFO_FORMAT_TABLE)
+	cie_addr = (unw_word_t) ((uint64_t) table_start + cie_offset);
+      else
+	cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset);
     }
 
   if ((ret = parse_cie (as, a, cie_addr, pi, &dci, arg)) < 0)
@@ -320,11 +330,12 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
 
   if (need_unwind_info)
     {
-      pi->format = UNW_INFO_FORMAT_TABLE;
+      // FRYSK LOCAL - we already set this.
+      // pi->format = UNW_INFO_FORMAT_TABLE;
       pi->unwind_info_size = sizeof (dci);
       pi->unwind_info = mempool_alloc (&dwarf_cie_info_pool);
       if (!pi->unwind_info)
-	return UNW_ENOMEM;
+	return -UNW_ENOMEM;
 
       if (dci.have_abi_marker)
 	{
diff --git a/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c b/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c
index a8a3b69..be22e3e 100644
--- a/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c
+++ b/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c
@@ -1,6 +1,8 @@
 /* libunwind - a platform-independent unwind library
    Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.
 	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+   Copyright (c) 2008, Red Hat, Inc.
+	Contributed by Mark Wielaard <mwielaard@redhat.com>
 
 This file is part of libunwind.
 
@@ -52,20 +54,23 @@ struct callback_data
     unw_dyn_info_t di;		/* table info (if single_fde is false) */
   };
 
+#endif /* !UNW_REMOTE_ONLY */
+
 static int
 linear_search (unw_addr_space_t as, unw_word_t ip,
 	       unw_word_t eh_frame_start, unw_word_t eh_frame_end,
 	       unw_word_t fde_count,
 	       unw_proc_info_t *pi, int need_unwind_info, void *arg)
 {
-  unw_accessors_t *a = unw_get_accessors (unw_local_addr_space);
+  unw_accessors_t *a = unw_get_accessors (as);
   unw_word_t i = 0, fde_addr, addr = eh_frame_start;
   int ret;
 
   while (i++ < fde_count && addr < eh_frame_end)
     {
       fde_addr = addr;
-      if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, 0, arg))
+      if ((ret = dwarf_extract_proc_info_from_fde (as, a, eh_frame_start,
+						   &addr, pi, 0, arg))
 	  < 0)
 	return ret;
 
@@ -74,7 +79,8 @@ linear_search (unw_addr_space_t as, unw_word_t ip,
 	  if (!need_unwind_info)
 	    return 1;
 	  addr = fde_addr;
-	  if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi,
+	  if ((ret = dwarf_extract_proc_info_from_fde (as, a, eh_frame_start,
+						       &addr, pi,
 						       need_unwind_info, arg))
 	      < 0)
 	    return ret;
@@ -84,6 +90,8 @@ linear_search (unw_addr_space_t as, unw_word_t ip,
   return -UNW_ENOINFO;
 }
 
+#ifndef UNW_REMOTE_ONLY
+
 /* Info is a pointer to a unw_dyn_info_t structure and, on entry,
    member u.rti.segbase contains the instruction-pointer we're looking
    for.  */
@@ -361,6 +369,15 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
 #endif
   int ret;
 
+  if (di->format == UNW_INFO_FORMAT_TABLE)
+    {
+      ret = linear_search (as, ip, di->u.rti.table_data,
+			   di->u.rti.table_data
+			   + di->u.rti.table_len * sizeof (unw_word_t),
+			   ~0UL, pi, need_unwind_info, arg);
+      return ret;
+    }
+
   assert (di->format == UNW_INFO_FORMAT_REMOTE_TABLE
 	  && (ip >= di->start_ip && ip < di->end_ip));
 
@@ -397,7 +414,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
   Debug (15, "ip=0x%lx, start_ip=0x%lx\n",
 	 (long) ip, (long) (e->start_ip_offset + segbase));
   fde_addr = e->fde_offset + segbase;
-  if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi,
+  if ((ret = dwarf_extract_proc_info_from_fde (as, a, segbase, &fde_addr, pi,
 					       need_unwind_info, arg)) < 0)
     return ret;
 
diff --git a/frysk-imports/libunwind/src/mi/Gget_unwind_table.c b/frysk-imports/libunwind/src/mi/Gget_unwind_table.c
index fc46269..0223013 100644
--- a/frysk-imports/libunwind/src/mi/Gget_unwind_table.c
+++ b/frysk-imports/libunwind/src/mi/Gget_unwind_table.c
@@ -1,4 +1,4 @@
-// Copyright 2007, Red Hat Inc.
+// Copyright 2007, 2008, Red Hat Inc.
 
 /* This file is part of libunwind.
 
@@ -25,11 +25,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 #include "dwarf_i.h"
 #include "dwarf-eh.h"
 
-int
-unw_get_unwind_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info,
-		     unw_accessors_t *eh_frame_accessors,
-		     unw_word_t eh_frame_hdr_address,
-		     void *eh_frame_arg)
+#include <stdio.h>
+
+static int
+get_frame_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info,
+		unw_accessors_t *eh_frame_accessors,
+		unw_word_t eh_frame_hdr_address,
+		void *eh_frame_arg)
 {
   int ret;
   unw_addr_space_t as = unw_create_addr_space (eh_frame_accessors, 0);
@@ -87,7 +89,51 @@ unw_get_unwind_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info,
   di.u.rti.table_data = eh_frame_hdr_address + 12;
   di.u.rti.segbase = eh_frame_hdr_address;
 
+  pi->start_ip = 0;
+  pi->end_ip = 0;
   ret = tdep_search_unwind_table (as, ip, &di, pi, need_unwind_info,
 				  eh_frame_arg);
   return ret;
 }
+
+static int
+get_debug_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info,
+                unw_accessors_t *accessors,
+                unw_word_t address,
+                void *arg)
+{
+  unw_addr_space_t as = unw_create_addr_space (accessors, 0);
+
+  unw_dyn_info_t di;
+  di.start_ip = pi->start_ip;
+  di.end_ip = pi->end_ip;
+  di.format = UNW_INFO_FORMAT_TABLE;
+  di.gp = pi->gp;
+
+  // XXX Should we use the ti struct of the union?
+  di.u.rti.name_ptr = 0;
+  di.u.rti.segbase = address;
+  di.u.rti.table_data = address;
+  di.u.rti.table_len = pi->unwind_info_size;
+  
+  pi->start_ip = 0;
+  pi->end_ip = 0;
+  return tdep_search_unwind_table (as, ip, &di, pi, need_unwind_info, arg);
+}
+
+int
+unw_get_unwind_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info,
+		     unw_accessors_t *accessors,
+		     unw_word_t address,
+		     void *arg)
+{
+  if (pi->format == UNW_INFO_FORMAT_TABLE)
+    return get_debug_table(ip, pi, need_unwind_info, accessors,
+			   address, arg);
+
+  if (pi->format == UNW_INFO_FORMAT_REMOTE_TABLE)
+    return get_frame_table(ip, pi, need_unwind_info, accessors,
+			   address, arg);
+
+  return -UNW_EINVAL;
+}
diff --git a/frysk-sys/lib/unwind/cni/UnwindH.hxx b/frysk-sys/lib/unwind/cni/UnwindH.hxx
index e69e031..08c8ce1 100644
--- a/frysk-sys/lib/unwind/cni/UnwindH.hxx
+++ b/frysk-sys/lib/unwind/cni/UnwindH.hxx
@@ -1,6 +1,6 @@
 // This file is part of the program FRYSK.
 //
-// Copyright 2007, Red Hat Inc.
+// Copyright 2007, 2008, Red Hat Inc.
 //
 // FRYSK is free software; you can redistribute it and/or modify it
 // under the terms of the GNU General Public License as published by
@@ -488,7 +488,26 @@ get_eh_frame_hdr_addr(unw_proc_info_t *pi, char *image, size_t size,
         }
     }
 
-  if (ptxt_ndx == -1 || peh_hdr_ndx == -1)
+  Elf_Data *debug_frame_data = NULL;
+  size_t shstrndx;
+  if (elf_getshstrndx (elf, &shstrndx) >= 0)
+    {
+      Elf_Scn *scn = NULL;
+      while ((scn = elf_nextscn (elf, scn)) != NULL
+	     && debug_frame_data == NULL)
+	{
+	  GElf_Shdr shdr;
+	  if (gelf_getshdr (scn, &shdr) != NULL
+	      && shdr.sh_type == SHT_PROGBITS)
+	    {
+	      const char *name = elf_strptr (elf, shstrndx, shdr.sh_name);
+	      if (strcmp (name, ".debug_frame") == 0)
+		debug_frame_data = elf_getdata (scn, NULL);
+	    }
+	}
+    }
+
+  if (ptxt_ndx == -1 || (peh_hdr_ndx == -1 && debug_frame_data == NULL))
     return NULL;
 
   GElf_Phdr ptxt, peh_hdr;
@@ -547,7 +566,23 @@ get_eh_frame_hdr_addr(unw_proc_info_t *pi, char *image, size_t size,
 
   *peh_vaddr = peh_hdr.p_vaddr;
 
-  char *hdr = image + peh_hdr.p_offset;
+  char *hdr;
+  // FIXME. Currently we prefer eh_frame, but we should switch to
+  // prefer debug_frame when all bugs have been squashed out of that
+  // in libunwind.
+  if (peh_hdr_ndx == -1
+      && debug_frame_data != NULL && debug_frame_data->d_buf != NULL
+      && debug_frame_data->d_size != 0)
+    {
+      pi->format = UNW_INFO_FORMAT_TABLE;
+      pi->unwind_info_size = debug_frame_data->d_size / sizeof (unw_word_t);
+      hdr = (char *) debug_frame_data->d_buf;
+    }
+  else
+    {
+      pi->format = UNW_INFO_FORMAT_REMOTE_TABLE;
+      hdr = image + peh_hdr.p_offset;
+    }
   return hdr;
 }
 
@@ -600,14 +635,26 @@ lib::unwind::TARGET::createProcInfoFromElfImage(lib::unwind::AddressSpace* addre
   if (eh_table_hdr == NULL)
     return new lib::unwind::ProcInfo(-UNW_ENOINFO);
 
-  int ret = unw_get_unwind_table((unw_word_t) ip,
-				 procInfo,
-				 (int) needUnwindInfo,
-				 &local_accessors,
-				 // virtual address
-				 peh_vaddr,
-				 // address adjustment
-				 eh_table_hdr - peh_vaddr);
+  int ret;
+  if (procInfo->format == UNW_INFO_FORMAT_REMOTE_TABLE)
+    ret = unw_get_unwind_table((unw_word_t) ip,
+			       procInfo,
+			       (int) needUnwindInfo,
+			       &local_accessors,
+			       // virtual address
+			       peh_vaddr,
+			       // address adjustment
+			       eh_table_hdr - peh_vaddr);
+  else
+    ret = unw_get_unwind_table((unw_word_t) ip,
+                               procInfo,
+                               (int) needUnwindInfo,
+                               &local_accessors,
+                               // virtual address
+                               0,
+                               // address adjustment
+                               eh_table_hdr);
+  
   
   logFine(this, logger, "Post unw_get_unwind_table");
   lib::unwind::ProcInfo *myInfo;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]