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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux