crash-stack: unwinding by frame pointer on aarch64
[platform/core/system/crash-worker.git] / src / crash-stack / crash-stack.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <libelf.h>
4 #include <elfutils/libdwfl.h>
5 #ifdef WITH_CORE_DUMP
6 #include <elfutils/libebl.h>
7 #endif
8 #include <errno.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <sys/prctl.h>
13 #include <linux/prctl.h>
14 #include "crash-stack.h"
15 #include <string.h>
16 #include <elfutils/version.h>
17 #include <getopt.h>
18 #include <sys/ptrace.h>
19 #include <sys/uio.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22
23 static FILE *outputfile = NULL;
24 static FILE *errfile = NULL;
25
26 enum {
27         OPT_PID,
28         OPT_OUTPUTFILE,
29         OPT_ERRFILE
30 };
31
32 const struct option opts[] = {
33         { "pid", required_argument, 0, OPT_PID },
34         { "output", required_argument, 0, OPT_OUTPUTFILE },
35         { "erroutput", required_argument, 0, OPT_ERRFILE },
36         { 0, 0, 0, 0 }
37 };
38
39 extern char *__cxa_demangle(const char *mangled_name, char *output_buffer,
40                 size_t *length, int *status);
41
42 static int module_callback(Dwfl_Module *module, void **userdata,
43                 const char *name, Dwarf_Addr address,
44                 void *arg)
45 {
46         if (name != NULL && name[0] == '[') {
47                 /* libdwfl couldn't get the module file - we will get it later from notes */
48                 Mappings *mappings = arg;
49                 if (mappings->elems < MAX_MAPPINGS_NUM) {
50                         size_t elems = mappings->elems;
51                         mappings->tab[elems].m_start = address;
52                         mappings->tab[elems].m_end = 0;
53                         mappings->tab[elems].m_offset = 0;
54                         mappings->tab[elems].m_name = NULL;
55                         mappings->tab[elems].m_fd = -1;
56                         mappings->tab[elems].m_elf = 0;
57                         mappings->elems++;
58                 }
59         }
60         return DWARF_CB_OK;
61 }
62
63 static void getvalue(Elf *core, const void *from, size_t size, void *to)
64 {
65         Elf_Type type = ELF_T_BYTE;
66         switch (size) {
67         case 8: type = ELF_T_BYTE; break;
68         case 16: type = ELF_T_HALF; break;
69         case 32: type = ELF_T_WORD; break;
70         case 64: type = ELF_T_XWORD; break;
71         default:
72                  fprintf(stderr, "getvalue for strange size: %llu\n", (unsigned long long)size);
73                  break;
74         }
75         Elf_Data out = {
76                 .d_buf = to,
77                 .d_type = type,
78                 .d_version = EV_CURRENT,
79                 .d_size = size/8,
80                 .d_off = 0,
81                 .d_align = 0
82         };
83         Elf_Data in = {
84                 .d_buf = (void*)(from),
85                 .d_type = out.d_type,
86                 .d_version = out.d_version,
87                 .d_size = out.d_size,
88                 .d_off = 0,
89                 .d_align = 0
90         };
91         Elf_Data *data;
92         if (gelf_getclass(core) == ELFCLASS32)
93                 data = elf32_xlatetom(&out, &in, elf_getident(core, NULL)[EI_DATA]);
94         else
95                 data = elf64_xlatetom(&out, &in, elf_getident(core, NULL)[EI_DATA]);
96         if (data == NULL)
97                 fprintf(errfile, "failed to get value from core file\n");
98 }
99
100 static void parse_note_file(Elf *elf, const char *desc, uint64_t *values_cnt, uint64_t *page_size,
101                 size_t *addr_size, const char **values, const char **filenames)
102 {
103         *addr_size = gelf_fsize(elf, ELF_T_ADDR, 1, EV_CURRENT);
104         getvalue(elf, desc, *addr_size*8, values_cnt);
105         getvalue(elf, desc + *addr_size, *addr_size*8, page_size);
106         /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
107          *     count = values_cnt
108          * Then the names of files.
109          */
110         *values = desc + 2 * *addr_size;
111         *filenames = *values + 3 * *addr_size * *values_cnt;
112 }
113
114 static void get_mapping_item(Elf *elf, size_t addr_size, const void *item,
115                 uint64_t *mapping_start, uint64_t *mapping_end, uint64_t *offset_in_pages)
116 {
117         getvalue(elf, item, addr_size*8, mapping_start);
118         getvalue(elf, item + addr_size, addr_size*8, mapping_end);
119         getvalue(elf, item + 2 * addr_size, addr_size*8, offset_in_pages);
120 }
121
122 static char *try_symbol_from_elfs(Elf *core, Elf_Data *notes, uintptr_t address,
123                 const char **module_name)
124 {
125         GElf_Nhdr nhdr;
126         char *symbol = NULL;
127         size_t pos = 0;
128         size_t new_pos = 0;
129         size_t name_pos;
130         size_t desc_pos;
131
132         while ((new_pos = gelf_getnote(notes, pos, &nhdr, &name_pos, &desc_pos)) > 0) {
133                 if (nhdr.n_type == NT_FILE) {
134                         uint64_t values_cnt = 0, page_size = 0;
135                         const char *values;
136                         const char *filenames;
137                         size_t addr_size = 0;
138
139                         parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size, &addr_size, &values, &filenames);
140
141                         int ii;
142                         for (ii = 0; ii < values_cnt; ii++) {
143                                 uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
144                                 const char *item = values + 3 * addr_size * ii;
145
146                                 get_mapping_item(core, addr_size, item, &mapping_start, &mapping_end, &offset_in_pages);
147
148                                 if (mapping_start <= address && address < mapping_end) {
149                                         Elf *elf;
150                                         int fd;
151                                         fd = open(filenames, O_RDONLY);
152                                         if (-1 == fd)
153                                                 return NULL;
154
155                                         elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
156
157                                         if (NULL == elf) {
158                                                 close(fd);
159                                                 return NULL;
160                                         }
161
162                                         Elf_Scn *scn = NULL;
163                                         *module_name = filenames;
164
165                                         while ((scn = elf_nextscn(elf, scn)) != NULL) {
166                                                 GElf_Shdr shdr_mem;
167                                                 GElf_Shdr *shdr = gelf_getshdr(scn, &shdr_mem);
168                                                 if (shdr != NULL && (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM)) {
169                                                         Elf_Data *sdata = elf_getdata(scn, NULL);
170                                                         unsigned int nsyms = sdata->d_size / (gelf_getclass(elf) == ELFCLASS32 ?
171                                                                         sizeof(Elf32_Sym) :
172                                                                         sizeof(Elf64_Sym));
173                                                         unsigned int cnt;
174                                                         uintptr_t address_offset = address;
175                                                         if (shdr->sh_type == SHT_DYNSYM)
176                                                                 address_offset -= mapping_start;
177                                                         for (cnt = 0; cnt < nsyms; ++cnt) {
178                                                                 GElf_Sym sym_mem;
179                                                                 Elf32_Word xndx;
180                                                                 GElf_Sym *sym = gelf_getsymshndx(sdata, NULL, cnt, &sym_mem, &xndx);
181                                                                 if (sym != NULL && sym->st_shndx != SHN_UNDEF) {
182                                                                         if (sym->st_value <= address_offset && address_offset < sym->st_value + sym->st_size) {
183                                                                                 symbol = strdup(elf_strptr(elf, shdr->sh_link, sym->st_name));
184                                                                                 break;
185                                                                         }
186                                                                 }
187                                                         }
188                                                 }
189                                         }
190
191                                         elf_end(elf);
192                                         close(fd);
193                                         return symbol;
194                                 }
195
196                                 filenames += strlen(filenames)+1;
197                         }
198                 }
199                 pos = new_pos;
200         }
201
202         return NULL;
203 }
204
205 static Dwfl *open_dwfl_with_pid(pid_t pid)
206 {
207         int status;
208         pid_t stopped_pid;
209
210         if (ptrace(PTRACE_SEIZE, pid, NULL, PTRACE_O_TRACEEXIT) != 0) {
211                 fprintf(errfile, "PTRACE_SEIZE failed on PID %d: %m\n", pid);
212                 return NULL;
213         }
214
215         ptrace(PTRACE_INTERRUPT, pid, 0, 0);
216
217         stopped_pid = waitpid(pid, &status, 0);
218         if (stopped_pid == -1 || stopped_pid != pid || !WIFSTOPPED(status)) {
219                 fprintf(errfile, "waitpid failed: %m, stopped_pid=%d, status=%d\n", stopped_pid, status);
220                 return NULL;
221         }
222
223         static const Dwfl_Callbacks proc_callbacks = {
224                 .find_elf = dwfl_linux_proc_find_elf,
225                 .find_debuginfo = dwfl_standard_find_debuginfo,
226                 .section_address = NULL,
227                 .debuginfo_path = NULL
228         };
229
230         Dwfl *dwfl = dwfl_begin(&proc_callbacks);
231         if (dwfl == NULL) {
232                 fprintf(errfile, "process %d : Can't start dwfl (%s)\n", pid, dwfl_errmsg(-1));
233                 return NULL;
234         }
235
236         if (dwfl_linux_proc_report(dwfl, pid) < 0) {
237                 fprintf(errfile, "process %d : dwfl report failed (%s)\n", pid, dwfl_errmsg(-1));
238                 dwfl_end(dwfl);
239                 return NULL;
240         }
241
242 #if _ELFUTILS_PREREQ(0,158)
243         if (dwfl_linux_proc_attach(dwfl, pid, true) < 0) {
244                 fprintf(errfile, "process %d : dwfl attach failed (%s)\n", pid, dwfl_errmsg(-1));
245                 dwfl_end(dwfl);
246                 return NULL;
247         }
248 #endif
249         return dwfl;
250 }
251
252 /*
253  * This function will open core file regardless of WITH_CORE_DUMP setting.
254  * It may help with detecting issues with using dwfl even without support for dumps.
255  */
256 static Dwfl *open_dwfl_with_core(Elf *core, const char *core_file_name)
257 {
258         static const Dwfl_Callbacks core_callbacks = {
259                 .find_elf = dwfl_build_id_find_elf,
260                 .find_debuginfo = dwfl_standard_find_debuginfo,
261                 .section_address = NULL,
262                 .debuginfo_path = NULL
263         };
264
265         Dwfl *dwfl = dwfl_begin(&core_callbacks);
266         if (dwfl == NULL) {
267                 fprintf(errfile, "%s : Can't start dwfl (%s)\n", core_file_name, dwfl_errmsg(-1));
268                 return NULL;
269         }
270
271 #if _ELFUTILS_PREREQ(0,158)
272         if (dwfl_core_file_report(dwfl, core, NULL) < 0)
273 #else
274                 if (dwfl_core_file_report(dwfl, core) < 0)
275 #endif
276                 {
277                         fprintf(errfile, "%s : dwfl report failed (%s)\n", core_file_name, dwfl_errmsg(-1));
278                         dwfl_end(dwfl);
279                         return NULL;
280                 }
281
282 #if _ELFUTILS_PREREQ(0,158)
283         if (dwfl_core_file_attach(dwfl, core) < 0) {
284                 fprintf(errfile, "%s : dwfl attach failed (%s)\n", core_file_name, dwfl_errmsg(-1));
285                 dwfl_end(dwfl);
286                 return NULL;
287         }
288 #endif
289         return dwfl;
290 }
291
292 static int get_registers_ptrace(pid_t pid)
293 {
294         struct iovec data;
295         data.iov_base = crash_stack_get_memory_for_ptrace_registers( &data.iov_len );
296
297         if (NULL == data.iov_base) {
298                 fprintf(errfile, "Cannot get memory for registers for ptrace (not implemented for this architecture\n");
299                 return -1;
300         }
301
302         if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &data) != 0) {
303                 fprintf(errfile, "PTRACE_GETREGSET failed on PID %d: %m\n", pid);
304                 return -1;
305         }
306
307         crash_stack_set_ptrace_registers(data.iov_base);
308
309         return 0;
310 }
311
312 #ifdef WITH_CORE_DUMP
313 static void updateMapping(Mappings *mappings, uint64_t mapping_start, uint64_t mapping_end,
314                 uint64_t offset, const char *name)
315 {
316         int i;
317         for (i = 0; i < mappings->elems; i++) {
318                 if (mappings->tab[i].m_start == mapping_start) {
319                         mappings->tab[i].m_end = mapping_end;
320                         mappings->tab[i].m_name = name;
321                         mappings->tab[i].m_offset = offset;
322                         mappings->tab[i].m_fd = open(name, O_RDONLY);
323                         mappings->tab[i].m_elf = elf_begin(mappings->tab[i].m_fd, ELF_C_READ_MMAP, NULL);
324                         return;
325                 }
326         }
327 }
328 #endif
329
330 static Elf_Data *get_registers_core(Elf *core, const char *core_file_name, Mappings *mappings)
331 {
332         Elf_Data *notes = NULL;
333 #ifdef WITH_CORE_DUMP
334         GElf_Phdr mem;
335         GElf_Phdr *phdr = gelf_getphdr(core, 0, &mem);
336
337         if (phdr == NULL || phdr->p_type != PT_NOTE) {
338                 fprintf(errfile, "%s : Missing note section at the first position in core file\n",
339                                 core_file_name);
340                 return NULL;
341         }
342
343         notes = elf_getdata_rawchunk(core, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
344         if (notes == NULL) {
345                 fprintf(errfile, "%s : error getting notes (%s)\n", core_file_name, dwfl_errmsg(-1));
346                 return NULL;
347         }
348
349         Ebl *ebl = ebl_openbackend(core);
350         if (ebl == NULL) {
351                 fprintf(errfile, "%s : Can't initialize ebl\n", core_file_name);
352                 return NULL;
353         }
354
355         GElf_Nhdr nhdr;
356         size_t name_pos;
357         size_t desc_pos;
358         size_t pos = 0;
359         size_t new_pos = 0;
360         int got_regs = 0;
361         /* registers should be in the first note! */
362         while ((new_pos = gelf_getnote(notes, pos, &nhdr, &name_pos, &desc_pos)) > 0) {
363                 if (nhdr.n_type == NT_PRSTATUS && !got_regs) {
364                         GElf_Word regs_offset;
365                         size_t nregloc;
366                         const Ebl_Register_Location *reglocs;
367                         size_t nitems;
368                         const Ebl_Core_Item *items;
369
370                         got_regs = 1;
371
372                         if (0 == ebl_core_note(ebl, &nhdr, "CORE", &regs_offset, &nregloc,
373                                                 &reglocs, &nitems, &items)) {
374                                 fprintf(errfile,
375                                                 "%s : error parsing notes (built with different build of libebl?)\n",
376                                                 core_file_name);
377                                 return NULL;
378                         }
379
380                         const char *regs_location = (const char *)(notes->d_buf) + pos + desc_pos
381                                 + regs_offset;
382                         unsigned i;
383
384                         for (i = 0; i < nregloc; i++) {
385                                 const char *register_location = regs_location + reglocs[i].offset;
386                                 int regnum;
387                                 for (regnum = reglocs[i].regno;
388                                                 regnum < reglocs[i].regno + reglocs[i].count;
389                                                 regnum++) {
390                                         char regname[5];
391                                         int bits, type;
392                                         const char *prefix = 0;
393                                         const char *setname = 0;
394
395                                         ssize_t ret = ebl_register_info(ebl, regnum, regname,
396                                                         sizeof(regname), &prefix, &setname,
397                                                         &bits, &type);
398                                         if (ret < 0) {
399                                                 fprintf(errfile, "%s : can't get register info\n", core_file_name);
400                                                 return NULL;
401                                         }
402                                         void *place_for_reg_value = get_place_for_register_value(regname, regnum);
403
404                                         if (place_for_reg_value != NULL)
405                                                 getvalue(core, register_location, bits, place_for_reg_value);
406
407                                         register_location += bits / 8 + reglocs[i].pad;
408                                 }
409                         }
410                 } else if (nhdr.n_type == NT_FILE) {
411                         uint64_t values_cnt = 0, page_size = 0;
412                         const char *values;
413                         const char *filenames;
414                         size_t addr_size = 0;
415
416                         parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size,
417                                         &addr_size, &values, &filenames);
418
419                         int ii;
420                         /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
421                          *     count = values_cnt
422                          * Then the names of files.
423                          */
424                         for (ii = 0; ii < values_cnt; ii++) {
425                                 uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
426                                 const char *item = values + 3 * addr_size * ii;
427
428                                 get_mapping_item(core, addr_size, item, &mapping_start, &mapping_end,
429                                                 &offset_in_pages);
430                                 updateMapping(mappings, mapping_start, mapping_end,
431                                                 offset_in_pages*page_size, filenames);
432                                 filenames += strlen(filenames)+1;
433                         }
434                 }
435                 pos = new_pos;
436         }
437         ebl_closebackend(ebl);
438 #else
439         fprintf(errfile, "Configured without support for core dump files\n");
440 #endif
441         return notes;
442 }
443
444 static void printCallstack(Callstack *callstack, Dwfl *dwfl, Elf *core, pid_t pid,
445                 Elf_Data *notes)
446 {
447         fprintf(outputfile, "Call stack");
448         if (pid > 1) fprintf(outputfile, " for PID %d", pid);
449         fprintf(outputfile, ":\n");
450
451         char *dem_buffer = NULL;
452         size_t it;
453         for (it = 0; it != callstack->elems; ++it) {
454                 if (sizeof(callstack->tab[0]) > 4)
455                         fprintf(outputfile, "0x%016llx: ", (long long)callstack->tab[it]);
456                 else
457                         fprintf(outputfile, "0x%08x: ", (int32_t)callstack->tab[it]);
458                 Dwfl_Module *module = dwfl_addrmodule(dwfl, callstack->tab[it]);
459                 if (module) {
460                         char *demangled_symbol = 0;
461                         const char *symbol = dwfl_module_addrname(module, callstack->tab[it]);
462                         const char *fname = 0;
463                         const char *module_name = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
464                         char *symbol_from_elf = 0;
465                         if (symbol == NULL)
466                                 symbol = symbol_from_elf = try_symbol_from_elfs(core, notes, callstack->tab[it], &fname);
467                         if (symbol != 0 && symbol[0] == '_' && symbol[1] == 'Z') {
468                                 int status = -1;
469
470                                 demangled_symbol = __cxa_demangle(symbol, dem_buffer, NULL, &status);
471                                 if (status == 0)
472                                         symbol = demangled_symbol;
473                         }
474                         if (symbol != 0)
475                                 fprintf(outputfile, "%s()", symbol);
476                         else
477                                 fprintf(outputfile, "<unknown>");
478
479                         if (demangled_symbol != 0)
480                                 free(demangled_symbol);
481
482                         if (symbol_from_elf != 0)
483                                 free(symbol_from_elf);
484
485                         fprintf(outputfile, " from %s\n", fname != NULL ? fname : module_name);
486                 } else {
487                         fprintf(outputfile, "unknown function\n");
488                 }
489         }
490 }
491
492 int main(int argc, char **argv)
493 {
494         int c;
495         pid_t pid = 0;
496
497         const char *core_file_name;
498
499         prctl(PR_SET_DUMPABLE, 0);
500
501         while ((c = getopt_long_only(argc, argv, "", opts, NULL)) != -1) {
502                 switch (c) {
503                 case OPT_PID:
504                         pid = atoi(optarg);
505                         break;
506                 case OPT_OUTPUTFILE:
507                         outputfile = fopen(optarg, "w");
508                         break;
509                 case OPT_ERRFILE:
510                         errfile = fopen(optarg, "w");
511                         break;
512                 }
513         }
514
515         if (NULL == errfile) errfile = stderr;
516         if (NULL == outputfile) outputfile = stdout;
517
518         core_file_name = argv[optind];
519         argc -= optind;
520
521         elf_version(EV_CURRENT);
522
523         /* First, prepare dwfl and modules */
524         Elf *core = NULL;
525         int core_fd = -1;
526         Dwfl *dwfl = NULL;
527
528         if (pid > 1)
529                 dwfl = open_dwfl_with_pid(pid);
530         else {
531                 if (argc != 1) {
532                         fprintf(errfile,
533                                         "Usage: %s [--output file] [--erroutput file] [--pid <pid> | <core-file>]\n",
534                                         argv[0]);
535                         return 1;
536                 }
537
538                 core_fd = open(core_file_name, O_RDONLY);
539                 if (core_fd < 0) {
540                         perror(core_file_name);
541                         return 2;
542                 }
543
544                 core = elf_begin(core_fd, ELF_C_READ_MMAP, NULL);
545                 if (core == NULL) {
546                         fprintf(errfile, "%s : Can't open ELF (%s)\n", core_file_name, elf_errmsg(-1));
547                         return 3;
548                 }
549
550                 dwfl = open_dwfl_with_core(core, core_file_name);
551         }
552
553         if (NULL == dwfl)
554                 return 1111;
555
556         Mappings mappings;
557         mappings.elems = 0;
558
559         dwfl_getmodules(dwfl, module_callback, &mappings, 0);
560         Elf_Data *notes = 0;
561
562         /* Now, get registers */
563         if (pid > 1) {
564                 if (-1 == get_registers_ptrace(pid))
565                         return 3333;
566         } else {
567                 notes = get_registers_core(core, core_file_name, &mappings);
568                 if (NULL == notes)
569                         return 2222;
570         }
571
572         /* Unwind call stack */
573         Callstack callstack;
574
575         create_crash_stack(dwfl, core, pid, &mappings, &callstack);
576
577         /* Print the results */
578         printCallstack(&callstack, dwfl, core, pid, notes);
579
580         /* Clean up */
581         dwfl_report_end(dwfl, NULL, NULL);
582         dwfl_end(dwfl);
583         if (NULL != core) elf_end(core);
584         if (-1 != core_fd) close(core_fd);
585
586         return 0;
587 }