The following patch read the .debug_frame section of vmlinux into crash. By doing this, we could backtrace kernel stack frame using dwarf unwind information contained in that section. ChangeLog: - Modified section_header_info() function to initialize the offset and size of .debug_frame section. - Modified init_unwind_table() function to read in .debug_frame. - Modified the prototype of unwind(), adding a int argument to indicate whether .eh_frame or .debug_frame is used. Also there're a few changes due to the difference between these two sections. Signed-off-by: Wang Chao --- defs.h | 2 ++ symbols.c | 8 ++++++++ unwind_x86_32_64.c | 43 ++++++++++++++++++++++++++++--------------- unwind_x86_64.h | 2 +- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/defs.h b/defs.h index dcece4a..ec85279 100755 --- a/defs.h +++ b/defs.h @@ -1977,6 +1977,8 @@ struct symbol_table_data { struct load_module *load_modules; off_t dwarf_eh_frame_file_offset; ulong dwarf_eh_frame_size; + off_t dwarf_debug_frame_file_offset; + ulong dwarf_debug_frame_size; ulong first_ksymbol; ulong __per_cpu_start; ulong __per_cpu_end; diff --git a/symbols.c b/symbols.c index ec9c893..78fca5a 100755 --- a/symbols.c +++ b/symbols.c @@ -8271,6 +8271,10 @@ section_header_info(bfd *bfd, asection *section, void *reqptr) st->dwarf_eh_frame_file_offset = (off_t)section->filepos; st->dwarf_eh_frame_size = (ulong)bfd_section_size(bfd, section); } + if (STREQ(bfd_get_section_name(bfd, section), ".debug_frame")) { + st->dwarf_debug_frame_file_offset = (off_t)section->filepos; + st->dwarf_debug_frame_size = (ulong)bfd_section_size(bfd, section); + } break; case (ulong)MODULE_SECTIONS: @@ -8291,6 +8295,10 @@ section_header_info(bfd *bfd, asection *section, void *reqptr) st->dwarf_eh_frame_file_offset = (off_t)section->filepos; st->dwarf_eh_frame_size = (ulong)bfd_section_size(bfd, section); } + if (STREQ(bfd_get_section_name(bfd, section), ".debug_frame")) { + st->dwarf_debug_frame_file_offset = (off_t)section->filepos; + st->dwarf_debug_frame_size = (ulong)bfd_section_size(bfd, section); + } break; default: diff --git a/unwind_x86_32_64.c b/unwind_x86_32_64.c index 13d86a9..754890e 100644 --- a/unwind_x86_32_64.c +++ b/unwind_x86_32_64.c @@ -496,7 +496,7 @@ static int processCFI(const u8 *start, /* Unwind to previous to frame. Returns 0 if successful, negative * number in case of an error. */ int -unwind(struct unwind_frame_info *frame) +unwind(struct unwind_frame_info *frame, int is_ehframe) { #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) const u32 *fde = NULL, *cie = NULL; @@ -527,17 +527,22 @@ unwind(struct unwind_frame_info *frame) fde += 1 + *fde / sizeof(*fde)) { if (!*fde || (*fde & (sizeof(*fde) - 1))) break; - if (!fde[1]) + if (is_ehframe && !fde[1]) + continue; /* this is a CIE */ + else if (fde[1] == 0xffffffff) continue; /* this is a CIE */ if ((fde[1] & (sizeof(*fde) - 1)) || fde[1] > (unsigned long)(fde + 1) - (unsigned long)unwind_table) continue; /* this is not a valid FDE */ - cie = fde + 1 - fde[1] / sizeof(*fde); + if (is_ehframe) + cie = fde + 1 - fde[1] / sizeof(*fde); + else + cie = unwind_table + fde[1]; if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) || (*cie & (sizeof(*cie) - 1)) - || cie[1] + || (cie[1] != 0xffffffff && cie[1]) || (ptrType = fde_pointer_type(cie)) < 0) { cie = NULL; /* this is not a (valid) CIE */ continue; @@ -783,10 +788,13 @@ init_unwind_table(void) try_eh_frame: - if (st->dwarf_eh_frame_size) { + if (st->dwarf_eh_frame_size || st->dwarf_debug_frame_size) { int fd; + int is_ehframe = (!st->dwarf_debug_frame_size && + st->dwarf_eh_frame_size); - unwind_table_size = st->dwarf_eh_frame_size; + unwind_table_size = is_ehframe ? st->dwarf_eh_frame_size : + st->dwarf_debug_frame_size; if (!(unwind_table = malloc(unwind_table_size))) { error(WARNING, "cannot malloc unwind table space\n"); @@ -794,18 +802,21 @@ try_eh_frame: } if ((fd = open(pc->namelist, O_RDONLY)) < 0) { - error(WARNING, "cannot open %s for .eh_frame data\n", - pc->namelist); + error(WARNING, "cannot open %s for %s data\n", + pc->namelist, is_ehframe ? ".eh_frame" : ".debug_frame"); free(unwind_table); return; } - lseek(fd, st->dwarf_eh_frame_file_offset, SEEK_SET); + if (is_ehframe) + lseek(fd, st->dwarf_eh_frame_file_offset, SEEK_SET); + else + lseek(fd, st->dwarf_debug_frame_file_offset, SEEK_SET); - if (read(fd, unwind_table, st->dwarf_eh_frame_size) != - st->dwarf_eh_frame_size) { - error(WARNING, "cannot read .eh_frame data from %s\n", - pc->namelist); + if (read(fd, unwind_table, unwind_table_size) != + unwind_table_size) { + error(WARNING, "cannot read %s data from %s\n", + is_ehframe ? ".eh_frame" : ".debug_frame", pc->namelist); free(unwind_table); return; } @@ -1054,6 +1065,7 @@ dwarf_backtrace(struct bt_info *bt, int level, ulong stacktop) struct syment *sp; char *name; struct unwind_frame_info *frame; + int is_ehframe = (!st->dwarf_debug_frame_size && st->dwarf_eh_frame_size); frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info)); // frame->regs.rsp = bt->stkptr; @@ -1102,7 +1114,7 @@ dwarf_backtrace(struct bt_info *bt, int level, ulong stacktop) UNW_PC(frame), frame->regs.rbp); while ((UNW_SP(frame) < stacktop) - && !unwind(frame) && UNW_PC(frame)) { + && !unwind(frame, is_ehframe) && UNW_PC(frame)) { /* To prevent rip pushed on IRQ stack being reported both * both on the IRQ and process stacks */ @@ -1193,6 +1205,7 @@ dwarf_debug(struct bt_info *bt) { struct unwind_frame_info *frame; ulong bp; + int is_ehframe = (!st->dwarf_debug_frame_size && st->dwarf_eh_frame_size); if (!bt->hp->eip) { dump_local_unwind_tables(); @@ -1220,7 +1233,7 @@ dwarf_debug(struct bt_info *bt) sizeof(unsigned long), "reading bp", FAULT_ON_ERROR); frame->regs.rbp = bp; /* fixme for x86 */ - unwind(frame); + unwind(frame, is_ehframe); fprintf(fp, "frame size: %lx (%lx)\n", (ulong)UNW_SP(frame), (ulong)UNW_SP(frame) - bt->hp->esp); diff --git a/unwind_x86_64.h b/unwind_x86_64.h index 52fcf7a..d3fae15 100644 --- a/unwind_x86_64.h +++ b/unwind_x86_64.h @@ -54,7 +54,7 @@ struct unwind_frame_info struct pt_regs regs; }; -extern int unwind(struct unwind_frame_info *); +extern int unwind(struct unwind_frame_info *, int); extern void init_unwind_table(void); extern void free_unwind_table(void);