static Elf *g_core = NULL;
static Dwfl *g_dwfl = NULL;
+static Mappings *g_mappings = NULL;
struct Regs
{
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);
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)
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 =
{
g_dwfl = dwfl;
g_core = core;
+ g_mappings = mappings;
callstack->tab[0] = regs->pc;
callstack->elems = 1;
#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, ®s->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 =
{
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);
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);
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;
size_t nitems;
const Ebl_Core_Item *items;
+ got_regs = 1;
+
if (0 == ebl_core_note (ebl, &nhdr, "CORE", ®s_offset, &nregloc, ®locs, &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;
}
}
}
- }
+ 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;
{
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;
}
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
{
dwfl_report_end (dwfl, NULL, NULL);
dwfl_end (dwfl);
+ ebl_closebackend(ebl);
elf_end (core);
close (core_fd);