All of lore.kernel.org
 help / color / mirror / Atom feed
From: Josh Poimboeuf <jpoimboe@redhat.com>
To: x86@kernel.org
Cc: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Andy Lutomirski <luto@kernel.org>, Jiri Slaby <jslaby@suse.cz>,
	Ingo Molnar <mingo@kernel.org>, "H. Peter Anvin" <hpa@zytor.com>,
	Peter Zijlstra <peterz@infradead.org>
Subject: [PATCH v2 8/8] x86/unwind: add undwarf unwinder
Date: Wed, 28 Jun 2017 10:11:12 -0500	[thread overview]
Message-ID: <f67778a0c75a61eba5f3f8a3016f9c0e7a09e728.1498659915.git.jpoimboe@redhat.com> (raw)
In-Reply-To: <cover.1498659915.git.jpoimboe@redhat.com>

Add a new 'undwarf' unwinder which is enabled by
CONFIG_UNDWARF_UNWINDER.  It plugs into the existing x86 unwinder
framework.

It relies on objtool to generate the needed .undwarf section.

For more details on why undwarf is used instead of DWARF, see
tools/objtool/Documentation/undwarf.txt.

Thanks to Andy Lutomirski for the performance improvement ideas:
splitting the undwarf table into two parallel arrays and creating a fast
lookup table to search a subset of the undwarf table.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 Documentation/x86/undwarf.txt     | 146 ++++++++++
 arch/um/include/asm/unwind.h      |   8 +
 arch/x86/Kconfig                  |   1 +
 arch/x86/Kconfig.debug            |  25 ++
 arch/x86/include/asm/module.h     |   9 +
 arch/x86/include/asm/unwind.h     |  77 +++--
 arch/x86/kernel/Makefile          |   8 +-
 arch/x86/kernel/module.c          |  12 +-
 arch/x86/kernel/setup.c           |   3 +
 arch/x86/kernel/unwind_frame.c    |  39 ++-
 arch/x86/kernel/unwind_guess.c    |   5 +
 arch/x86/kernel/unwind_undwarf.c  | 589 ++++++++++++++++++++++++++++++++++++++
 arch/x86/kernel/vmlinux.lds.S     |   2 +
 include/asm-generic/vmlinux.lds.h |  20 +-
 lib/Kconfig.debug                 |   3 +
 scripts/Makefile.build            |  14 +-
 16 files changed, 898 insertions(+), 63 deletions(-)
 create mode 100644 Documentation/x86/undwarf.txt
 create mode 100644 arch/um/include/asm/unwind.h
 create mode 100644 arch/x86/kernel/unwind_undwarf.c

