support for libelf 0.153 AND later up to 0.165 39/65539/2
authorAdrian Szyndela <adrian.s@samsung.com>
Mon, 11 Apr 2016 10:32:31 +0000 (12:32 +0200)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 12 Apr 2016 16:05:04 +0000 (18:05 +0200)
Change-Id: If3927cbb1438115c7de2397a11ea91be9b9329fe

src/crash-stack/crash-stack-arm.c
src/crash-stack/crash-stack-libelf.c
src/crash-stack/crash-stack.c
src/crash-stack/crash-stack.h

index 81dfe4e..8643473 100644 (file)
@@ -5,6 +5,7 @@
 
 static Elf *g_core = NULL;
 static Dwfl *g_dwfl = NULL;
+static Mappings *g_mappings = NULL;
 
 struct Regs
 {
@@ -58,7 +59,7 @@ static Boolean report (void *data, Int32 address)
 Boolean readT (Int32 a, void *v, size_t size)
 {
   Dwfl_Module *module = 0;
-  Boolean result = FALSE;
+  Elf_Data *data = NULL;
 
   int segment = dwfl_addrsegment (g_dwfl, a, &module);
 
@@ -70,30 +71,51 @@ Boolean readT (Int32 a, void *v, size_t size)
     GElf_Addr bias;
     Elf *elf = dwfl_module_getelf (module, &bias);
 
-    Elf_Data *data = elf_getdata_rawchunk (elf, a-start, size, ELF_T_BYTE);
-    if (data != NULL)
-    {
-      memcpy (v, data->d_buf, size);
-      result = TRUE;
-    }
+    data = elf_getdata_rawchunk (elf, a-start, size, ELF_T_BYTE);
   }
-  if (!result && segment != -1)
+  if (NULL == data && segment != -1)
   {
     // get data from segment
     GElf_Phdr mem;
     GElf_Phdr *phdr = gelf_getphdr (g_core, segment, &mem);
     Dwarf_Addr offset_in_segment = a - phdr->p_vaddr;
-    Dwarf_Addr offset_in_file = phdr->p_offset + offset_in_segment;
+    if (offset_in_segment < phdr->p_filesz)
+    {
+      Dwarf_Addr offset_in_file = phdr->p_offset + offset_in_segment;
+
+      data = elf_getdata_rawchunk (g_core, offset_in_file, size, ELF_T_BYTE);
+    }
+  }
 
-    Elf_Data *data = elf_getdata_rawchunk (g_core, offset_in_file, size, ELF_T_BYTE);
-    if (data != NULL)
+  if (NULL == data && module != NULL)
+  {
+    const char *name = dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (name != NULL && name[0] == '[')
     {
-      memcpy (v, data->d_buf, size);
-      result = TRUE;
+      int i;
+      // get module from mappings
+      for (i = 0; i < g_mappings->elems; i++)
+      {
+        if (g_mappings->tab[i].m_start <= a && a < g_mappings->tab[i].m_end)
+        {
+          // compute offset relative to the start of the mapping
+          Int32 offset = a - g_mappings->tab[i].m_start;
+          // read from the file, but also account file offset
+          data = elf_getdata_rawchunk (g_mappings->tab[i].m_elf,
+              offset + g_mappings->tab[i].m_offset, size, ELF_T_BYTE);
+          break;
+        }
+      }
     }
   }
 
-  return result;
+  if (data != NULL)
+  {
+    memcpy (v, data->d_buf, size);
+    return TRUE;
+  }
+
+  return FALSE;
 }
 
 static Boolean readW (Int32 a, Int32 *v)
