[PATCH 6/7] unwinder: plug in the DWARF unwinder

From: Jiri Slaby
Date: Fri May 05 2017 - 08:22:35 EST


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