diff --git a/Documentation/x86/undwarf.txt b/Documentation/x86/undwarf.txt
new file mode 100644
index 0000000..d76c6b4
--- /dev/null
+++ b/Documentation/x86/undwarf.txt
@@ -0,0 +1,146 @@
+Undwarf unwinder debuginfo generation
+=====================================
+
+Overview
+--------
+
+The kernel CONFIG_UNDWARF_UNWINDER option enables objtool generation of
+undwarf debuginfo, which is out-of-band data which is used by the
+in-kernel undwarf unwinder.  It's similar in concept to DWARF CFI
+debuginfo which would be used by a DWARF unwinder.  The difference is
+that the format of the undwarf data is simpler than DWARF, which in turn
+allows the unwinder to be simpler and faster.
+
+Objtool generates the undwarf data by first doing compile-time stack
+metadata validation (CONFIG_STACK_VALIDATION).  After analyzing all the
+code paths of a .o file, it determines information about the stack state
+at each instruction address in the file and outputs that information to
+the .undwarf and .undwarf_ip sections.
+
+The undwarf sections are combined at link time and are sorted at boot
+time.  The unwinder uses the resulting data to correlate instruction
+addresses with their stack states at run time.
+
+
+Undwarf vs frame pointers
+-------------------------
+
+With frame pointers enabled, GCC adds instrumentation code to every
+function in the kernel.  The kernel's .text size increases by about
+3.2%, resulting in a broad kernel-wide slowdown.  Measurements by Mel
+Gorman [1] have shown a slowdown of 5-10% for some workloads.
+
+In contrast, the undwarf unwinder has no effect on text size or runtime
+performance, because the debuginfo is out of band.  So if you disable
+frame pointers and enable undwarf, you get a nice performance
+improvement across the board, and still have reliable stack traces.
+
+Another benefit of undwarf compared to frame pointers is that it can
+reliably unwind across interrupts and exceptions.  Frame pointer based
+unwinds can skip the caller of the interrupted function if it was a leaf
+function or if the interrupt hit before the frame pointer was saved.
+
+The main disadvantage of undwarf compared to frame pointers is that it
+needs more memory to store the undwarf table: roughly 2-4MB depending on
+the kernel config.
+
+
+Undwarf vs DWARF
+----------------
+
+Undwarf debuginfo's advantage over DWARF itself is that it's much
+simpler.  It gets rid of the complex DWARF CFI state machine and also
+gets rid of the tracking of unnecessary registers.  This allows the
+unwinder to be much simpler, meaning fewer bugs, which is especially
+important for mission critical oops code.
+
+The simpler debuginfo format also enables the unwinder to be much faster
+than DWARF, which is important for perf and lockdep.  In a basic
+performance test by Jiri Slaby [2], the undwarf unwinder was about 20x
+faster than an out-of-tree DWARF unwinder.  (Note: that measurement was
+taken before some performance tweaks were implemented, so the speedup
+may be even higher.)
+
+The undwarf format does have a few downsides compared to DWARF.  The
+undwarf table takes up ~2MB more memory than an DWARF .eh_frame table.
+
+Another potential downside is that, as GCC evolves, it's conceivable
+that the undwarf data may end up being *too* simple to describe the
+state of the stack for certain optimizations.  But IMO this is unlikely
+because GCC saves the frame pointer for any unusual stack adjustments it
+does, so I suspect we'll really only ever need to keep track of the
+stack pointer and the frame pointer between call frames.  But even if we
+do end up having to track all the registers DWARF tracks, at least we
+will still be able to control the format, e.g.  no complex state
+machines.
+
+
+Undwarf debuginfo generation
+----------------------------
+
+The undwarf data is generated by objtool.  With the existing
+compile-time stack metadata validation feature, objtool already follows
+all code paths, and so it already has all the information it needs to be
+able to generate undwarf data from scratch.  So it's an easy step to go
+from stack validation to undwarf generation.
+
+It should be possible to instead generate the undwarf data with a simple
+tool which converts DWARF to undwarf.  However, such a solution would be
+incomplete due to the kernel's extensive use of asm, inline asm, and
+special sections like exception tables.
+
+That could be rectified by manually annotating those special code paths
+using GNU assembler .cfi annotations in .S files, and homegrown
+annotations for inline asm in .c files.  But asm annotations were tried
+in the past and were found to be unmaintainable.  They were often
+incorrect/incomplete and made the code harder to read and keep updated.
+And based on looking at glibc code, annotating inline asm in .c files
+might be even worse.
+
+Objtool still needs a few annotations, but only in code which does
+unusual things to the stack like entry code.  And even then, far fewer
+annotations are needed than what DWARF would need, so they're much more
+maintainable than DWARF CFI annotations.
+
+So the advantages of using objtool to generate undwarf are that it gives
+more accurate debuginfo, with very few annotations.  It also insulates
+the kernel from toolchain bugs which can be very painful to deal with in
+the kernel since we often have to workaround issues in older versions of
+the toolchain for years.
+
+The downside is that the unwinder now becomes dependent on objtool's
+ability to reverse engineer GCC code paths.  If GCC optimizations become
+too complicated for objtool to follow, the undwarf generation might stop
+working or become incomplete.  (It's worth noting that livepatch already
+has such a dependency on objtool's ability to follow GCC code paths.)
+
+If newer versions of GCC come up with some optimizations which break
+objtool, we may need to revisit the current implementation.  Some
+possible solutions would be asking GCC to make the optimizations more
+palatable, or having objtool use DWARF as an additional input, or
+creating a GCC plugin to assist objtool with its analysis.  But for now,
+objtool follows GCC code quite well.
+
+
+Unwinder implementation details
+-------------------------------
+
+Objtool generates the undwarf data by integrating with the compile-time
+stack metadata validation feature, which is described in detail in
+tools/objtool/Documentation/stack-validation.txt.  After analyzing all
+the code paths of a .o file, it creates an array of undwarf structs, and
+a parallel array of instruction addresses associated with those structs,
+and writes them to the .undwarf and .undwarf_ip sections respectively.
+
+The undwarf data is split into the two arrays for performance reasons,
+to make the searchable part of the data (.undwarf_ip) more compact.  The
+arrays are sorted in parallel at boot time.
+
+Performance is further improved by the use of a fast lookup table which
+is created at runtime.  The fast lookup table associates a given address
+with a range of undwarf table indices, so that only a small subset of
+the undwarf table needs to be searched.
+
+
+[1] https://lkml.kernel.org/r/20170602104048.jkkzssljsompjdwy@suse.de
+[2] https://lkml.kernel.org/r/d2ca5435-6386-29b8-db87-7f227c2b713a@suse.cz
diff --git a/arch/um/include/asm/unwind.h b/arch/um/include/asm/unwind.h
new file mode 100644
index 0000000..53f507c
--- /dev/null
+++ b/arch/um/include/asm/unwind.h
@@ -0,0 +1,8 @@
+#ifndef _ASM_UML_UNWIND_H
+#define _ASM_UML_UNWIND_H
+
+static inline void
+unwind_module_init(struct module *mod, void *undwarf_ip, size_t unward_ip_size,
+		   void *undwarf, size_t undwarf_size) {}
+
+#endif /* _ASM_UML_UNWIND_H */
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 72028a1..adf3222 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -153,6 +153,7 @@ config X86
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP
 	select HAVE_MIXED_BREAKPOINTS_REGS
+	select HAVE_MOD_ARCH_SPECIFIC
 	select HAVE_NMI
 	select HAVE_OPROFILE
 	select HAVE_OPTPROBES
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index fcb7604..995434c 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -357,4 +357,29 @@ config PUNIT_ATOM_DEBUG
 	  The current power state can be read from
 	  /sys/kernel/debug/punit_atom/dev_power_state
 
+config UNDWARF_UNWINDER
+	bool "undwarf unwinder"
+	depends on X86_64
+	select STACK_VALIDATION
+	---help---
+	  This option enables the "undwarf" unwinder for unwinding kernel stack
+	  traces.  It uses a custom data format which is a simplified version
+	  of the DWARF Call Frame Information standard.
+
+	  This unwinder is more accurate across interrupt entry frames than the
+	  frame pointer unwinder.  This also can enable a small performance
+	  improvement across the entire kernel if CONFIG_FRAME_POINTER is
+	  disabled.
+
+	  Enabling this option will increase the kernel's runtime memory usage
+	  by roughly 2-4MB, depending on your kernel config.
+
+config FRAME_POINTER_UNWINDER
+	def_bool y
+	depends on !UNDWARF_UNWINDER && FRAME_POINTER
+
+config GUESS_UNWINDER
+	def_bool y
+	depends on !UNDWARF_UNWINDER && !FRAME_POINTER
+
 endmenu
diff --git a/arch/x86/include/asm/module.h b/arch/x86/include/asm/module.h
index e3b7819..4dc6427 100644
--- a/arch/x86/include/asm/module.h
+++ b/arch/x86/include/asm/module.h
@@ -2,6 +2,15 @@
 #define _ASM_X86_MODULE_H
 
 #include <asm-generic/module.h>
+#include <asm/undwarf.h>
+
+struct mod_arch_specific {
+#ifdef CONFIG_UNDWARF_UNWINDER
+	unsigned int num_undwarves;
+	int *undwarf_ip;
+	struct undwarf *undwarf;
+#endif
+};
 
 #ifdef CONFIG_X86_64
 /* X86_64 does not define MODULE_PROC_FAMILY */
diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h
index e667649..1f8cb78 100644
--- a/arch/x86/include/asm/unwind.h
+++ b/arch/x86/include/asm/unwind.h
@@ -12,11 +12,14 @@ struct unwind_state {
 	struct task_struct *task;
 	int graph_idx;
 	bool error;
-#ifdef CONFIG_FRAME_POINTER
+#if defined(CONFIG_UNDWARF_UNWINDER)
+	bool signal, full_regs;
+	unsigned long sp, bp, ip;
+	struct pt_regs *regs;
+#elif defined(CONFIG_FRAME_POINTER)
 	bool got_irq;
-	unsigned long *bp, *orig_sp;
+	unsigned long *bp, *orig_sp, ip;
 	struct pt_regs *regs;
-	unsigned long ip;
 #else
 	unsigned long *sp;
 #endif
@@ -24,41 +27,30 @@ struct unwind_state {
 
 void __unwind_start(struct unwind_state *state, struct task_struct *task,
 		    struct pt_regs *regs, unsigned long *first_frame);
-
 bool unwind_next_frame(struct unwind_state *state);
-
 unsigned long unwind_get_return_address(struct unwind_state *state);
+unsigned long *unwind_get_return_address_ptr(struct unwind_state *state);
 
 static inline bool unwind_done(struct unwind_state *state)
 {
 	return state->stack_info.type == STACK_TYPE_UNKNOWN;
 }
 
-static inline
-void unwind_start(struct unwind_state *state, struct task_struct *task,
-		  struct pt_regs *regs, unsigned long *first_frame)
-{
-	first_frame = first_frame ? : get_stack_pointer(task, regs);
-
-	__unwind_start(state, task, regs, first_frame);
-}
-
 static inline bool unwind_error(struct unwind_state *state)
 {
 	return state->error;
 }
 
-#ifdef CONFIG_FRAME_POINTER
-
 static inline
-unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
+void unwind_start(struct unwind_state *state, struct task_struct *task,
+		  struct pt_regs *regs, unsigned long *first_frame)
 {
-	if (unwind_done(state))
-		return NULL;
+	first_frame = first_frame ? : get_stack_pointer(task, regs);
 
-	return state->regs ? &state->regs->ip : state->bp + 1;
+	__unwind_start(state, task, regs, first_frame);
 }
 
+#if defined(CONFIG_UNDWARF_UNWINDER) || defined(CONFIG_FRAME_POINTER)
 static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
 {
 	if (unwind_done(state))
@@ -66,20 +58,47 @@ static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
 
 	return state->regs;
 }
-
-#else /* !CONFIG_FRAME_POINTER */
-
-static inline
-unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
+#else
+static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
 {
 	return NULL;
 }
+#endif
 
-static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
+#ifdef CONFIG_UNDWARF_UNWINDER
+void unwind_init(void);
+void unwind_module_init(struct module *mod, void *undwarf_ip,
+			size_t undwarf_ip_size, void *undwarf,
+			size_t undwarf_size);
+#else
+static inline void unwind_init(void) {}
+static inline void unwind_module_init(struct module *mod, void *undwarf_ip,
+				      size_t undwarf_ip_size, void *undwarf,
+				      size_t undwarf_size) {}
+#endif
+
+/*
+ * This disables KASAN checking when reading a value from another task's stack,
+ * since the other task could be running on another CPU and could have poisoned
+ * the stack in the meantime.
+ */
+#define READ_ONCE_TASK_STACK(task, x)			\
+({							\
+	unsigned long val;				\
+	if (task == current)				\
+		val = READ_ONCE(x);			\
+	else						\
+		val = READ_ONCE_NOCHECK(x);		\
+	val;						\
+})
+
+static inline bool task_on_another_cpu(struct task_struct *task)
 {
-	return NULL;
+#ifdef CONFIG_SMP
+	return task != current && task->on_cpu;
+#else
+	return false;
+#endif
 }
 
-#endif /* CONFIG_FRAME_POINTER */
-
 #endif /* _ASM_X86_UNWIND_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 3c7c419..4865889 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -125,11 +125,9 @@ obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o
 obj-$(CONFIG_TRACING)			+= tracepoint.o
 obj-$(CONFIG_SCHED_MC_PRIO)		+= itmt.o
 
-ifdef CONFIG_FRAME_POINTER
-obj-y					+= unwind_frame.o
-else
-obj-y					+= unwind_guess.o
-endif
+obj-$(CONFIG_UNDWARF_UNWINDER)		+= unwind_undwarf.o
+obj-$(CONFIG_FRAME_POINTER_UNWINDER)	+= unwind_frame.o
+obj-$(CONFIG_GUESS_UNWINDER)		+= unwind_guess.o
 
 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c
index f67bd32..203b5a7 100644
--- a/arch/x86/kernel/module.c
+++ b/arch/x86/kernel/module.c
@@ -35,6 +35,7 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/setup.h>
+#include <asm/unwind.h>
 
 #if 0
 #define DEBUGP(fmt, ...)				\
@@ -213,7 +214,7 @@ int module_finalize(const Elf_Ehdr *hdr,
 		    struct module *me)
 {
 	const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL,
-		*para = NULL;
+		*para = NULL, *undwarf = NULL, *undwarf_ip = NULL;
 	char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 
 	for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
@@ -225,6 +226,10 @@ int module_finalize(const Elf_Ehdr *hdr,
 			locks = s;
 		if (!strcmp(".parainstructions", secstrings + s->sh_name))
 			para = s;
+		if (!strcmp(".undwarf", secstrings + s->sh_name))
+			undwarf = s;
+		if (!strcmp(".undwarf_ip", secstrings + s->sh_name))
+			undwarf_ip = s;
 	}
 
 	if (alt) {
@@ -248,6 +253,11 @@ int module_finalize(const Elf_Ehdr *hdr,
 	/* make jump label nops */
 	jump_label_apply_nops(me);
 
+	if (undwarf && undwarf_ip)
+		unwind_module_init(me, (void *)undwarf_ip->sh_addr,
+				   undwarf_ip->sh_size,
+				   (void *)undwarf->sh_addr, undwarf->sh_size);
+
 	return 0;
 }
 
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 65622f0..d736761 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -115,6 +115,7 @@
 #include <asm/microcode.h>
 #include <asm/mmu_context.h>
 #include <asm/kaslr.h>
+#include <asm/unwind.h>
 
 /*
  * max_low_pfn_mapped: highest direct mapped pfn under 4GB
@@ -1303,6 +1304,8 @@ void __init setup_arch(char **cmdline_p)
 	if (efi_enabled(EFI_BOOT))
 		efi_apply_memmap_quirks();
 #endif
+
+	unwind_init();
 }
 
 #ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c
index b9389d7..7574ef5 100644
--- a/arch/x86/kernel/unwind_frame.c
+++ b/arch/x86/kernel/unwind_frame.c
@@ -10,20 +10,22 @@
 
 #define FRAME_HEADER_SIZE (sizeof(long) * 2)
 
-/*
- * This disables KASAN checking when reading a value from another task's stack,
- * since the other task could be running on another CPU and could have poisoned
- * the stack in the meantime.
- */
-#define READ_ONCE_TASK_STACK(task, x)			\
-({							\
-	unsigned long val;				\
-	if (task == current)				\
-		val = READ_ONCE(x);			\
-	else						\
-		val = READ_ONCE_NOCHECK(x);		\
-	val;						\
-})
+unsigned long unwind_get_return_address(struct unwind_state *state)
+{
+	if (unwind_done(state))
+		return 0;
+
+	return __kernel_text_address(state->ip) ? state->ip : 0;
+}
+EXPORT_SYMBOL_GPL(unwind_get_return_address);
+
+unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
+{
+	if (unwind_done(state))
+		return NULL;
+
+	return state->regs ? &state->regs->ip : state->bp + 1;
+}
 
 static void unwind_dump(struct unwind_state *state)
 {
@@ -66,15 +68,6 @@ static void unwind_dump(struct unwind_state *state)
 	}
 }
 
-unsigned long unwind_get_return_address(struct unwind_state *state)
-{
-	if (unwind_done(state))
-		return 0;
-
-	return __kernel_text_address(state->ip) ? state->ip : 0;
-}
-EXPORT_SYMBOL_GPL(unwind_get_return_address);
-
 static size_t regs_size(struct pt_regs *regs)
 {
 	/* x86_32 regs from kernel mode are two words shorter: */
diff --git a/arch/x86/kernel/unwind_guess.c b/arch/x86/kernel/unwind_guess.c
index 039f367..4f0e17b 100644
--- a/arch/x86/kernel/unwind_guess.c
+++ b/arch/x86/kernel/unwind_guess.c
@@ -19,6 +19,11 @@ unsigned long unwind_get_return_address(struct unwind_state *state)
 }
 EXPORT_SYMBOL_GPL(unwind_get_return_address);
 
+unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
+{
+	return NULL;
+}
+
 bool unwind_next_frame(struct unwind_state *state)
 {
 	struct stack_info *info = &state->stack_info;
diff --git a/arch/x86/kernel/unwind_undwarf.c b/arch/x86/kernel/unwind_undwarf.c
new file mode 100644
index 0000000..44f62af
--- /dev/null
+++ b/arch/x86/kernel/unwind_undwarf.c
@@ -0,0 +1,589 @@
+#include <linux/module.h>
+#include <linux/sort.h>
+#include <asm/ptrace.h>
+#include <asm/stacktrace.h>
+#include <asm/unwind.h>
+#include <asm/undwarf.h>
+#include <asm/sections.h>
+
+#define undwarf_warn(fmt, ...) \
+	printk_deferred_once(KERN_WARNING pr_fmt("WARNING: " fmt), ##__VA_ARGS__)
+
+extern int __start_undwarf_ip[];
+extern int __stop_undwarf_ip[];
+extern struct undwarf __start_undwarf[];
+extern struct undwarf __stop_undwarf[];
+
+bool undwarf_init;
+static DEFINE_MUTEX(sort_mutex);
+
+int *cur_undwarf_ip_table = __start_undwarf_ip;
+struct undwarf *cur_undwarf_table = __start_undwarf;
+
+/*
+ * This is a lookup table for speeding up access to the undwarf table.  Given
+ * an input address offset, the corresponding lookup table entry specifies a
+ * subset of the undwarf table to search.
+ *
+ * Each block represents the end of the previous range and the start of the
+ * next range.  An extra block is added to give the last range an end.
+ *
+ * Some measured performance results for different values of LOOKUP_NUM_BLOCKS:
+ *
+ *  num blocks       array size	   lookup speedup   total speedup
+ *     2k		8k		1.5x		1.5x
+ *     4k		16k		1.6x		1.6x
+ *     8k		32k		1.8x		1.7x
+ *     16k		64k		2.0x		1.8x
+ *     32k		128k		2.5x		2.0x
+ *     64k		256k		2.9x		2.2x
+ *     128k		512k		3.3x		2.4x
+ *
+ * Go with 32k blocks because it doubles unwinder performance while only adding
+ * 3.5% to the undwarf data footprint.
+ */
+#define LOOKUP_NUM_BLOCKS		(32 * 1024)
+static unsigned int undwarf_fast_lookup[LOOKUP_NUM_BLOCKS + 1] __ro_after_init;
+
+#define LOOKUP_START_IP			(unsigned long)_stext
+#define LOOKUP_STOP_IP			(unsigned long)_etext
+#define LOOKUP_BLOCK_SIZE						\
+	(DIV_ROUND_UP(LOOKUP_STOP_IP - LOOKUP_START_IP,			\
+		      LOOKUP_NUM_BLOCKS))
+
+
+static inline unsigned long undwarf_ip(const int *ip)
+{
+	return (unsigned long)ip + *ip;
+}
+
+static struct undwarf *__undwarf_find(int *ip_table, struct undwarf *u_table,
+				      unsigned int num_entries,
+				      unsigned long ip)
+{
+	int *first = ip_table;
+	int *last = ip_table + num_entries - 1;
+	int *mid = first, *found = first;
+
+	if (!num_entries)
+		return NULL;
+
+	/*
+	 * Do a binary range search to find the rightmost duplicate of a given
+	 * starting address.  Some entries are section terminators which are
+	 * "weak" entries for ensuring there are no gaps.  They should be
+	 * ignored when they conflict with a real entry.
+	 */
+	while (first <= last) {
+		mid = first + ((last - first) / 2);
+
+		if (undwarf_ip(mid) <= ip) {
+			found = mid;
+			first = mid + 1;
+		} else
+			last = mid - 1;
+	}
+
+	return u_table + (found - ip_table);
+}
+
+static struct undwarf *undwarf_find(unsigned long ip)
+{
+	struct module *mod;
+
+	if (!undwarf_init)
+		return NULL;
+
+	/* For non-init vmlinux addresses, use the fast lookup table: */
+	if (ip >= LOOKUP_START_IP && ip < LOOKUP_STOP_IP) {
+		unsigned int idx, start, stop;
+
+		idx = (ip - LOOKUP_START_IP) / LOOKUP_BLOCK_SIZE;
+
+		if (WARN_ON_ONCE(idx >= LOOKUP_NUM_BLOCKS))
+			return NULL;
+
+		start = undwarf_fast_lookup[idx];
+		stop = undwarf_fast_lookup[idx + 1] + 1;
+
+		if (WARN_ON_ONCE(__start_undwarf + start >= __stop_undwarf) ||
+				 __start_undwarf + stop > __stop_undwarf)
+			return NULL;
+
+		return __undwarf_find(__start_undwarf_ip + start,
+				      __start_undwarf + start,
+				      stop - start, ip);
+	}
+
+	/* vmlinux .init slow lookup: */
+	if (ip >= (unsigned long)_sinittext && ip < (unsigned long)_einittext)
+		return __undwarf_find(__start_undwarf_ip, __start_undwarf,
+				      __stop_undwarf - __start_undwarf, ip);
+
+	/* Module lookup: */
+	mod = __module_address(ip);
+	if (!mod || !mod->arch.undwarf || !mod->arch.undwarf_ip)
+		return NULL;
+	return __undwarf_find(mod->arch.undwarf_ip, mod->arch.undwarf,
+			      mod->arch.num_undwarves, ip);
+}
+
+static void undwarf_sort_swap(void *_a, void *_b, int size)
+{
+	struct undwarf *undwarf_a, *undwarf_b;
+	struct undwarf undwarf_tmp;
+	int *a = _a, *b = _b, tmp;
+	int delta = _b - _a;
+
+	/* Swap the undwarf_ip entries: */
+	tmp = *a;
+	*a = *b + delta;
+	*b = tmp - delta;
+
+	/* Swap the corresponding undwarf entries: */
+	undwarf_a = cur_undwarf_table + (a - cur_undwarf_ip_table);
+	undwarf_b = cur_undwarf_table + (b - cur_undwarf_ip_table);
+	undwarf_tmp = *undwarf_a;
+	*undwarf_a = *undwarf_b;
+	*undwarf_b = undwarf_tmp;
+}
+
+static int undwarf_sort_cmp(const void *_a, const void *_b)
+{
+	struct undwarf *undwarf_a;
+	const int *a = _a, *b = _b;
+	unsigned long a_val = undwarf_ip(a);
+	unsigned long b_val = undwarf_ip(b);
+
+	if (a_val > b_val)
+		return 1;
+	if (a_val < b_val)
+		return -1;
+
+	/*
+	 * The "weak" section terminator entries need to always be on the left
+	 * to ensure the lookup code skips them in favor of real entries.
+	 * These terminator entries exist to handle any gaps created by
+	 * whitelisted .o files which didn't get objtool generation.
+	 */
+	undwarf_a = cur_undwarf_table + (a - cur_undwarf_ip_table);
+	return undwarf_a->cfa_reg == UNDWARF_REG_UNDEFINED ? -1 : 1;
+}
+
+void unwind_module_init(struct module *mod, void *_undwarf_ip,
+			size_t undwarf_ip_size, void *_undwarf,
+			size_t undwarf_size)
+{
+	int *undwarf_ip = _undwarf_ip;
+	struct undwarf *undwarf = _undwarf;
+	unsigned int num_entries = undwarf_ip_size / sizeof(int);
+
+	WARN_ON_ONCE(undwarf_ip_size % sizeof(int) != 0 ||
+		     undwarf_size % sizeof(*undwarf) != 0 ||
+		     num_entries != undwarf_size / sizeof(*undwarf));
+
+	/*
+	 * The 'cur_undwarf_*' globals allow the undwarf_sort_swap() callback
+	 * to associate an undwarf_ip table entry with its corresponding
+	 * undwarf entry so they can both be swapped.
+	 */
+	mutex_lock(&sort_mutex);
+	cur_undwarf_ip_table = undwarf_ip;
+	cur_undwarf_table = undwarf;
+	sort(undwarf_ip, num_entries, sizeof(int),undwarf_sort_cmp,
+	     undwarf_sort_swap);
+	mutex_unlock(&sort_mutex);
+
+	mod->arch.undwarf_ip = undwarf_ip;
+	mod->arch.undwarf = undwarf;
+	mod->arch.num_undwarves = num_entries;
+}
+
+void __init unwind_init(void)
+{
+	size_t undwarf_ip_size = (void *)__stop_undwarf_ip - (void *)__start_undwarf_ip;
+	size_t undwarf_size = (void *)__stop_undwarf - (void *)__start_undwarf;
+	size_t num_entries = undwarf_ip_size / sizeof(int);
+	struct undwarf *undwarf;
+	int i;
+
+	if (!num_entries || undwarf_ip_size % sizeof(int) != 0 ||
+	    undwarf_size % sizeof(struct undwarf) != 0 ||
+	    num_entries != undwarf_size / sizeof(struct undwarf)) {
+		pr_warn("WARNING: Bad or missing undwarf table.  Disabling unwinder.\n");
+		return;
+	}
+
+	/* Sort the undwarf table: */
+	sort(__start_undwarf_ip, num_entries, sizeof(int), undwarf_sort_cmp,
+	     undwarf_sort_swap);
+
+	/* Initialize the fast lookup table: */
+	for (i = 0; i < LOOKUP_NUM_BLOCKS; i++) {
+		undwarf = __undwarf_find(__start_undwarf_ip, __start_undwarf,
+					 num_entries,
+					 LOOKUP_START_IP + (LOOKUP_BLOCK_SIZE * i));
+		if (!undwarf) {
+			pr_warn("WARNING: Corrupt undwarf table.  Disabling unwinder.\n");
+			return;
+		}
+
+		undwarf_fast_lookup[i] = undwarf - __start_undwarf;
+	}
+
+	/* Initialize the last 'end' block: */
+	undwarf = __undwarf_find(__start_undwarf_ip, __start_undwarf,
+				 num_entries, LOOKUP_STOP_IP);
+	if (!undwarf) {
+		pr_warn("WARNING: Corrupt undwarf table.  Disabling unwinder.\n");
+		return;
+	}
+	undwarf_fast_lookup[LOOKUP_NUM_BLOCKS] = undwarf - __start_undwarf;
+
+	undwarf_init = true;
+}
+
+unsigned long unwind_get_return_address(struct unwind_state *state)
+{
+	if (unwind_done(state))
+		return 0;
+
+	return __kernel_text_address(state->ip) ? state->ip : 0;
+}
+EXPORT_SYMBOL_GPL(unwind_get_return_address);
+
+unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
+{
+	if (unwind_done(state))
+		return NULL;
+
+	if (state->regs)
+		return &state->regs->ip;
+
+	if (state->sp)
+		return (unsigned long *)state->sp - 1;
+
+	return NULL;
+}
+
+static bool stack_access_ok(struct unwind_state *state, unsigned long addr,
+			    size_t len)
+{
+	struct stack_info *info = &state->stack_info;
+
+	/*
+	 * If the address isn't on the current stack, switch to the next one.
+	 *
+	 * We may have to traverse multiple stacks to deal with the possibility
+	 * that info->next_sp could point to an empty stack and the address
+	 * could be on a subsequent stack.
+	 */
+	while (!on_stack(info, (void *)addr, len))
+		if (get_stack_info(info->next_sp, state->task, info,
+				   &state->stack_mask))
+			return false;
+
+	return true;
+}
+
+static bool deref_stack_reg(struct unwind_state *state, unsigned long addr,
+			    unsigned long *val)
+{
+	if (!stack_access_ok(state, addr, sizeof(long)))
+		return false;
+
+	*val = READ_ONCE_TASK_STACK(state->task, *(unsigned long *)addr);
+	return true;
+}
+
+#define REGS_SIZE (sizeof(struct pt_regs))
+#define SP_OFFSET (offsetof(struct pt_regs, sp))
+#define IRET_REGS_SIZE (REGS_SIZE - offsetof(struct pt_regs, ip))
+#define IRET_SP_OFFSET (SP_OFFSET - offsetof(struct pt_regs, ip))
+
+static bool deref_stack_regs(struct unwind_state *state, unsigned long addr,
+			     unsigned long *ip, unsigned long *sp, bool full)
+{
+	size_t regs_size = full ? REGS_SIZE : IRET_REGS_SIZE;
+	size_t sp_offset = full ? SP_OFFSET : IRET_SP_OFFSET;
+	struct pt_regs *regs = (struct pt_regs *)(addr + regs_size - REGS_SIZE);
+
+	if (IS_ENABLED(CONFIG_X86_64)) {
+		if (!stack_access_ok(state, addr, regs_size))
+			return false;
+
+		*ip = regs->ip;
+		*sp = regs->sp;
+
+		return true;
+	}
+
+	if (!stack_access_ok(state, addr, sp_offset))
+		return false;
+
+	*ip = regs->ip;
+
+	if (user_mode(regs)) {
+		if (!stack_access_ok(state, addr + sp_offset,
+				     REGS_SIZE - SP_OFFSET))
+			return false;
+
+		*sp = regs->sp;
+	} else
+		*sp = (unsigned long)&regs->sp;
+
+	return true;
+}
+
+bool unwind_next_frame(struct unwind_state *state)
+{
+	enum stack_type prev_type = state->stack_info.type;
+	unsigned long ip_p, prev_sp = state->sp;
+	unsigned long cfa, orig_ip, orig_sp;
+	struct undwarf *undwarf;
+	struct pt_regs *ptregs;
+	bool indirect = false;
+
+	if (unwind_done(state))
+		return false;
+
+	/* Don't let modules unload while we're reading their undwarf data. */
+	preempt_disable();
+
+	/* Have we reached the end? */
+	if (state->regs && user_mode(state->regs))
+		goto done;
+
+	/*
+	 * Find the undwarf table entry associated with the text address.
+	 *
+	 * Decrement call return addresses by one so they work for sibling
+	 * calls and calls to noreturn functions.
+	 */
+	undwarf = undwarf_find(state->signal ? state->ip : state->ip - 1);
+	if (!undwarf || undwarf->cfa_reg == UNDWARF_REG_UNDEFINED)
+		goto done;
+	orig_ip = state->ip;
+
+	/* Calculate the CFA (caller frame address): */
+	switch (undwarf->cfa_reg) {
+	case UNDWARF_REG_SP:
+		cfa = state->sp + undwarf->cfa_offset;
+		break;
+
+	case UNDWARF_REG_BP:
+		cfa = state->bp + undwarf->cfa_offset;
+		break;
+
+	case UNDWARF_REG_SP_INDIRECT:
+		cfa = state->sp + undwarf->cfa_offset;
+		indirect = true;
+		break;
+
+	case UNDWARF_REG_BP_INDIRECT:
+		cfa = state->bp + undwarf->cfa_offset;
+		indirect = true;
+		break;
+
+	case UNDWARF_REG_R10:
+		if (!state->regs || !state->full_regs) {
+			undwarf_warn("missing regs for base reg R10 at ip %p\n",
+				     (void *)state->ip);
+			goto done;
+		}
+		cfa = state->regs->r10;
+		break;
+
+	case UNDWARF_REG_R13:
+		if (!state->regs || !state->full_regs) {
+			undwarf_warn("missing regs for base reg R13 at ip %p\n",
+				     (void *)state->ip);
+			goto done;
+		}
+		cfa = state->regs->r13;
+		break;
+
+	case UNDWARF_REG_DI:
+		if (!state->regs || !state->full_regs) {
+			undwarf_warn("missing regs for base reg DI at ip %p\n",
+				     (void *)state->ip);
+			goto done;
+		}
+		cfa = state->regs->di;
+		break;
+
+	case UNDWARF_REG_DX:
+		if (!state->regs || !state->full_regs) {
+			undwarf_warn("missing regs for base reg DX at ip %p\n",
+				     (void *)state->ip);
+			goto done;
+		}
+		cfa = state->regs->dx;
+		break;
+
+	default:
+		undwarf_warn("unknown CFA base reg %d for ip %p\n",
+			     undwarf->cfa_reg, (void *)state->ip);
+		goto done;
+	}
+
+	if (indirect) {
+		if (!deref_stack_reg(state, cfa, &cfa))
+			goto done;
+	}
+
+	/* Find IP, SP and possibly regs: */
+	switch (undwarf->type) {
+	case UNDWARF_TYPE_CFA:
+		ip_p = cfa - sizeof(long);
+
+		if (!deref_stack_reg(state, ip_p, &state->ip))
+			goto done;
+
+		state->ip = ftrace_graph_ret_addr(state->task, &state->graph_idx,
+						  state->ip, (void *)ip_p);
+
+		state->sp = cfa;
+		state->regs = NULL;
+		state->signal = false;
+		break;
+
+	case UNDWARF_TYPE_REGS:
+		if (!deref_stack_regs(state, cfa, &state->ip, &state->sp, true)) {
+			undwarf_warn("can't dereference registers at %p for ip %p\n",
+				     (void *)cfa, (void *)orig_ip);
+			goto done;
+		}
+
+		state->regs = (struct pt_regs *)cfa;
+		state->full_regs = true;
+		state->signal = true;
+		break;
+
+	case UNDWARF_TYPE_REGS_IRET:
+		orig_sp = state->sp;
+		if (!deref_stack_regs(state, cfa, &state->ip, &state->sp, false)) {
+			undwarf_warn("can't dereference iret registers at %p for ip %p\n",
+				     (void *)cfa, (void *)orig_ip);
+			goto done;
+		}
+
+		ptregs = container_of((void *)cfa, struct pt_regs, ip);
+		if ((unsigned long)ptregs >= orig_sp &&
+		    on_stack(&state->stack_info, ptregs, REGS_SIZE)) {
+			state->regs = ptregs;
+			state->full_regs = false;
+		} else
+			state->regs = NULL;
+
+		state->signal = true;
+		break;
+
+	default:
+		undwarf_warn("unknown undwarf type %d\n", undwarf->type);
+		break;
+	}
+
+	/* Find BP: */
+	switch (undwarf->bp_reg) {
+	case UNDWARF_REG_UNDEFINED:
+		if (state->regs && state->full_regs)
+			state->bp = state->regs->bp;
+		break;
+
+	case UNDWARF_REG_CFA:
+		if (!deref_stack_reg(state, cfa + undwarf->bp_offset,&state->bp))
+			goto done;
+		break;
+
+	case UNDWARF_REG_BP:
+		if (!deref_stack_reg(state, state->bp + undwarf->bp_offset, &state->bp))
+			goto done;
+		break;
+
+	default:
+		undwarf_warn("unknown BP base reg %d for ip %p\n",
+			     undwarf->bp_reg, (void *)orig_ip);
+		goto done;
+	}
+
+	/* Prevent a recursive loop due to bad undwarf data: */
+	if (state->stack_info.type == prev_type &&
+	    on_stack(&state->stack_info, (void *)state->sp, sizeof(long)) &&
+	    state->sp <= prev_sp) {
+		undwarf_warn("stack going in the wrong direction? ip=%p\n",
+			     (void *)orig_ip);
+		goto done;
+	}
+
+	preempt_enable();
+	return true;
+
+done:
+	preempt_enable();
+	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)
+{
+	memset(state, 0, sizeof(*state));
+	state->task = task;
+
+	/*
+	 * Refuse to unwind the stack of a task while it's executing on another
+	 * CPU.  This check is racy, but that's ok: the unwinder has other
+	 * checks to prevent it from going off the rails.
+	 */
+	if (task_on_another_cpu(task))
+		goto done;
+
+	if (regs) {
+		if (user_mode(regs))
+			goto done;
+
+		state->ip = regs->ip;
+		state->sp = kernel_stack_pointer(regs);
+		state->bp = regs->bp;
+		state->regs = regs;
+		state->full_regs = true;
+		state->signal = true;
+
+	} else if (task == current) {
+		asm volatile("lea (%%rip), %0\n\t"
+			     "mov %%rsp, %1\n\t"
+			     "mov %%rbp, %2\n\t"
+			     : "=r" (state->ip), "=r" (state->sp),
+			       "=r" (state->bp));
+
+	} else {
+		struct inactive_task_frame *frame = (void *)task->thread.sp;
+
+		state->ip = frame->ret_addr;
+		state->sp = task->thread.sp;
+		state->bp = frame->bp;
+	}
+
+	if (get_stack_info((unsigned long *)state->sp, state->task,
+			   &state->stack_info, &state->stack_mask))
+		return;
+
+	/*
+	 * The caller can provide the address of the first frame directly
+	 * (first_frame) or indirectly (regs->sp) to indicate which stack frame
+	 * to start unwinding at.  Skip ahead until we reach it.
+	 */
+	while (!unwind_done(state) &&
+	       (!on_stack(&state->stack_info, first_frame, sizeof(long)) ||
+			state->sp <= (unsigned long)first_frame))
+		unwind_next_frame(state);
+
+	return;
+
+done:
+	state->stack_info.type = STACK_TYPE_UNKNOWN;
+	return;
+}
+EXPORT_SYMBOL_GPL(__unwind_start);
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index c8a3b61..e3b7cfc 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -148,6 +148,8 @@ SECTIONS
 
 	BUG_TABLE
 
+	UNDWARF_TABLE
+
 	. = ALIGN(PAGE_SIZE);
 	__vvar_page = .;
 
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 0d64658..a8ed616 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -668,6 +668,24 @@
 #define BUG_TABLE
 #endif
 
+#ifdef CONFIG_UNDWARF_UNWINDER
+#define UNDWARF_TABLE							\
+	. = ALIGN(4);							\
+	.undwarf_ip : AT(ADDR(.undwarf_ip) - LOAD_OFFSET) {		\
+		VMLINUX_SYMBOL(__start_undwarf_ip) = .;			\
+		KEEP(*(.undwarf_ip))					\
+		VMLINUX_SYMBOL(__stop_undwarf_ip) = .;			\
+	}								\
+	. = ALIGN(8);							\
+	.undwarf : AT(ADDR(.undwarf) - LOAD_OFFSET) {			\
+		VMLINUX_SYMBOL(__start_undwarf) = .;			\
+		KEEP(*(.undwarf))					\
+		VMLINUX_SYMBOL(__stop_undwarf) = .;			\
+	}
+#else
+#define UNDWARF_TABLE
+#endif
+
 #ifdef CONFIG_PM_TRACE
 #define TRACEDATA							\
 	. = ALIGN(4);							\
@@ -854,7 +872,7 @@
 		DATA_DATA						\
 		CONSTRUCTORS						\
 	}								\
-	BUG_TABLE
+	BUG_TABLE							\
 
 #define INIT_TEXT_SECTION(inittext_align)				\
 	. = ALIGN(inittext_align);					\
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 9c5d40a..ec79366 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -374,6 +374,9 @@ config STACK_VALIDATION
 	  pointers (if CONFIG_FRAME_POINTER is enabled).  This helps ensure
 	  that runtime stack traces are more reliable.
 
+	  This is also a prerequisite for creation of the undwarf format which
+	  is needed for CONFIG_UNDWARF_UNWINDER.
+
 	  For more information, see
 	  tools/objtool/Documentation/stack-validation.txt.
 
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 733e044..7859c79 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -258,7 +258,8 @@ ifneq ($(SKIP_STACK_VALIDATION),1)
 
 __objtool_obj := $(objtree)/tools/objtool/objtool
 
-objtool_args = check
+objtool_args = $(if $(CONFIG_UNDWARF_UNWINDER),undwarf generate,check)
+
 ifndef CONFIG_FRAME_POINTER
 objtool_args += --no-fp
 endif
@@ -276,6 +277,11 @@ objtool_obj = $(if $(patsubst y%,, \
 endif # SKIP_STACK_VALIDATION
 endif # CONFIG_STACK_VALIDATION
 
+# Rebuild all objects when objtool changes, or is enabled/disabled.
+objtool_dep = $(objtool_obj)					\
+	      $(wildcard include/config/undwarf/unwinder.h	\
+			 include/config/stack/validation.h)
+
 define rule_cc_o_c
 	$(call echo-cmd,checksrc) $(cmd_checksrc)			  \
 	$(call cmd_and_fixdep,cc_o_c)					  \
@@ -298,13 +304,13 @@ cmd_undef_syms = echo
 endif
 
 # Built-in and composite module parts
-$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_obj) FORCE
+$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE
 	$(call cmd,force_checksrc)
 	$(call if_changed_rule,cc_o_c)
 
 # Single-part modules are special since we need to mark them in $(MODVERDIR)
 
-$(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_obj) FORCE
+$(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE
 	$(call cmd,force_checksrc)
 	$(call if_changed_rule,cc_o_c)
 	@{ echo $(@:.o=.ko); echo $@; \
@@ -399,7 +405,7 @@ cmd_modversions_S =								\
 endif
 endif
 
-$(obj)/%.o: $(src)/%.S $(objtool_obj) FORCE
+$(obj)/%.o: $(src)/%.S $(objtool_dep) FORCE
 	$(call if_changed_rule,as_o_S)
 
 targets += $(real-objs-y) $(real-objs-m) $(lib-y)
-- 
2.7.5

  parent reply	other threads:[~2017-06-28 15:13 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-06-28 15:11 [PATCH v2 0/8] x86: undwarf unwinder Josh Poimboeuf
2017-06-28 15:11 ` [PATCH v2 1/8] objtool: move checking code to check.c Josh Poimboeuf
2017-06-30 13:12   ` [tip:core/objtool] objtool: Move " tip-bot for Josh Poimboeuf
2017-06-28 15:11 ` [PATCH v2 2/8] objtool, x86: add several functions and files to the objtool whitelist Josh Poimboeuf
2017-06-30 13:12   ` [tip:core/objtool] objtool, x86: Add " tip-bot for Josh Poimboeuf
2017-06-28 15:11 ` [PATCH v2 3/8] objtool: stack validation 2.0 Josh Poimboeuf
2017-06-30  8:32   ` Ingo Molnar
2017-06-30 13:23     ` Josh Poimboeuf
2017-06-30 13:26       ` Josh Poimboeuf
2017-06-30 14:09     ` [PATCH] objtool: silence warnings for functions which use iret Josh Poimboeuf
2017-06-30 17:49       ` [tip:core/objtool] objtool: Silence warnings for functions which use IRET tip-bot for Josh Poimboeuf
2017-06-30 13:13   ` [tip:core/objtool] objtool: Implement stack validation 2.0 tip-bot for Josh Poimboeuf
2017-06-28 15:11 ` [PATCH v2 4/8] objtool: add undwarf debuginfo generation Josh Poimboeuf
2017-06-29  7:14   ` Ingo Molnar
2017-06-29 13:40     ` Josh Poimboeuf
2017-06-29  7:25   ` Ingo Molnar
2017-06-29 14:04     ` Josh Poimboeuf
2017-06-29 14:46       ` Ingo Molnar
2017-06-29 15:06         ` Josh Poimboeuf
2017-07-06 20:36           ` Josh Poimboeuf
2017-07-07  9:44             ` Ingo Molnar
2017-07-11  2:58               ` Josh Poimboeuf
2017-07-11  8:40                 ` Ingo Molnar
2017-06-28 15:11 ` [PATCH v2 5/8] objtool, x86: add facility for asm code to provide unwind hints Josh Poimboeuf
2017-06-28 15:11 ` [PATCH v2 6/8] x86/entry: add unwind hint annotations Josh Poimboeuf
2017-06-29 17:53   ` Josh Poimboeuf
2017-06-29 18:50     ` Andy Lutomirski
2017-06-29 19:05       ` Josh Poimboeuf
2017-06-29 21:09         ` Andy Lutomirski
2017-06-29 21:41           ` Josh Poimboeuf
2017-06-29 22:59             ` Andy Lutomirski
2017-06-30  2:12               ` Josh Poimboeuf
2017-06-30  5:05                 ` Andy Lutomirski
2017-06-30  5:41                   ` Andy Lutomirski
2017-06-30 13:11                     ` Josh Poimboeuf
2017-06-30 15:44                       ` Andy Lutomirski
2017-06-30 15:55                         ` Josh Poimboeuf
2017-06-30 15:56                           ` Andy Lutomirski
2017-06-30 16:16                             ` Josh Poimboeuf
2017-06-28 15:11 ` [PATCH v2 7/8] x86/asm: add unwind hint annotations to sync_core() Josh Poimboeuf
2017-06-28 15:11 ` Josh Poimboeuf [this message]
2017-06-29  7:55 ` [PATCH v2 0/8] x86: undwarf unwinder Ingo Molnar
2017-06-29 14:12   ` Josh Poimboeuf
2017-06-29 19:13     ` Josh Poimboeuf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f67778a0c75a61eba5f3f8a3016f9c0e7a09e728.1498659915.git.jpoimboe@redhat.com \
    --to=jpoimboe@redhat.com \
    --cc=hpa@zytor.com \
    --cc=jslaby@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=luto@kernel.org \
    --cc=mingo@kernel.org \
    --cc=peterz@infradead.org \
    --cc=torvalds@linux-foundation.org \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.