The DWARF unwinder is ready to use, so plug it into the generic unwinding code. Signed-off-by: Jiri Slaby <jslaby@xxxxxxx> Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: "H. Peter Anvin" <hpa@xxxxxxxxx> Cc: x86@xxxxxxxxxx Cc: Josh Poimboeuf <jpoimboe@xxxxxxxxxx> --- arch/x86/include/asm/unwind.h | 36 +++++++++++++++- arch/x86/kernel/Makefile | 4 ++ arch/x86/kernel/unwind_dwarf.c | 95 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 arch/x86/kernel/unwind_dwarf.c diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h index e6676495b125..4c792ad0d9f9 100644 --- a/arch/x86/include/asm/unwind.h +++ b/arch/x86/include/asm/unwind.h @@ -12,7 +12,14 @@ struct unwind_state { struct task_struct *task; int graph_idx; bool error; -#ifdef CONFIG_FRAME_POINTER +#ifdef CONFIG_DWARF_UNWIND + union { + struct pt_regs regs; + char regs_arr[sizeof(struct pt_regs)]; + } u; + unsigned long dw_sp; + unsigned call_frame:1; +#elif defined(CONFIG_FRAME_POINTER) bool got_irq; unsigned long *bp, *orig_sp; struct pt_regs *regs; @@ -48,7 +55,32 @@ static inline bool unwind_error(struct unwind_state *state) return state->error; } -#ifdef CONFIG_FRAME_POINTER +#ifdef CONFIG_DWARF_UNWIND + +#include <asm/dwarf.h> + +static inline +unsigned long *unwind_get_return_address_ptr(struct unwind_state *state) +{ + if (unwind_done(state)) + return NULL; + + return &DW_PC(state); +} + +static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state) +{ + if (unwind_done(state)) + return NULL; + + /* + * Should we return something? If we return the registers here, they + * are output for every frame. + */ + return NULL; +} + +#elif defined(CONFIG_FRAME_POINTER) static inline unsigned long *unwind_get_return_address_ptr(struct unwind_state *state) diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 4b994232cb57..604cbcbba9a5 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -124,11 +124,15 @@ obj-$(CONFIG_PERF_EVENTS) += perf_regs.o obj-$(CONFIG_TRACING) += tracepoint.o obj-$(CONFIG_SCHED_MC_PRIO) += itmt.o +ifdef CONFIG_DWARF_UNWIND +obj-y += unwind_dwarf.o +else ifdef CONFIG_FRAME_POINTER obj-y += unwind_frame.o else obj-y += unwind_guess.o endif +endif ### # 64 bit specific files diff --git a/arch/x86/kernel/unwind_dwarf.c b/arch/x86/kernel/unwind_dwarf.c new file mode 100644 index 000000000000..bbcca970aca8 --- /dev/null +++ b/arch/x86/kernel/unwind_dwarf.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016-2017 SUSE + * Jiri Slaby <jirislaby@xxxxxxxxxx> + * This code is released under the GPL v2. + */ + +#include <linux/dwarf.h> + +unsigned long unwind_get_return_address(struct unwind_state *state) +{ + unsigned long *addr = unwind_get_return_address_ptr(state); + + if (unwind_done(state)) + return 0; + + return ftrace_graph_ret_addr(state->task, &state->graph_idx, *addr, + addr); +} +EXPORT_SYMBOL_GPL(unwind_get_return_address); + +bool unwind_next_frame(struct unwind_state *state) +{ + if (unwind_done(state)) + goto bad; + + if (arch_dwarf_user_mode(state)) + goto bad; + + if ((state->dw_sp & PAGE_MASK) == (DW_SP(state) & PAGE_MASK) && + state->dw_sp > DW_SP(state)) + goto bad; + + if (dwarf_unwind(state) || !DW_PC(state)) + goto bad; + + state->dw_sp = DW_SP(state); + + return true; +bad: + state->stack_info.type = STACK_TYPE_UNKNOWN; + return false; +} +EXPORT_SYMBOL_GPL(unwind_next_frame); + +void __unwind_start(struct unwind_state *state, struct task_struct *task, + struct pt_regs *regs, unsigned long *first_frame) +{ + bool do_skipping = true; + char type; + + memset(state, 0, sizeof(*state)); + state->task = task; + + if (regs) { + arch_dwarf_init_frame_info(state, regs); + type = 'R'; + } else if (task == current) { + arch_dwarf_init_running(&state->u.regs); + type = 'C'; +#ifdef CONFIG_SMP + } else if (task->on_cpu) { + return; +#endif + } else { + arch_dwarf_init_blocked(state); + type = 'B'; + do_skipping = false; + } + + pr_debug("%s: %c FF=%p rip=%lx (%pS) rsp=%lx rbp=%lx\n", + __func__, type, first_frame, DW_PC(state), + (void *)DW_PC(state), DW_SP(state), DW_FP(state)); + + get_stack_info((void *)DW_SP(state), task, &state->stack_info, + &state->stack_mask); + + state->dw_sp = DW_SP(state); + + if (arch_dwarf_user_mode(state)) + return; + + while (do_skipping) { + if (DW_SP(state) > (unsigned long)first_frame) { + pr_debug("%s: hit first=%p sp=%lx\n", __func__, + first_frame, DW_SP(state)); + break; + } + if (!unwind_next_frame(state)) + break; + pr_debug("%s: skipped to %pS rsp=%lx rbp=%lx\n", __func__, + (void *)DW_PC(state), DW_SP(state), + DW_FP(state)); + } +} +EXPORT_SYMBOL_GPL(__unwind_start); -- 2.12.2 -- To unsubscribe from this list: send the line "unsubscribe live-patching" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html