@@ -117,15 +139,59 @@ static Int32 getProloguePC (Int32 current_pc)
     Dwfl_Module *module = dwfl_addrmodule (g_dwfl, current_pc);
     if (module)
     {
-        GElf_Off offset;
+//        GElf_Off offset;
         GElf_Sym sym;
-        dwfl_module_addrinfo (module, current_pc, &offset, &sym, NULL, NULL, NULL);
-        result = current_pc - offset;
+//        dwfl_module_addrinfo (module, current_pc, &offset, &sym, NULL, NULL, NULL);
+        dwfl_module_addrsym (module, current_pc, &sym, NULL);
+//        result = current_pc - offset;
+        result = sym.st_value;
+    }
+    if (0 == result)
+    {
+      int i;
+      for (i=0; i < g_mappings->elems; i++)
+      {
+        if (g_mappings->tab[i].m_start <= current_pc && current_pc < g_mappings->tab[i].m_end)
+        {
+          /* go through symbols to find the nearest */
+          Elf_Scn *scn = NULL;
+          Elf *elf = g_mappings->tab[i].m_elf;
+          while ((scn = elf_nextscn (elf, scn)) != NULL)
+          {
+            GElf_Shdr shdr_mem;
+            GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+            if (shdr != NULL && (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM))
+            {
+              Elf_Data *sdata = elf_getdata (scn, NULL);
+              unsigned int nsyms = sdata->d_size / (gelf_getclass(elf) == ELFCLASS32 ?
+                  sizeof (Elf32_Sym) :
+                  sizeof (Elf64_Sym));
+              unsigned int cnt;
+              uintptr_t address_offset = current_pc;
+              if (shdr->sh_type == SHT_DYNSYM)
+                address_offset -= g_mappings->tab[i].m_start;
+              for (cnt = 0; cnt < nsyms; ++cnt)
+              {
+                GElf_Sym sym_mem;
+                Elf32_Word xndx;
+                GElf_Sym *sym = gelf_getsymshndx(sdata, NULL, cnt, &sym_mem, &xndx);
+                if (sym != NULL && sym->st_shndx != SHN_UNDEF)
+                {
+                  if (sym->st_value <= address_offset && address_offset < sym->st_value + sym->st_size)
+                  {
+                    return sym->st_value;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
     }
     return result;
 }
 
-void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Callstack *callstack)
+void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Mappings *mappings, Callstack *callstack)
 {
   UnwindCallbacks callbacks =
   {
@@ -143,6 +209,7 @@ void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Callstack *callstack
 
   g_dwfl = dwfl;
   g_core = core;
+  g_mappings = mappings;
 
   callstack->tab[0] = regs->pc;
   callstack->elems = 1;
index 3bde52f..732c1ea 100644 (file)
@@ -26,7 +26,7 @@ void *get_place_for_register_value (const char *regname, int regnum)
   return 0;
 }
 
-void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Callstack *callstack)
+void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Mappings *mappings, Callstack *callstack)
 {
   callstack->elems = 0;
   dwfl_getthreads (dwfl, thread_callback, callstack);
index 8f535ea..f7e6639 100644 (file)
 #include <sys/prctl.h>
 #include <linux/prctl.h>
 #include "crash-stack.h"
+#include <string.h>
+#include <elfutils/version.h>
 
 extern char *__cxa_demangle (const char *mangled_name, char *output_buffer,
                             size_t *length, int *status);
 
-/*
-static int frame_callback (Dwfl_Frame *state, void *arg)
-{
-  Regs *regs = static_cast<Regs*>(arg);
-  dwfl_frame_pc (state, &regs->pc, NULL);
-  return DWARF_CB_ABORT;
-}
-
-static int thread_callback (Dwfl_Thread *thread, void *thread_arg)
-{
-  dwfl_thread_getframes (thread, frame_callback, thread_arg);
-  return DWARF_CB_ABORT;
-}
-*/
-
 static int module_callback (Dwfl_Module *module, void **userdata,
                                                   const char *name, Dwarf_Addr address,
                                                   void *arg)
 {
-  /* To get something from module file do something like:
-
-     GElf_Addr bias;
-     Elf *elf = dwfl_module_getelf (module, &bias);
-
-     cout << "Module : " << name << " @" << hex << address << " bias " << bias << endl;
-
-     Elf_Data *data = elf_getdata_rawchunk (elf, 0, 4, ELF_T_BYTE);
-     cout << " " << static_cast<char*>(data->d_buf)+1 << endl;
-   */
+  if (name != NULL && name[0] == '[')
+  {
+    /* libdwfl couldn't get the module file - we will get it later from notes */
+    Mappings *mappings = arg;
+    if (mappings->elems < MAX_MAPPINGS_NUM)
+    {
+      size_t elems = mappings->elems;
+      mappings->tab[elems].m_start = address;
+      mappings->tab[elems].m_end = 0;
+      mappings->tab[elems].m_offset = 0;
+      mappings->tab[elems].m_name = NULL;
+      mappings->tab[elems].m_fd = -1;
+      mappings->tab[elems].m_elf = 0;
+      mappings->elems++;
+    }
+  }
   return DWARF_CB_OK;
 }
 
-void getvalue (Elf *core, const void *from, size_t size, void *to)
+static void getvalue (Elf *core, const void *from, size_t size, void *to)
 {
   Elf_Data out =
   {
@@ -75,6 +68,136 @@ void getvalue (Elf *core, const void *from, size_t size, void *to)
     fprintf (stderr, "failed to get value from core file\n");
 }
 
+static void updateMapping (Mappings *mappings, uint64_t mapping_start, uint64_t mapping_end,
+    uint64_t offset, const char *name)
+{
+  int i;
+  for (i = 0; i < mappings->elems; i++)
+  {
+    if (mappings->tab[i].m_start == mapping_start)
+    {
+      mappings->tab[i].m_end = mapping_end;
+      mappings->tab[i].m_name = name;
+      mappings->tab[i].m_offset = offset;
+      mappings->tab[i].m_fd = open(name, O_RDONLY);
+      mappings->tab[i].m_elf = elf_begin(mappings->tab[i].m_fd, ELF_C_READ_MMAP, NULL);
+      return;
+    }
+  }
+}
+
+static void parse_note_file (Elf *elf, const char *desc, uint64_t *values_cnt, uint64_t *page_size,
+    size_t *addr_size, const char **values, const char **filenames)
+{
+  *addr_size = gelf_fsize (elf, ELF_T_ADDR, 1, EV_CURRENT);
+  getvalue(elf, desc, *addr_size*8, values_cnt);
+  getvalue(elf, desc + *addr_size, *addr_size*8, page_size);
+  /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
+   *     count = values_cnt
+   * Then the names of files.
+   */
+  *values = desc + 2 * *addr_size;
+  *filenames = *values + 3 * *addr_size * *values_cnt;
+}
+
+static void get_mapping_item(Elf *elf, size_t addr_size, const void *item,
+    uint64_t *mapping_start, uint64_t *mapping_end, uint64_t *offset_in_pages)
+{
+  getvalue(elf, item, addr_size*8, mapping_start);
+  getvalue(elf, item + addr_size, addr_size*8, mapping_end);
+  getvalue(elf, item + 2 * addr_size, addr_size*8, offset_in_pages);
+}
+
+static char *try_symbol_from_elfs (Elf *core, Elf_Data *notes, uintptr_t address,
+    const char **module_name)
+{
+  GElf_Nhdr nhdr;
+  char *symbol = NULL;
+  size_t pos = 0;
+  size_t new_pos = 0;
+  size_t name_pos;
+  size_t desc_pos;
+
+  while ((new_pos = gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos)) > 0)
+  {
+    if (nhdr.n_type == NT_FILE)
+    {
+      uint64_t values_cnt, page_size;
+      const char *values;
+      const char *filenames;
+      size_t addr_size;
+
+      parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size, &addr_size, &values, &filenames);
+
+      int ii;
+      for (ii = 0; ii < values_cnt; ii++)
+      {
+          uint64_t mapping_start, mapping_end, offset_in_pages;
+          const char *item = values + 3 * addr_size * ii;
+
+          get_mapping_item (core, addr_size, item, &mapping_start, &mapping_end, &offset_in_pages);
+
+          if (mapping_start <= address && address < mapping_end)
+          {
+            Elf *elf;
+            int fd;
+            fd = open(filenames, O_RDONLY);
+            if (-1 == fd)
+              return NULL;
+
+            elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+
+            if (NULL == elf)
+              return NULL;
+
+            Elf_Scn *scn = NULL;
+            *module_name = filenames;
+
+            while ((scn = elf_nextscn (elf, scn)) != NULL)
+            {
+              GElf_Shdr shdr_mem;
+              GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+              if (shdr != NULL && (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM))
+              {
+                Elf_Data *sdata = elf_getdata (scn, NULL);
+                unsigned int nsyms = sdata->d_size / (gelf_getclass(elf) == ELFCLASS32 ?
+                    sizeof (Elf32_Sym) :
+                    sizeof (Elf64_Sym));
+                unsigned int cnt;
+                uintptr_t address_offset = address;
+                if (shdr->sh_type == SHT_DYNSYM)
+                  address_offset -= mapping_start;
+                for (cnt = 0; cnt < nsyms; ++cnt)
+                {
+                  GElf_Sym sym_mem;
+                  Elf32_Word xndx;
+                  GElf_Sym *sym = gelf_getsymshndx(sdata, NULL, cnt, &sym_mem, &xndx);
+                  if (sym != NULL && sym->st_shndx != SHN_UNDEF)
+                  {
+                    if (sym->st_value <= address_offset && address_offset < sym->st_value + sym->st_size)
+                    {
+                      symbol = strdup(elf_strptr (elf, shdr->sh_link, sym->st_name));
+                      break;
+                    }
+                  }
+                }
+              }
+            }
+
+            elf_end(elf);
+            close(fd);
+            return symbol;
+          }
+
+          filenames += strlen(filenames)+1;
+      }
+    }
+    pos = new_pos;
+  }
+
+  return NULL;
+}
+
 int main(int argc, char **argv)
 {
   prctl (PR_SET_DUMPABLE, 0);
@@ -116,27 +239,30 @@ int main(int argc, char **argv)
     return 4;
   }
 
+#if _ELFUTILS_PREREQ(0,158)
   if (dwfl_core_file_report (dwfl, core, NULL) < 0)
+#else
+  if (dwfl_core_file_report (dwfl, core) < 0)
+#endif
   {
     fprintf (stderr, "%s : dwfl report failed (%s)\n", argv[1], dwfl_errmsg(-1));
     return 5;
   }
 
+#if _ELFUTILS_PREREQ(0,158)
   if (dwfl_core_file_attach (dwfl, core) < 0)
   {
     fprintf (stderr, "%s : dwfl attach failed (%s)\n", argv[1], dwfl_errmsg(-1));
     return 6;
   }
+#endif
 
-   Regs *regs = get_regs_struct();
-/*
-   To unwind with libelf do this:
-
-  dwfl_getthreads (dwfl, thread_callback, regs);
+  Regs *regs = get_regs_struct();
 
-*/
+  Mappings mappings;
+  mappings.elems = 0;
 
-  dwfl_getmodules (dwfl, module_callback, 0, 0);
+  dwfl_getmodules (dwfl, module_callback, &mappings, 0);
 
   GElf_Phdr mem;
   GElf_Phdr *phdr = gelf_getphdr (core, 0, &mem);
@@ -164,10 +290,12 @@ int main(int argc, char **argv)
   size_t name_pos;
   size_t desc_pos;
   size_t pos = 0;
+  size_t new_pos = 0;
+  int got_regs = 0;
   /* registers should be in the first note! */
-  if (gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos) > 0)
+  while ((new_pos = gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos)) > 0)
   {
-    if (nhdr.n_type == NT_PRSTATUS)
+    if (nhdr.n_type == NT_PRSTATUS && !got_regs)
     {
       GElf_Word regs_offset;
       size_t nregloc;
@@ -175,10 +303,12 @@ int main(int argc, char **argv)
       size_t nitems;
       const Ebl_Core_Item *items;
 
+      got_regs = 1;
+
       if (0 == ebl_core_note (ebl, &nhdr, "CORE", &regs_offset, &nregloc, &reglocs, &nitems, &items))
       {
         fprintf (stderr, "%s : error parsing notes\n", argv[1]);
-        return 10;
+        return 100;
       }
 
       const char *regs_location = (const char *)(notes->d_buf) + pos + desc_pos + regs_offset;
@@ -209,25 +339,43 @@ int main(int argc, char **argv)
         }
       }
     }
