Hello,

The attached patch improves support for binaries missing the GNU_EH_FRAME segment (.eh_frame_hdr section) by adding a function 'dwarf_find_eh_frame_section' that can create a synthetic one.

Regards,
Jonathan Byrd
Senior Application Developer
Allinea Software Ltd.
diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c
index 6e1f4c3..ce53086 100644
--- a/src/dwarf/Gfind_proc_info-lsb.c
+++ b/src/dwarf/Gfind_proc_info-lsb.c
@@ -523,6 +523,79 @@ dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, unw_word_t ip,
 
 #ifndef UNW_REMOTE_ONLY
 
+#define EH_FRAME_LEN /* strlen(".eh_frame") + 1 */ 10
+
+static ElfW(Addr)
+dwarf_find_eh_frame_section(struct dl_phdr_info *info)
+{
+  int fd;
+  Elf_W (Ehdr) ehdr;
+  Elf_W (Half) shstrndx;
+  Elf_W (Addr) eh_frame;
+  unsigned int i;
+  const char *file = info->dlpi_name;
+  char secname[EH_FRAME_LEN];
+  static Elf_W (Shdr) sec_hdrs[100];
+
+  if (strlen(file) == 0)
+      file = "/proc/self/exe";
+
+  Debug (1, "looking for .eh_frame section in %s\n",
+         file);
+
+  fd = open(file, O_RDONLY);
+
+  if (fd == -1)
+    return 0;
+
+  if (read (fd, &ehdr, sizeof (Elf_W (Ehdr))) != sizeof (Elf_W (Ehdr)))
+    goto file_error;
+
+  if (ehdr.e_shnum > sizeof(sec_hdrs) / sizeof(Elf_W (Shdr)))
+    {
+      Debug (4, "too many sections (%d > %d)\n", (int) ehdr.e_shnum, (int) (sizeof(sec_hdrs) / sizeof(Elf_W (Shdr))));
+      goto file_error;
+    }
+
+  shstrndx = ehdr.e_shstrndx;
+
+  Debug (4, "opened file '%s'. Section header at offset %d\n",
+         file, (int) ehdr.e_shoff);
+
+  if (lseek (fd, ehdr.e_shoff, SEEK_SET) == (off_t) -1)
+    goto file_error;
+  if (read (fd, sec_hdrs, sizeof (Elf_W (Shdr)) * ehdr.e_shnum) != (ssize_t) sizeof (Elf_W (Shdr)) * ehdr.e_shnum)
+    goto file_error;
+
+  for (i = 1; i < ehdr.e_shnum; i++)
+    {
+      if (sec_hdrs[shstrndx].sh_size - sec_hdrs[i].sh_name < EH_FRAME_LEN)
+        goto file_error;
+      if (lseek (fd, sec_hdrs[shstrndx].sh_offset + sec_hdrs[i].sh_name, SEEK_SET) == (off_t) -1)
+        goto file_error;
+      if (read (fd, secname, EH_FRAME_LEN) < (ssize_t) sizeof(EH_FRAME_LEN))
+        goto file_error;
+
+      if (strncmp (secname, ".eh_frame", EH_FRAME_LEN) == 0)
+        {
+          eh_frame = sec_hdrs[i].sh_addr + info->dlpi_addr;
+          Debug (4, "found .eh_frame at address %lx\n",
+                 eh_frame);
+          break;
+        }
+    }
+
+  close (fd);
+
+  return eh_frame;
+
+/* An error reading image file. Release resources and return error code */
+file_error:
+  close(fd);
+
+  return 0;
+}
+
 /* ptr is a pointer to a dwarf_callback_data structure and, on entry,
    member ip contains the instruction-pointer we're looking
    for.  */
@@ -536,10 +609,11 @@ dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr)
   Elf_W(Addr) load_base, max_load_addr = 0;
   int ret, need_unwind_info = cb_data->need_unwind_info;
   unw_proc_info_t *pi = cb_data->pi;
-  struct dwarf_eh_frame_hdr *hdr;
+  struct dwarf_eh_frame_hdr *hdr = NULL;
   unw_accessors_t *a;
   long n;
   int found = 0;
+  struct dwarf_eh_frame_hdr synth_eh_frame_hdr;
 #ifdef CONFIG_DEBUG_FRAME
   unw_word_t start, end;
 #endif /* CONFIG_DEBUG_FRAME*/
@@ -585,6 +659,29 @@ dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr)
 
   if (p_eh_hdr)
     {
+      hdr = (struct dwarf_eh_frame_hdr *) (p_eh_hdr->p_vaddr + load_base);
+    }
+  else
+    {
+      ElfW(Addr) eh_frame;
+      Debug (1, "no .eh_frame_hdr section found\n");
+      eh_frame = dwarf_find_eh_frame_section (info);
+      if (eh_frame)
+        {
+          unsigned char *p = (unsigned char *) &synth_eh_frame_hdr;
+          Debug (1, "using synthetic .eh_frame_hdr section for %s\n",
+                 info->dlpi_name);
+          /* synth_eh_frame_hdr.version */ p[0] = DW_EH_VERSION;
+          /* synth_eh_frame_hdr.eh_frame_ptr_enc */ p[1] = DW_EH_PE_absptr | ((sizeof(ElfW(Addr)) == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8);
+          /* synth_eh_frame_hdr.fde_count_enc */  p[2] = DW_EH_PE_omit;
+          /* synth_eh_frame_hdr.table_enc */  p[3] = DW_EH_PE_omit;
+          *(ElfW(Addr) *)(&p[4]) = eh_frame;
+          hdr = &synth_eh_frame_hdr;
+        }
+    }
+
+  if (hdr)
+    {
       if (p_dynamic)
         {
           /* For dynamicly linked executables and shared libraries,
@@ -607,7 +704,6 @@ dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr)
         di->gp = 0;
       pi->gp = di->gp;
 
-      hdr = (struct dwarf_eh_frame_hdr *) (p_eh_hdr->p_vaddr + load_base);
       if (hdr->version != DW_EH_VERSION)
         {
           Debug (1, "table `%s' has unexpected version %d\n",
@@ -648,6 +744,9 @@ dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr)
           if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
             abort ();
 
+          Debug (1, "eh_frame_start = %lx eh_frame_end = %lx\n",
+                 eh_frame_start, eh_frame_end);
+
           /* XXX we know how to build a local binary search table for
              .debug_frame, so we could do that here too.  */
           cb_data->single_fde = 1;
_______________________________________________
Libunwind-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/libunwind-devel

Reply via email to