From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751447AbdFAFpM (ORCPT ); Thu, 1 Jun 2017 01:45:12 -0400 Received: from mx1.redhat.com ([209.132.183.28]:48730 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751190AbdFAFob (ORCPT ); Thu, 1 Jun 2017 01:44:31 -0400 DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 682B54F5 Authentication-Results: ext-mx05.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx05.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=jpoimboe@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 682B54F5 From: Josh Poimboeuf To: x86@kernel.org Cc: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, Linus Torvalds , Andy Lutomirski , Jiri Slaby , Ingo Molnar , "H. Peter Anvin" , Peter Zijlstra Subject: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder Date: Thu, 1 Jun 2017 00:44:16 -0500 Message-Id: <89552d4047e5aed843f7b6a54277f9af62da6a82.1496293620.git.jpoimboe@redhat.com> In-Reply-To: References: X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Thu, 01 Jun 2017 05:44:31 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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. Signed-off-by: Josh Poimboeuf --- arch/um/include/asm/unwind.h | 7 + arch/x86/Kconfig | 1 + arch/x86/Kconfig.debug | 26 +++ arch/x86/include/asm/module.h | 8 + arch/x86/include/asm/unwind.h | 64 +++--- arch/x86/kernel/Makefile | 8 +- arch/x86/kernel/module.c | 9 +- arch/x86/kernel/unwind_frame.c | 39 ++-- arch/x86/kernel/unwind_guess.c | 5 + arch/x86/kernel/unwind_undwarf.c | 402 ++++++++++++++++++++++++++++++++++++++ include/asm-generic/vmlinux.lds.h | 14 ++ lib/Kconfig.debug | 3 + scripts/Makefile.build | 3 +- scripts/link-vmlinux.sh | 5 + 14 files changed, 534 insertions(+), 60 deletions(-) create mode 100644 arch/um/include/asm/unwind.h create mode 100644 arch/x86/kernel/unwind_undwarf.c diff --git a/arch/um/include/asm/unwind.h b/arch/um/include/asm/unwind.h new file mode 100644 index 0000000..4e3f719 --- /dev/null +++ b/arch/um/include/asm/unwind.h @@ -0,0 +1,7 @@ +#ifndef _ASM_UML_UNWIND_H +#define _ASM_UML_UNWIND_H + +static inline void +unwind_module_init(struct module *mod, void *undwarf, size_t size) {} + +#endif /* _ASM_UML_UNWIND_H */ diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 4ccfacc..869fbc5 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -151,6 +151,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..6717463 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -357,4 +357,30 @@ 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 + select SORTTABLE + ---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 3-5MB, depending on the 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..454eeea 100644 --- a/arch/x86/include/asm/module.h +++ b/arch/x86/include/asm/module.h @@ -2,6 +2,14 @@ #define _ASM_X86_MODULE_H #include +#include + +struct mod_arch_specific { +#ifdef CONFIG_UNDWARF_UNWINDER + unsigned int num_undwarves; + 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..f06be3f 100644 --- a/arch/x86/include/asm/unwind.h +++ b/arch/x86/include/asm/unwind.h @@ -12,11 +12,13 @@ struct unwind_state { struct task_struct *task; int graph_idx; bool error; -#ifdef CONFIG_FRAME_POINTER +#if defined(CONFIG_UNDWARF_UNWINDER) + 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 +26,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 +57,33 @@ 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) -{ - return NULL; -} - +#else static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state) { return NULL; } +#endif + +#ifdef CONFIG_UNDWARF_UNWINDER +void unwind_module_init(struct module *mod, void *undwarf, size_t size); +#else +static inline void +unwind_module_init(struct module *mod, void *undwarf, size_t size) {} +#endif -#endif /* CONFIG_FRAME_POINTER */ +/* + * 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; \ +}) #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..6756070 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -35,6 +35,7 @@ #include #include #include +#include #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; char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { @@ -225,6 +226,8 @@ 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 (alt) { @@ -248,6 +251,10 @@ int module_finalize(const Elf_Ehdr *hdr, /* make jump label nops */ jump_label_apply_nops(me); + if (undwarf) + unwind_module_init(me, (void *)undwarf->sh_addr, + undwarf->sh_size); + return 0; } 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..8023662 --- /dev/null +++ b/arch/x86/kernel/unwind_undwarf.c @@ -0,0 +1,402 @@ +#include +#include +#include +#include +#include +#include + +#define undwarf_warn(fmt, ...) \ + printk_deferred_once(KERN_WARNING pr_fmt("WARNING: " fmt), ##__VA_ARGS__) + +extern struct undwarf __undwarf_start[]; +extern struct undwarf __undwarf_end[]; + +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 inline unsigned long undwarf_ip(struct undwarf *undwarf) +{ + return (unsigned long)&undwarf->ip + undwarf->ip; +} + +static struct undwarf *__undwarf_lookup(struct undwarf *undwarf, + unsigned int num, unsigned long ip) +{ + struct undwarf *first = undwarf; + struct undwarf *last = undwarf + num - 1; + struct undwarf *mid; + unsigned long u_ip; + + while (first <= last) { + mid = first + ((last - first) / 2); + u_ip = undwarf_ip(mid); + + if (ip >= u_ip) { + if (ip < u_ip + mid->len) + return mid; + first = mid + 1; + } else + last = mid - 1; + } + + return NULL; +} + +static struct undwarf *undwarf_lookup(unsigned long ip) +{ + struct undwarf *undwarf; + struct module *mod; + + /* Look in vmlinux undwarf section: */ + undwarf = __undwarf_lookup(__undwarf_start, __undwarf_end - __undwarf_start, ip); + if (undwarf) + return undwarf; + + /* Look in module undwarf sections: */ + preempt_disable(); + mod = __module_address(ip); + if (!mod || !mod->arch.undwarf) + goto module_out; + undwarf = __undwarf_lookup(mod->arch.undwarf, mod->arch.num_undwarves, ip); + +module_out: + preempt_enable(); + return undwarf; +} + +static bool stack_access_ok(struct unwind_state *state, unsigned long addr, + size_t len) +{ + struct stack_info *info = &state->stack_info; + + /* + * If the next bp 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 next bp + * 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)®s->sp; + + return true; +} + +bool unwind_next_frame(struct unwind_state *state) +{ + struct undwarf *undwarf; + unsigned long cfa; + bool indirect = false; + enum stack_type prev_type = state->stack_info.type; + unsigned long ip_p, prev_sp = state->sp; + + if (unwind_done(state)) + return false; + + /* Have we reached the end? */ + if (state->regs && user_mode(state->regs)) + goto done; + + /* Look up the instruction address in the .undwarf table: */ + undwarf = undwarf_lookup(state->ip); + if (!undwarf || undwarf->cfa_reg == UNDWARF_REG_UNDEFINED) + goto done; + + /* 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) { + 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_DI: + if (!state->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) { + undwarf_warn("missing regs for base reg DI 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; + 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 *)state->ip); + goto done; + } + + state->regs = (struct pt_regs *)cfa; + break; + + case UNDWARF_TYPE_REGS_IRET: + 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 *)state->ip); + goto done; + } + + state->regs = NULL; + 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->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 *)undwarf_ip(undwarf)); + 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 *)state->ip); + goto done; + } + + return true; + +done: + 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; + + if (regs) { + if (user_mode(regs)) { + state->stack_info.type = STACK_TYPE_UNKNOWN; + return; + } + + state->ip = regs->ip; + state->sp = kernel_stack_pointer(regs); + state->bp = regs->bp; + state->regs = regs; + + } else if (task == current) { + register void *__sp asm(_ASM_SP); + + 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), "+r" (__sp)); + + state->regs = NULL; + + } else { + struct inactive_task_frame *frame = (void *)task->thread.sp; + + state->ip = frame->ret_addr; + state->sp = task->thread.sp; + state->bp = frame->bp; + state->regs = NULL; + } + + 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); +} +EXPORT_SYMBOL_GPL(__unwind_start); + +static void undwarf_sort_swap(void *_a, void *_b, int size) +{ + struct undwarf *a = _a, *b = _b, tmp; + int delta = _b - _a; + + tmp = *a; + *a = *b; + *b = tmp; + + a->ip += delta; + b->ip -= delta; +} + +static int undwarf_sort_cmp(const void *_a, const void *_b) +{ + unsigned long a = undwarf_ip((struct undwarf *)_a); + unsigned long b = undwarf_ip((struct undwarf *)_b); + + if (a > b) + return 1; + if (a < b) + return -1; + return 0; +} + +void unwind_module_init(struct module *mod, void *u, size_t size) +{ + struct undwarf *undwarf = u; + unsigned int num = size / sizeof(*undwarf); + + WARN_ON_ONCE(size % sizeof(*undwarf) != 0); + + sort(undwarf, num, sizeof(*undwarf), undwarf_sort_cmp, undwarf_sort_swap); + + mod->arch.undwarf = undwarf; + mod->arch.num_undwarves = num; +} diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 314a0b9..e350116 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -324,6 +324,8 @@ \ TRACEDATA \ \ + UNDWARF_TABLE \ + \ /* Kernel symbol table: Normal symbols */ \ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab) = .; \ @@ -669,6 +671,18 @@ #define BUG_TABLE #endif +#ifdef CONFIG_UNDWARF_UNWINDER +#define UNDWARF_TABLE \ + . = ALIGN(16); \ + .undwarf : AT(ADDR(.undwarf) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__undwarf_start) = .; \ + KEEP(*(.undwarf)) \ + VMLINUX_SYMBOL(__undwarf_end) = .; \ + } +#else +#define UNDWARF_TABLE +#endif + #ifdef CONFIG_PM_TRACE #define TRACEDATA \ . = ALIGN(4); \ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e4587eb..31f73d9 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..b43dddf 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 diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index f4eb9dc..286ea8d 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -296,6 +296,11 @@ if [ -n "${CONFIG_BUILDTIME_EXTABLE_SORT}" ]; then sortextable vmlinux fi +if [ -n "${CONFIG_UNDWARF_UNWINDER}" ]; then + info SORTUD vmlinux + sortundwarf vmlinux +fi + info SYSMAP System.map mksysmap vmlinux System.map -- 2.7.4