-  }
+    else if (nhdr.n_type == NT_FILE)
+    {
+      uint64_t values_cnt, page_size;
+      const char *values;
+      const char *filenames;
+      size_t addr_size;
+
+      parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size, &addr_size, &values, &filenames);
+//      printf("addr_size: %d, mappings: %llu, page size: %llu\n", addr_size, values_cnt, page_size);
+
+      int ii;
+      /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
+       *     count = values_cnt
+       * Then the names of files.
+       */
+      for (ii = 0; ii < values_cnt; ii++)
+      {
+          uint64_t mapping_start, mapping_end, offset_in_pages;
+          const char *item = values + 3 * addr_size * ii;
 
-/*  for (int i = 0; i < 20; i++)
-  {
-    char name[100] = {0};
-    int bits = 0, type = 0;
-    const char *setname = 0;
-    const char *prefix = 0;
-    ssize_t ret = ebl_register_info (ebl, i, name, sizeof(name), &prefix, &setname, &bits, &type);
-    printf ("ebl_register_info %d ret: %d, name: %s, prefix: %s, setname: %s, bits: %d, type: %d\n",
-        i, ret, name, prefix, setname, bits, type);
-  }
+          get_mapping_item (core, addr_size, item, &mapping_start, &mapping_end, &offset_in_pages);
+/*          printf ("<0x%08llx-0x%08llx>@0x%08llx: %s\n", mapping_start, mapping_end, offset_in_pages,
+              filenames);
 */
+          updateMapping (&mappings, mapping_start, mapping_end, offset_in_pages*page_size, filenames);
+          filenames += strlen(filenames)+1;
+      }
+    }
+    pos = new_pos;
+  }
+
 /*  printf ("PC: 0x%llx\n", (unsigned long long)regs.pc);
   printf ("SP: 0x%llx\n", (unsigned long long)regs.sp);*/
 
   Callstack callstack;
 
-  create_crash_stack (regs, dwfl, core, &callstack);
+  create_crash_stack (regs, dwfl, core, &mappings, &callstack);
 
   char *dem_buffer = NULL;
   size_t it;
@@ -244,6 +392,13 @@ int main(int argc, char **argv)
     {
       char *demangled_symbol = 0;
       const char *symbol = dwfl_module_addrname (module, callstack.tab[it]);
+      const char *fname = 0;
+      const char *module_name = dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
+      char *symbol_from_elf = 0;
+      if (symbol == NULL)
+      {
+        symbol = symbol_from_elf = try_symbol_from_elfs (core, notes, callstack.tab[it], &fname);
+      }
       if (symbol != 0 && symbol[0] == '_' && symbol[1] == 'Z')
       {
         int status = -1;
@@ -254,11 +409,16 @@ int main(int argc, char **argv)
       }
       if (symbol != 0)
         printf ("%s()", symbol);
+      else
+        printf ("<unknown>");
 
       if (demangled_symbol != 0)
         free (demangled_symbol);
 
-      printf (" from %s\n", dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
+      if (symbol_from_elf != 0)
+        free (symbol_from_elf);
+
+      printf (" from %s\n", fname != NULL ? fname : module_name);
     }
     else
     {
@@ -268,6 +428,7 @@ int main(int argc, char **argv)
 
   dwfl_report_end (dwfl, NULL, NULL);
   dwfl_end (dwfl);
+  ebl_closebackend(ebl);
   elf_end (core);
   close (core_fd);
 
index 6560d31..595a2de 100644 (file)
@@ -13,11 +13,29 @@ typedef struct Callstack
   size_t elems;
 } Callstack;
 
+typedef struct Mapping
+{
+  uintptr_t m_start;
+  uintptr_t m_end;
+  uintptr_t m_offset;
+  const char *m_name;
+  int m_fd;
+  Elf *m_elf;
+} Mapping;
+
+#define MAX_MAPPINGS_NUM 1000
+
+typedef struct Mappings
+{
+  Mapping tab[MAX_MAPPINGS_NUM];
+  size_t elems;
+} Mappings;
+
 struct Regs;
 typedef struct Regs Regs;
 
 Regs *get_regs_struct (void);
 void *get_place_for_register_value (const char *regname, int regnum);
-void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Callstack *callstack);
+void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Mappings *mappings, Callstack *callstack);
 
 #endif /* CRASH_STACK_H */