x86: undwarf unwinder
Subsystems (18)
v2 cover x86: undwarf unwinder
v2:
- 2x performance improvement by using a fast lookup table and splitting undwarf array into two parallel arrays (Andy L)
- reduce data size by ~1MB by getting rid of 'len' field
- sort and post-process data at boot time
- don't search vmlinux tables for module addresses (Peter Z)
- disable preemption to prevent module from getting unloaded while reading its undwarf data (Peter Z)
- avoid unwinding a running task's stack (Jiri S)
- remove '__sp' constraint from inline asm (Jiri S)
- rename "CFI_*" -> "UNWIND_HINT_*" (Andy L)
- replace '999:' label with '.Lunwind_hint_ip_\@' (Andy L)
- entry code annotation fixes: extra=0 fix, symmetrical macro annotations, ret_from_fork fix (Andy L)
- invalidate all object files when enabling/disabling CONFIG_UNDWARF_UNWINDER
- pass ip-1 to undwarf_find() for call return addresses to fix stack traces for sibling calls and noreturn calls at end of function
- docs: clarify benefits vs frame pointers (Ingo)
- docs: improve wording, add more info, add performance info from Mel G and Jiri S, move to kernel docs dir
- objtool: several minor fixes (Jiri S)
- objtool: append file instead of rewriting it
- objtool: improve elf warnings
- objtool: fix handling of the GCC DRAP register for aligned stacks
- objtool: rewrite 'undwarf dump' command to be much faster and to work on vmlinux
- objtool: rename undwarf.c -> undwarf_gen.c
-----
Create a new 'undwarf' unwinder, enabled by CONFIG_UNDWARF_UNWINDER, and plug it into the x86 unwinder framework. Objtool is used to generate the undwarf debuginfo. The undwarf debuginfo format is basically a simplified version of DWARF CFI. More details below.
The unwinder works well in my testing. It unwinds through interrupts, exceptions, and preemption, with and without frame pointers, across aligned stacks and dynamically allocated stacks. If something goes wrong during an oops, it successfully falls back to printing the '?' entries just like the frame pointer unwinder.
I'm not tied to the 'undwarf' name, other naming ideas are welcome.
Some potential future improvements:
- properly annotate or fix whitelisted functions and files
- reduce the number of base CFA registers needed in entry code
- compress undwarf debuginfo to use less memory
- make it easier to disable CONFIG_FRAME_POINTER
- add reliability checks for livepatch
- runtime NMI stack reliability checker
This code can also be found at:
git://github.com/jpoimboe/linux undwarf-v2
Here's the contents of the undwarf.txt file which explains the 'why' in more detail:
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 3-5MB 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/[email protected]
[2] https://lkml.kernel.org/r/[email protected]
Josh Poimboeuf (8):
objtool: move checking code to check.c
objtool, x86: add several functions and files to the objtool whitelist
objtool: stack validation 2.0
objtool: add undwarf debuginfo generation
objtool, x86: add facility for asm code to provide unwind hints x86/entry: add unwind hint annotations
x86/asm: add unwind hint annotations to sync_core() x86/unwind: add undwarf unwinder
Documentation/x86/undwarf.txt | 146 +++
arch/um/include/asm/unwind.h | 8 +
arch/x86/Kconfig | 1 +
arch/x86/Kconfig.debug | 25 +
arch/x86/crypto/Makefile | 2 +
arch/x86/crypto/sha1-mb/Makefile | 2 +
arch/x86/crypto/sha256-mb/Makefile | 2 +
arch/x86/entry/Makefile | 1 -
arch/x86/entry/calling.h | 6 +
arch/x86/entry/entry_64.S | 56 +-
arch/x86/include/asm/module.h | 9 +
arch/x86/include/asm/processor.h | 3 +
arch/x86/include/asm/undwarf-types.h | 99 ++
arch/x86/include/asm/undwarf.h | 103 ++
arch/x86/include/asm/unwind.h | 77 +-
arch/x86/kernel/Makefile | 9 +-
arch/x86/kernel/acpi/Makefile | 2 +
arch/x86/kernel/kprobes/opt.c | 9 +-
arch/x86/kernel/module.c | 12 +-
arch/x86/kernel/reboot.c | 2 +
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 +
arch/x86/kvm/svm.c | 2 +
arch/x86/kvm/vmx.c | 3 +
arch/x86/lib/msr-reg.S | 8 +-
arch/x86/net/Makefile | 2 +
arch/x86/platform/efi/Makefile | 1 +
arch/x86/power/Makefile | 2 +
arch/x86/xen/Makefile | 3 +
include/asm-generic/vmlinux.lds.h | 20 +-
kernel/kexec_core.c | 4 +-
lib/Kconfig.debug | 3 +
scripts/Makefile.build | 14 +-
tools/objtool/Build | 4 +
tools/objtool/Documentation/stack-validation.txt | 195 ++--
tools/objtool/Makefile | 5 +-
tools/objtool/arch.h | 64 +-
tools/objtool/arch/x86/decode.c | 400 ++++++-
tools/objtool/builtin-check.c | 1281 +---------------------
tools/objtool/builtin-undwarf.c | 70 ++
tools/objtool/builtin.h | 1 +
tools/objtool/cfi.h | 55 +
tools/objtool/{builtin-check.c => check.c} | 954 ++++++++++++----
tools/objtool/check.h | 79 ++
tools/objtool/elf.c | 265 ++++-
tools/objtool/elf.h | 21 +-
tools/objtool/objtool.c | 3 +-
tools/objtool/special.c | 6 +-
tools/objtool/undwarf-types.h | 99 ++
tools/objtool/{builtin.h => undwarf.h} | 18 +-
tools/objtool/undwarf_dump.c | 212 ++++
tools/objtool/undwarf_gen.c | 215 ++++
tools/objtool/warn.h | 10 +
56 files changed, 3466 insertions(+), 1765 deletions(-)
create mode 100644 Documentation/x86/undwarf.txt
create mode 100644 arch/um/include/asm/unwind.h
create mode 100644 arch/x86/include/asm/undwarf-types.h
create mode 100644 arch/x86/include/asm/undwarf.h
create mode 100644 arch/x86/kernel/unwind_undwarf.c
create mode 100644 tools/objtool/builtin-undwarf.c
create mode 100644 tools/objtool/cfi.h
copy tools/objtool/{builtin-check.c => check.c} (59%)
create mode 100644 tools/objtool/check.h
create mode 100644 tools/objtool/undwarf-types.h
copy tools/objtool/{builtin.h => undwarf.h} (67%)
create mode 100644 tools/objtool/undwarf_dump.c
create mode 100644 tools/objtool/undwarf_gen.cSignature · 1 line
2.7.5
PATCH v2 1/8 objtool: move checking code to check.c
In preparation for the new 'objtool undwarf generate' command, which will rely on 'objtool check', move the checking code from builtin-check.c to check.c where it can be used by other commands.
---
tools/objtool/Build | 1 +
tools/objtool/builtin-check.c | 1281 +---------------------------
tools/objtool/{builtin-check.c => check.c} | 58 +-
tools/objtool/check.h | 51 ++
4 files changed, 70 insertions(+), 1321 deletions(-)
copy tools/objtool/{builtin-check.c => check.c} (95%)
create mode 100644 tools/objtool/check.htools/objtool/Build +1 -0
@@ -1,5 +1,6 @@ objtool-y += arch/$(SRCARCH)/ objtool-y += builtin-check.o +objtool-y += check.o objtool-y += elf.o objtool-y += special.o objtool-y += objtool.o
tools/objtool/builtin-check.c +13 -1268
1,305 lines not on this page · raw message
tools/objtool/check.c renamed from tools/objtool/builtin-check.c +5 -53
92 lines not on this page · raw message
tools/objtool/check.h new file +51 -0
51 lines not on this page · raw message
Signature · 1 line
2.7.5
tip-bot for Josh Poimboeuf
merge bot[tip:core/objtool] objtool: Move checking code to check.c
objtool: Move checking code to check.c
Commit-ID: dcc914f44f065ef73685b37e59877a5bb3cb7358 Gitweb: http://git.kernel.org/tip/dcc914f44f065ef73685b37e59877a5bb3cb7358 Author: Josh Poimboeuf <[email protected]> AuthorDate: Wed, 28 Jun 2017 10:11:05 -0500 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 30 Jun 2017 10:19:19 +0200
objtool: Move checking code to check.c
In preparation for the new 'objtool undwarf generate' command, which will rely on 'objtool check', move the checking code from builtin-check.c to check.c where it can be used by other commands.
---
tools/objtool/Build | 1 +
tools/objtool/builtin-check.c | 1281 +---------------------------
tools/objtool/{builtin-check.c => check.c} | 58 +-
tools/objtool/{special.h => check.h} | 43 +-
4 files changed, 45 insertions(+), 1338 deletions(-)tools/objtool/Build +1 -0
@@ -1,5 +1,6 @@ objtool-y += arch/$(SRCARCH)/ objtool-y += builtin-check.o +objtool-y += check.o objtool-y += elf.o objtool-y += special.o objtool-y += objtool.o
tools/objtool/builtin-check.c +13 -1268
1,305 lines not on this page · raw message
tools/objtool/check.c renamed from tools/objtool/builtin-check.c +5 -53
92 lines not on this page · raw message
tools/objtool/check.h renamed from tools/objtool/special.h +26 -17
59 lines not on this page · raw message
PATCH v2 2/8 objtool, x86: add several functions and files to the objtool whitelist
In reply to Josh PoimboeufIn preparation for an objtool rewrite which will have broader checks, whitelist functions and files which cause problems because they do unusual things with the stack.
These whitelists serve as a TODO list for which functions and files don't yet have undwarf unwinder coverage. Eventually most of the whitelists can be removed in favor of manual CFI hint annotations or objtool improvements.
--- arch/x86/crypto/Makefile | 2 ++ arch/x86/crypto/sha1-mb/Makefile | 2 ++ arch/x86/crypto/sha256-mb/Makefile | 2 ++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/acpi/Makefile | 2 ++ arch/x86/kernel/kprobes/opt.c | 9 ++++++++- arch/x86/kernel/reboot.c | 2 ++ arch/x86/kvm/svm.c | 2 ++ arch/x86/kvm/vmx.c | 3 +++ arch/x86/lib/msr-reg.S | 8 ++++---- arch/x86/net/Makefile | 2 ++ arch/x86/platform/efi/Makefile | 1 + arch/x86/power/Makefile | 2 ++ arch/x86/xen/Makefile | 3 +++ kernel/kexec_core.c | 4 +++- 15 files changed, 39 insertions(+), 6 deletions(-)
arch/x86/crypto/Makefile +2 -0
@@ -2,6 +2,8 @@ # Arch-specific CryptoAPI modules. # +OBJECT_FILES_NON_STANDARD := y + avx_supported := $(call as-instr,vpxor %xmm0$(comma)%xmm0$(comma)%xmm0,yes,no) avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\ $(comma)4)$(comma)%ymm2,yes,no)
arch/x86/crypto/sha1-mb/Makefile +2 -0
@@ -2,6 +2,8 @@ # Arch-specific CryptoAPI modules. # +OBJECT_FILES_NON_STANDARD := y + avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\ $(comma)4)$(comma)%ymm2,yes,no) ifeq ($(avx2_supported),yes)
arch/x86/crypto/sha256-mb/Makefile +2 -0
@@ -2,6 +2,8 @@ # Arch-specific CryptoAPI modules. # +OBJECT_FILES_NON_STANDARD := y + avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\ $(comma)4)$(comma)%ymm2,yes,no) ifeq ($(avx2_supported),yes)
arch/x86/kernel/Makefile +1 -0
@@ -29,6 +29,7 @@ OBJECT_FILES_NON_STANDARD_head_$(BITS).o := y OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y OBJECT_FILES_NON_STANDARD_ftrace_$(BITS).o := y OBJECT_FILES_NON_STANDARD_test_nx.o := y +OBJECT_FILES_NON_STANDARD_paravirt_patch_$(BITS).o := y # If instrumentation of this dir is enabled, boot hangs during first second. # Probably could be more selective here, but note that files related to irqs,
arch/x86/kernel/acpi/Makefile +2 -0
@@ -1,3 +1,5 @@ +OBJECT_FILES_NON_STANDARD_wakeup_$(BITS).o := y + obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o obj-$(CONFIG_ACPI_APEI) += apei.o
arch/x86/kernel/kprobes/opt.c +8 -1
@@ -28,6 +28,7 @@#include <linux/kdebug.h> #include <linux/kallsyms.h> #include <linux/ftrace.h> +#include <linux/frame.h> #include <asm/text-patching.h> #include <asm/cacheflush.h>
@@ -94,6 +95,7 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val) } asm ( + "optprobe_template_func:\n" ".global optprobe_template_entry\n" "optprobe_template_entry:\n"#ifdef CONFIG_X86_64
@@ -131,7 +133,12 @@ asm (" popf\n" #endif".global optprobe_template_end\n" - "optprobe_template_end:\n"); +"optprobe_template_end:\n" +".type optprobe_template_func, @function\n" + ".size optprobe_template_func, .-optprobe_template_func\n"); + +void optprobe_template_func (void ); +STACK_FRAME_NON_STANDARD (optprobe_template_func);#define TMPL_MOVE_IDX \ ((long)&optprobe_template_val - (long)&optprobe_template_entry)
arch/x86/kernel/reboot.c +2 -0
@@ -9,6 +9,7 @@#include <linux/sched.h> #include <linux/tboot.h> #include <linux/delay.h> +#include <linux/frame.h> #include <acpi/reboot.h> #include <asm/io.h> #include <asm/apic.h>
@@ -123,6 +124,7 @@ void __noreturn machine_real_restart(unsigned int type)#ifdef CONFIG_APM_MODULE EXPORT_SYMBOL (machine_real_restart);#endif +STACK_FRAME_NON_STANDARD (machine_real_restart);/* * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
arch/x86/kvm/svm.c +2 -0
@@ -36,6 +36,7 @@#include <linux/slab.h> #include <linux/amd-iommu.h> #include <linux/hashtable.h> +#include <linux/frame.h> #include <asm/apic.h> #include <asm/perf_event.h>
@@ -4906,6 +4907,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)mark_all_clean (svm->vmcb); } +STACK_FRAME_NON_STANDARD (svm_vcpu_run);static void svm_set_cr3 (struct kvm_vcpu *vcpu,unsigned long root) {
arch/x86/kvm/vmx.c +3 -0
@@ -33,6 +33,7 @@#include <linux/slab.h> #include <linux/tboot.h> #include <linux/hrtimer.h> +#include <linux/frame.h> #include "kvm_cache_regs.h" #include "x86.h"
@@ -8661,6 +8662,7 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu) ); } } +STACK_FRAME_NON_STANDARD (vmx_handle_external_intr);static bool vmx_has_high_real_mode_segbase (void ) {
@@ -9043,6 +9045,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)vmx_recover_nmi_blocking (vmx);vmx_complete_interrupts (vmx); } +STACK_FRAME_NON_STANDARD (vmx_vcpu_run);static void vmx_switch_vmcs (struct kvm_vcpu *vcpu,struct loaded_vmcs *vmcs) {
arch/x86/lib/msr-reg.S +4 -4
@@ -13,14 +13,14 @@ .macro op_safe_regs op ENTRY(\op\()_safe_regs) pushq %rbx - pushq %rbp + pushq %r12 movq %rdi, %r10 /* Save pointer */ xorl %r11d, %r11d /* Return value */ movl (%rdi), %eax movl 4(%rdi), %ecx movl 8(%rdi), %edx movl 12(%rdi), %ebx - movl 20(%rdi), %ebp + movl 20(%rdi), %r12d movl 24(%rdi), %esi movl 28(%rdi), %edi 1: \op
@@ -29,10 +29,10 @@ ENTRY(\op\()_safe_regs) movl %ecx, 4(%r10) movl %edx, 8(%r10) movl %ebx, 12(%r10) - movl %ebp, 20(%r10) + movl %r12d, 20(%r10) movl %esi, 24(%r10) movl %edi, 28(%r10) - popq %rbp + popq %r12 popq %rbx ret 3:
arch/x86/net/Makefile +2 -0
@@ -1,4 +1,6 @@ # # Arch-specific network modules # +OBJECT_FILES_NON_STANDARD_bpf_jit.o += y + obj-$(CONFIG_BPF_JIT) += bpf_jit.o bpf_jit_comp.o
arch/x86/platform/efi/Makefile +1 -0
@@ -1,4 +1,5 @@ OBJECT_FILES_NON_STANDARD_efi_thunk_$(BITS).o := y +OBJECT_FILES_NON_STANDARD_efi_stub_$(BITS).o := y obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o
arch/x86/power/Makefile +2 -0
@@ -1,3 +1,5 @@ +OBJECT_FILES_NON_STANDARD_hibernate_asm_$(BITS).o := y + # __restore_processor_state() restores %gs after S3 resume and so should not # itself be stack-protected nostackp := $(call cc-option, -fno-stack-protector)
arch/x86/xen/Makefile +3 -0
@@ -1,3 +1,6 @@ +OBJECT_FILES_NON_STANDARD_xen-asm_$(BITS).o := y +OBJECT_FILES_NON_STANDARD_xen-pvh.o := y + ifdef CONFIG_FUNCTION_TRACER # Do not profile debug and lowlevel utilities CFLAGS_REMOVE_spinlock.o = -pg
kernel/kexec_core.c +3 -1
@@ -38,6 +38,7 @@#include <linux/syscore_ops.h> #include <linux/compiler.h> #include <linux/hugetlb.h> +#include <linux/frame.h> #include <asm/page.h> #include <asm/sections.h>
@@ -874,7 +875,7 @@ int kexec_load_disabled;* only when panic_cpu holds the current CPU number; this is the only CPU * which processes crash_kexec routines. */ -void __crash_kexec (struct pt_regs *regs) +void __noclone __crash_kexec (struct pt_regs *regs) {/* Take the kexec_mutex here to prevent sys_kexec_load * running on one cpu from replacing the crash kernel
@@ -896,6 +897,7 @@ void __crash_kexec(struct pt_regs *regs)mutex_unlock (&kexec_mutex); } } +STACK_FRAME_NON_STANDARD (__crash_kexec);void crash_kexec (struct pt_regs *regs) {
Signature · 1 line
2.7.5
tip-bot for Josh Poimboeuf
merge bot[tip:core/objtool] objtool, x86: Add several functions and files to the objtool whitelist
objtool, x86: Add several functions and files to the objtool whitelist
Commit-ID: c207aee48037abca71c669cbec407b9891965c34 Gitweb: http://git.kernel.org/tip/c207aee48037abca71c669cbec407b9891965c34 Author: Josh Poimboeuf <[email protected]> AuthorDate: Wed, 28 Jun 2017 10:11:06 -0500 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 30 Jun 2017 10:19:19 +0200
objtool, x86: Add several functions and files to the objtool whitelist
In preparation for an objtool rewrite which will have broader checks, whitelist functions and files which cause problems because they do unusual things with the stack.
These whitelists serve as a TODO list for which functions and files don't yet have undwarf unwinder coverage. Eventually most of the whitelists can be removed in favor of manual CFI hint annotations or objtool improvements.
--- arch/x86/crypto/Makefile | 2 ++ arch/x86/crypto/sha1-mb/Makefile | 2 ++ arch/x86/crypto/sha256-mb/Makefile | 2 ++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/acpi/Makefile | 2 ++ arch/x86/kernel/kprobes/opt.c | 9 ++++++++- arch/x86/kernel/reboot.c | 2 ++ arch/x86/kvm/svm.c | 2 ++ arch/x86/kvm/vmx.c | 3 +++ arch/x86/lib/msr-reg.S | 8 ++++---- arch/x86/net/Makefile | 2 ++ arch/x86/platform/efi/Makefile | 1 + arch/x86/power/Makefile | 2 ++ arch/x86/xen/Makefile | 3 +++ kernel/kexec_core.c | 4 +++- 15 files changed, 39 insertions(+), 6 deletions(-)
arch/x86/crypto/Makefile +2 -0
@@ -2,6 +2,8 @@ # Arch-specific CryptoAPI modules. # +OBJECT_FILES_NON_STANDARD := y + avx_supported := $(call as-instr,vpxor %xmm0$(comma)%xmm0$(comma)%xmm0,yes,no) avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\ $(comma)4)$(comma)%ymm2,yes,no)
arch/x86/crypto/sha1-mb/Makefile +2 -0
@@ -2,6 +2,8 @@ # Arch-specific CryptoAPI modules. # +OBJECT_FILES_NON_STANDARD := y + avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\ $(comma)4)$(comma)%ymm2,yes,no) ifeq ($(avx2_supported),yes)
arch/x86/crypto/sha256-mb/Makefile +2 -0
@@ -2,6 +2,8 @@ # Arch-specific CryptoAPI modules. # +OBJECT_FILES_NON_STANDARD := y + avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\ $(comma)4)$(comma)%ymm2,yes,no) ifeq ($(avx2_supported),yes)
arch/x86/kernel/Makefile +1 -0
@@ -29,6 +29,7 @@ OBJECT_FILES_NON_STANDARD_head_$(BITS).o := y OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y OBJECT_FILES_NON_STANDARD_ftrace_$(BITS).o := y OBJECT_FILES_NON_STANDARD_test_nx.o := y +OBJECT_FILES_NON_STANDARD_paravirt_patch_$(BITS).o := y # If instrumentation of this dir is enabled, boot hangs during first second. # Probably could be more selective here, but note that files related to irqs,
arch/x86/kernel/acpi/Makefile +2 -0
@@ -1,3 +1,5 @@ +OBJECT_FILES_NON_STANDARD_wakeup_$(BITS).o := y + obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o obj-$(CONFIG_ACPI_APEI) += apei.o
arch/x86/kernel/kprobes/opt.c +8 -1
@@ -28,6 +28,7 @@#include <linux/kdebug.h> #include <linux/kallsyms.h> #include <linux/ftrace.h> +#include <linux/frame.h> #include <asm/text-patching.h> #include <asm/cacheflush.h>
@@ -94,6 +95,7 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val) } asm ( + "optprobe_template_func:\n" ".global optprobe_template_entry\n" "optprobe_template_entry:\n"#ifdef CONFIG_X86_64
@@ -131,7 +133,12 @@ asm (" popf\n" #endif".global optprobe_template_end\n" - "optprobe_template_end:\n"); +"optprobe_template_end:\n" +".type optprobe_template_func, @function\n" + ".size optprobe_template_func, .-optprobe_template_func\n"); + +void optprobe_template_func (void ); +STACK_FRAME_NON_STANDARD (optprobe_template_func);#define TMPL_MOVE_IDX \ ((long)&optprobe_template_val - (long)&optprobe_template_entry)
arch/x86/kernel/reboot.c +2 -0
@@ -9,6 +9,7 @@#include <linux/sched.h> #include <linux/tboot.h> #include <linux/delay.h> +#include <linux/frame.h> #include <acpi/reboot.h> #include <asm/io.h> #include <asm/apic.h>
@@ -123,6 +124,7 @@ void __noreturn machine_real_restart(unsigned int type)#ifdef CONFIG_APM_MODULE EXPORT_SYMBOL (machine_real_restart);#endif +STACK_FRAME_NON_STANDARD (machine_real_restart);/* * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
arch/x86/kvm/svm.c +2 -0
@@ -36,6 +36,7 @@#include <linux/slab.h> #include <linux/amd-iommu.h> #include <linux/hashtable.h> +#include <linux/frame.h> #include <asm/apic.h> #include <asm/perf_event.h>
@@ -4906,6 +4907,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)mark_all_clean (svm->vmcb); } +STACK_FRAME_NON_STANDARD (svm_vcpu_run);static void svm_set_cr3 (struct kvm_vcpu *vcpu,unsigned long root) {
arch/x86/kvm/vmx.c +3 -0
@@ -33,6 +33,7 @@#include <linux/slab.h> #include <linux/tboot.h> #include <linux/hrtimer.h> +#include <linux/frame.h> #include "kvm_cache_regs.h" #include "x86.h"
@@ -8652,6 +8653,7 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu) ); } } +STACK_FRAME_NON_STANDARD (vmx_handle_external_intr);static bool vmx_has_high_real_mode_segbase (void ) {
@@ -9028,6 +9030,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)vmx_recover_nmi_blocking (vmx);vmx_complete_interrupts (vmx); } +STACK_FRAME_NON_STANDARD (vmx_vcpu_run);static void vmx_switch_vmcs (struct kvm_vcpu *vcpu,struct loaded_vmcs *vmcs) {
arch/x86/lib/msr-reg.S +4 -4
@@ -13,14 +13,14 @@ .macro op_safe_regs op ENTRY(\op\()_safe_regs) pushq %rbx - pushq %rbp + pushq %r12 movq %rdi, %r10 /* Save pointer */ xorl %r11d, %r11d /* Return value */ movl (%rdi), %eax movl 4(%rdi), %ecx movl 8(%rdi), %edx movl 12(%rdi), %ebx - movl 20(%rdi), %ebp + movl 20(%rdi), %r12d movl 24(%rdi), %esi movl 28(%rdi), %edi 1: \op
@@ -29,10 +29,10 @@ ENTRY(\op\()_safe_regs) movl %ecx, 4(%r10) movl %edx, 8(%r10) movl %ebx, 12(%r10) - movl %ebp, 20(%r10) + movl %r12d, 20(%r10) movl %esi, 24(%r10) movl %edi, 28(%r10) - popq %rbp + popq %r12 popq %rbx ret 3:
arch/x86/net/Makefile +2 -0
@@ -1,4 +1,6 @@ # # Arch-specific network modules # +OBJECT_FILES_NON_STANDARD_bpf_jit.o += y + obj-$(CONFIG_BPF_JIT) += bpf_jit.o bpf_jit_comp.o
arch/x86/platform/efi/Makefile +1 -0
@@ -1,4 +1,5 @@ OBJECT_FILES_NON_STANDARD_efi_thunk_$(BITS).o := y +OBJECT_FILES_NON_STANDARD_efi_stub_$(BITS).o := y obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o
arch/x86/power/Makefile +2 -0
@@ -1,3 +1,5 @@ +OBJECT_FILES_NON_STANDARD_hibernate_asm_$(BITS).o := y + # __restore_processor_state() restores %gs after S3 resume and so should not # itself be stack-protected nostackp := $(call cc-option, -fno-stack-protector)
arch/x86/xen/Makefile +3 -0
@@ -1,3 +1,6 @@ +OBJECT_FILES_NON_STANDARD_xen-asm_$(BITS).o := y +OBJECT_FILES_NON_STANDARD_xen-pvh.o := y + ifdef CONFIG_FUNCTION_TRACER # Do not profile debug and lowlevel utilities CFLAGS_REMOVE_spinlock.o = -pg
kernel/kexec_core.c +3 -1
@@ -38,6 +38,7 @@#include <linux/syscore_ops.h> #include <linux/compiler.h> #include <linux/hugetlb.h> +#include <linux/frame.h> #include <asm/page.h> #include <asm/sections.h>
@@ -874,7 +875,7 @@ int kexec_load_disabled;* only when panic_cpu holds the current CPU number; this is the only CPU * which processes crash_kexec routines. */ -void __crash_kexec (struct pt_regs *regs) +void __noclone __crash_kexec (struct pt_regs *regs) {/* Take the kexec_mutex here to prevent sys_kexec_load * running on one cpu from replacing the crash kernel
@@ -896,6 +897,7 @@ void __crash_kexec(struct pt_regs *regs)mutex_unlock (&kexec_mutex); } } +STACK_FRAME_NON_STANDARD (__crash_kexec);void crash_kexec (struct pt_regs *regs) {
PATCH v2 3/8 objtool: stack validation 2.0
In reply to Josh PoimboeufThis is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores.
In addition to making stack validation more robust, this also paves the way for undwarf generation.
--- tools/objtool/Documentation/stack-validation.txt | 153 +++-- tools/objtool/Makefile | 2 +- tools/objtool/arch.h | 64 ++- tools/objtool/arch/x86/decode.c | 400 ++++++++++++-- tools/objtool/cfi.h | 55 ++ tools/objtool/check.c | 676 ++++++++++++++++++----- tools/objtool/check.h | 19 +- tools/objtool/elf.c | 59 +- tools/objtool/elf.h | 6 +- tools/objtool/special.c | 6 +- tools/objtool/warn.h | 10 + 11 files changed, 1130 insertions(+), 320 deletions(-) create mode 100644 tools/objtool/cfi.h
tools/objtool/Documentation/stack-validation.txt +65 -88
@@ -127,28 +127,13 @@ b) 100% reliable stack traces for DWARF enabled kernels c) Higher live patching compatibility rate - (NOTE: This is not yet implemented) - - Currently with CONFIG_LIVEPATCH there's a basic live patching - framework which is safe for roughly 85-90% of "security" fixes. But - patches can't have complex features like function dependency or - prototype changes, or data structure changes. - - There's a strong need to support patches which have the more complex - features so that the patch compatibility rate for security fixes can - eventually approach something resembling 100%. To achieve that, a - "consistency model" is needed, which allows tasks to be safely - transitioned from an unpatched state to a patched state. - - One of the key requirements of the currently proposed livepatch - consistency model [*] is that it needs to walk the stack of each - sleeping task to determine if it can be transitioned to the patched - state. If objtool can ensure that stack traces are reliable, this - consistency model can be used and the live patching compatibility - rate can be improved significantly. - - [*] https://lkml.kernel.org/r/[email protected] + Livepatch has an optional "consistency model", which is needed for + more complex patches. In order for the consistency model to work, + stack traces need to be reliable (or an unreliable condition needs to + be detectable). Objtool makes that possible. + For more details, see the livepatch documentation in the Linux kernel + source tree at Documentation/livepatch/livepatch.txt. Rules -----
@@ -201,80 +186,84 @@ To achieve the validation, objtool enforces the following rules: return normally. -Errors in .S files ------------------- +Objtool warnings +---------------- -If you're getting an error in a compiled .S file which you don't -understand, first make sure that the affected code follows the above -rules. +For asm files, if you're getting an error which doesn't make sense, +first make sure that the affected code follows the above rules. + +For C files, the common culprits are inline asm statements and calls to +"noreturn" functions. See below for more details. + +Another possible cause for errors in C code is if the Makefile removes +-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. Here are some examples of common warnings reported by objtool, what they mean, and suggestions for how to fix them. -1. asm_file.o: warning: objtool: func()+0x128: call without frame pointer save/setup +1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup The func() function made a function call without first saving and/or - updating the frame pointer. - - If func() is indeed a callable function, add proper frame pointer - logic using the FRAME_BEGIN and FRAME_END macros. Otherwise, remove - its ELF function annotation by changing ENDPROC to END. + updating the frame pointer, and CONFIG_FRAME_POINTER is enabled. - If you're getting this error in a .c file, see the "Errors in .c - files" section. + If the error is for an asm file, and func() is indeed a callable + function, add proper frame pointer logic using the FRAME_BEGIN and + FRAME_END macros. Otherwise, if it's not a callable function, remove + its ELF function annotation by changing ENDPROC to END, and instead + use the manual CFI hint macros in asm/undwarf.h. + If it's a GCC-compiled .c file, the error may be because the function + uses an inline asm() statement which has a "call" instruction. An + asm() statement with a call instruction must declare the use of the + stack pointer in its output operand. For example, on x86_64: -2. asm_file.o: warning: objtool: .text+0x53: return instruction outside of a callable function - - A return instruction was detected, but objtool couldn't find a way - for a callable function to reach the instruction. + register void *__sp asm("rsp"); + asm volatile("call func" : "+r" (__sp)); - If the return instruction is inside (or reachable from) a callable - function, the function needs to be annotated with the ENTRY/ENDPROC - macros. + Otherwise the stack frame may not get created before the call. - If you _really_ need a return instruction outside of a function, and - are 100% sure that it won't affect stack traces, you can tell - objtool to ignore it. See the "Adding exceptions" section below. +2. file.o: warning: objtool: .text+0x53: unreachable instruction -3. asm_file.o: warning: objtool: func()+0x9: function has unreachable instruction + Objtool couldn't find a code path to reach the instruction. - The instruction lives inside of a callable function, but there's no - possible control flow path from the beginning of the function to the - instruction. + If the error is for an asm file, and the instruction is inside (or + reachable from) a callable function, the function should be annotated + with the ENTRY/ENDPROC macros (ENDPROC is the important one). + Otherwise, the code should probably be annotated with the CFI hint + macros in asm/undwarf.h so objtool and the unwinder can know the + stack state associated with the code. - If the instruction is actually needed, and it's actually in a - callable function, ensure that its function is properly annotated - with ENTRY/ENDPROC. + If you're 100% sure the code won't affect stack traces, or if you're + a just a bad person, you can tell objtool to ignore it. See the + "Adding exceptions" section below. If it's not actually in a callable function (e.g. kernel entry code), change ENDPROC to END. -4. asm_file.o: warning: objtool: func(): can't find starting instruction +4. file.o: warning: objtool: func(): can't find starting instruction or - asm_file.o: warning: objtool: func()+0x11dd: can't decode instruction + file.o: warning: objtool: func()+0x11dd: can't decode instruction - Did you put data in a text section? If so, that can confuse + Does the file have data in a text section? If so, that can confuse objtool's instruction decoder. Move the data to a more appropriate section like .data or .rodata. -5. asm_file.o: warning: objtool: func()+0x6: kernel entry/exit from callable instruction - - This is a kernel entry/exit instruction like sysenter or sysret. - Such instructions aren't allowed in a callable function, and are most - likely part of the kernel entry code. +5. file.o: warning: objtool: func()+0x6: unsupported instruction in callable function - If the instruction isn't actually in a callable function, change - ENDPROC to END. + This is a kernel entry/exit instruction like sysenter or iret. Such + instructions aren't allowed in a callable function, and are most + likely part of the kernel entry code. They should usually not have + the callable function annotation (ENDPROC) and should always be + annotated with the CFI hint macros in asm/undwarf.h. -6. asm_file.o: warning: objtool: func()+0x26: sibling call from callable instruction with changed frame pointer +6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame - This is a dynamic jump or a jump to an undefined symbol. Stacktool + This is a dynamic jump or a jump to an undefined symbol. Objtool assumed it's a sibling call and detected that the frame pointer wasn't first restored to its original state.
@@ -282,24 +271,28 @@ they mean, and suggestions for how to fix them. destination code to the local file. If the instruction is not actually in a callable function (e.g. - kernel entry code), change ENDPROC to END. + kernel entry code), change ENDPROC to END and annotate manually with + the CFI hint macros in asm/undwarf.h. -7. asm_file: warning: objtool: func()+0x5c: frame pointer state mismatch +7. file: warning: objtool: func()+0x5c: stack state mismatch The instruction's frame pointer state is inconsistent, depending on which execution path was taken to reach the instruction. - Make sure the function pushes and sets up the frame pointer (for - x86_64, this means rbp) at the beginning of the function and pops it - at the end of the function. Also make sure that no other code in the - function touches the frame pointer. + Make sure that, when CONFIG_FRAME_POINTER is enabled, the function + pushes and sets up the frame pointer (for x86_64, this means rbp) at + the beginning of the function and pops it at the end of the function. + Also make sure that no other code in the function touches the frame + pointer. + Another possibility is that the code has some asm or inline asm which + does some unusual things to the stack or the frame pointer. In such + cases it's probably appropriate to use the CFI hint macros in + asm/undwarf.h. -Errors in .c files ------------------- -1. c_file.o: warning: objtool: funcA() falls through to next function funcB() +8. file.o: warning: objtool: funcA() falls through to next function funcB() This means that funcA() doesn't end with a return instruction or an unconditional jump, and that objtool has determined that the function
@@ -318,22 +311,6 @@ Errors in .c files might be corrupt due to a gcc bug. For more details, see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646 -2. If you're getting any other objtool error in a compiled .c file, it - may be because the file uses an asm() statement which has a "call" - instruction. An asm() statement with a call instruction must declare - the use of the stack pointer in its output operand. For example, on - x86_64: - - register void *__sp asm("rsp"); - asm volatile("call func" : "+r" (__sp)); - - Otherwise the stack frame may not get created before the call. - -3. Another possible cause for errors in C code is if the Makefile removes - -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. - -Also see the above section for .S file errors for more information what -the individual error messages mean. If the error doesn't seem to make sense, it could be a bug in objtool. Feel free to ask the objtool maintainer for help.
tools/objtool/Makefile +1 -1
@@ -25,7 +25,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o all: $(OBJTOOL) INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi -CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES) +CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES) LDFLAGS += -lelf $(LIBSUBCMD) # Allow old libelf to be used:
tools/objtool/arch.h +51 -13
76 lines not on this page · raw message
tools/objtool/arch/x86/decode.c +362 -38
476 lines not on this page · raw message
tools/objtool/cfi.h new file +55 -0
55 lines not on this page · raw message
tools/objtool/check.c +535 -141
943 lines not on this page · raw message
tools/objtool/check.h +17 -2
@@ -20,22 +20,34 @@#include <stdbool.h> #include "elf.h" +#include "cfi.h" #include "arch.h" #include <linux/hashtable.h> +struct insn_state { +struct cfi_reg cfa; +struct cfi_reg regs[CFI_NUM_REGS ]; +int stack_size; +bool bp_scratch; +bool drap; +int drap_reg; +}; +struct instruction {struct list_head list;struct hlist_node hash;struct section *sec;unsigned long offset; -unsigned int len, state; +unsigned int len;unsigned char type;unsigned long immediate; -bool alt_group, visited, dead_end; +bool alt_group, visited, dead_end, ignore;struct symbol *call_dest;struct instruction *jump_dest;struct list_head alts;struct symbol *func; +struct stack_op stack_op; +struct insn_state state; };struct objtool_file {
@@ -48,4 +60,7 @@ struct objtool_file {int check (const char *objname,bool nofp); +#define for_each_insn (file, insn) \ + list_for_each_entry(insn, &file->insn_list, list) +#endif /* _CHECK_H */
tools/objtool/elf.c +28 -31
147 lines not on this page · raw message
tools/objtool/elf.h +3 -3
@@ -37,10 +37,9 @@ struct section {DECLARE_HASHTABLE (rela_hash,16 );struct section *base, *rela;struct symbol *sym; -Elf_Data *elf_data; +Elf_Data *data;char *name;int idx; -unsigned long data;unsigned int len; };
@@ -86,6 +85,7 @@ struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,struct symbol *find_containing_func (struct section *sec,unsigned long offset);void elf_close (struct elf *elf); - +#define for_each_sec (file, sec) \ + list_for_each_entry(sec, &file->elf->sections, list)#endif /* _OBJTOOL_ELF_H */
tools/objtool/special.c +3 -3
@@ -91,16 +91,16 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry, alt->jump_or_nop = entry->jump_or_nop;if (alt->group) { - alt->orig_len = *(unsigned char *)(sec->data + offset + + alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset + entry->orig_len); - alt->new_len = *(unsigned char *)(sec->data + offset + + alt->new_len = *(unsigned char *)(sec->data->d_buf + offset + entry->new_len); }if (entry->feature) {unsigned short feature; - feature = *(unsigned short *)(sec->data + offset + + feature = *(unsigned short *)(sec->data->d_buf + offset + entry->feature);/*
tools/objtool/warn.h +10 -0
@@ -18,6 +18,13 @@#ifndef _WARN_H#define _WARN_H +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "elf.h" +extern const char *objname;static inline char *offstr (struct section *sec,unsigned long offset)
@@ -57,4 +64,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)free (_str); \ }) +#define WARN_ELF (format, ...) \ + WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) +#endif /* _WARN_H */
Signature · 1 line
2.7.5
Re: [PATCH v2 3/8] objtool: stack validation 2.0
Quoted 8 lines · Josh Poimboeuf
This is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores.
In addition to making stack validation more robust, this also paves the way for undwarf generation.
Signed-off-by Josh Poimboeuf <[email protected]>
Note, I have applied the first 3 patches, and got a bunch of new warnings on x86 64-bit allmodconfig:
arch/x86/kernel/alternative.o: warning: objtool: do_sync_core()+0x1b: unsupported instruction in callable function arch/x86/kernel/alternative.o: warning: objtool: text_poke()+0x1a8: unsupported instruction in callable function arch/x86/kernel/ftrace.o: warning: objtool: do_sync_core()+0x16: unsupported instruction in callable function arch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: machine_check_poll()+0x166: unsupported instruction in callable function arch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: do_machine_check()+0x147: unsupported instruction in callable function
(That's the vmlinux build - plus 4 more warnings in the modules build.)
That's with GCC 5.3.1.
Let me know if you need any more info.
Thanks,
Ingo
Re: [PATCH v2 3/8] objtool: stack validation 2.0
Quoted 21 lines · Ingo Molnar
Quoted 8 lines · Josh Poimboeuf
This is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores.
In addition to making stack validation more robust, this also paves the way for undwarf generation.
Signed-off-by Josh Poimboeuf <[email protected]>Note, I have applied the first 3 patches, and got a bunch of new warnings on x86 64-bit allmodconfig:
arch/x86/kernel/alternative.o: warning: objtool: do_sync_core()+0x1b: unsupported instruction in callable functionarch/x86/kernel/alternative.o: warning: objtool: text_poke()+0x1a8: unsupported instruction in callable functionarch/x86/kernel/ftrace.o: warning: objtool: do_sync_core()+0x16: unsupported instruction in callable functionarch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: machine_check_poll()+0x166: unsupported instruction in callable functionarch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: do_machine_check()+0x147: unsupported instruction in callable function
Quoted 5 lines
(That's the vmlinux build - plus 4 more warnings in the modules build.)
That's with GCC 5.3.1.
Let me know if you need any more info.
Hm, this is odd. I tried with GCC 5.3.0 (which should presumably be almost identical to 5.3.1) and I don't see any warnings. Can you send me one of the object files it's complaining about?
Signature · 1 line
Josh
Re: [PATCH v2 3/8] objtool: stack validation 2.0
Quoted 24 lines · Josh Poimboeuf
Quoted 23 lines · Ingo Molnar
Quoted 8 lines · Josh Poimboeuf
This is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores.
In addition to making stack validation more robust, this also paves the way for undwarf generation.
Signed-off-by Josh Poimboeuf <[email protected]>Note, I have applied the first 3 patches, and got a bunch of new warnings on x86 64-bit allmodconfig:
arch/x86/kernel/alternative.o: warning: objtool: do_sync_core()+0x1b: unsupported instruction in callable functionarch/x86/kernel/alternative.o: warning: objtool: text_poke()+0x1a8: unsupported instruction in callable functionarch/x86/kernel/ftrace.o: warning: objtool: do_sync_core()+0x16: unsupported instruction in callable functionarch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: machine_check_poll()+0x166: unsupported instruction in callable functionarch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: do_machine_check()+0x147: unsupported instruction in callable function(That's the vmlinux build - plus 4 more warnings in the modules build.)
Quoted 7 lines
Quoted 3 lines
That's with GCC 5.3.1.
Let me know if you need any more info.
Hm, this is odd. I tried with GCC 5.3.0 (which should presumably be almost identical to 5.3.1) and I don't see any warnings. Can you send me one of the object files it's complaining about?
Oh wait, never mind. Now that I'm actually reading the warnings, it makes sense. Those are warnings I fixed in a later patch in the series.
I'll try to come up with something to fix those without pulling in the rest of the patches.
Signature · 1 line
Josh
PATCH 1/1 objtool: silence warnings for functions which use iret
In reply to Ingo MolnarQuoted 21 lines · Ingo Molnar
Quoted 8 lines · Josh Poimboeuf
This is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores.
In addition to making stack validation more robust, this also paves the way for undwarf generation.
Signed-off-by Josh Poimboeuf <[email protected]>Note, I have applied the first 3 patches, and got a bunch of new warnings on x86 64-bit allmodconfig:
arch/x86/kernel/alternative.o: warning: objtool: do_sync_core()+0x1b: unsupported instruction in callable functionarch/x86/kernel/alternative.o: warning: objtool: text_poke()+0x1a8: unsupported instruction in callable functionarch/x86/kernel/ftrace.o: warning: objtool: do_sync_core()+0x16: unsupported instruction in callable functionarch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: machine_check_poll()+0x166: unsupported instruction in callable functionarch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: do_machine_check()+0x147: unsupported instruction in callable function
Quoted 3 lines
(That's the vmlinux build - plus 4 more warnings in the modules build.)
That's with GCC 5.3.1.
Here's the fix. It can be squashed into the 3rd commit ("objtool:
Implement stack validation 2.0") or can be added as a standalone commit, whichever you prefer.
----
From: Josh Poimboeuf <[email protected]>
Subject: [PATCH] objtool: silence warnings for functions which use iret
Previously, objtool ignored functions which have the 'iret' instruction in them. That's because it assumed that such functions know what they're doing with respect to frame pointers.
With the new "objtool 2.0" changes, it stopped ignoring such functions, and started complaining about them:
arch/x86/kernel/alternative.o: warning: objtool: do_sync_core()+0x1b: unsupported instruction in callable function arch/x86/kernel/alternative.o: warning: objtool: text_poke()+0x1a8: unsupported instruction in callable function arch/x86/kernel/ftrace.o: warning: objtool: do_sync_core()+0x16: unsupported instruction in callable function arch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: machine_check_poll()+0x166: unsupported instruction in callable function arch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: do_machine_check()+0x147: unsupported instruction in callable function
Silence those warnings for now. They can be re-enabled later, once we have unwind hints which will allow the code to annotate the iret usages.
--- tools/objtool/check.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
tools/objtool/check.c +6 -8
@@ -100,6 +100,7 @@ static bool gcov_enabled(struct objtool_file *file)static bool ignore_func (struct objtool_file *file,struct symbol *func) {struct rela *rela; +struct instruction *insn;/* check for STACK_FRAME_NON_STANDARD */ if (file->whitelist && file->whitelist->rela)
@@ -112,6 +113,11 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func)return true; } +/* check if it has a context switching instruction */ +func_for_each_insn (file, func, insn) +if (insn->type ==INSN_CONTEXT_SWITCH ) +return true; +return false; }
@@ -1446,14 +1452,6 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,return 0 ; -case INSN_CONTEXT_SWITCH : -if (func) { -WARN_FUNC ("unsupported instruction in callable function" , - sec, insn->offset); -return 1 ; - } -return 0 ; -case INSN_STACK :if (update_insn_state (insn, &state))return -1 ;
Signature · 1 line
2.7.5
tip-bot for Josh Poimboeuf
merge bot[tip:core/objtool] objtool: Silence warnings for functions which use IRET
objtool: Silence warnings for functions which use IRET
Commit-ID: 2513cbf9d622d85268655bfd787d4f004342cfc9 Gitweb: http://git.kernel.org/tip/2513cbf9d622d85268655bfd787d4f004342cfc9 Author: Josh Poimboeuf <[email protected]> AuthorDate: Fri, 30 Jun 2017 09:09:34 -0500 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 30 Jun 2017 19:43:50 +0200
objtool: Silence warnings for functions which use IRET
Previously, objtool ignored functions which have the IRET instruction in them. That's because it assumed that such functions know what they're doing with respect to frame pointers.
With the new "objtool 2.0" changes, it stopped ignoring such functions, and started complaining about them:
arch/x86/kernel/alternative.o: warning: objtool: do_sync_core()+0x1b: unsupported instruction in callable function arch/x86/kernel/alternative.o: warning: objtool: text_poke()+0x1a8: unsupported instruction in callable function arch/x86/kernel/ftrace.o: warning: objtool: do_sync_core()+0x16: unsupported instruction in callable function arch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: machine_check_poll()+0x166: unsupported instruction in callable function arch/x86/kernel/cpu/mcheck/mce.o: warning: objtool: do_machine_check()+0x147: unsupported instruction in callable function
Silence those warnings for now. They can be re-enabled later, once we have unwind hints which will allow the code to annotate the IRET usages.
--- tools/objtool/check.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
tools/objtool/check.c +6 -8
@@ -100,6 +100,7 @@ static bool gcov_enabled(struct objtool_file *file)static bool ignore_func (struct objtool_file *file,struct symbol *func) {struct rela *rela; +struct instruction *insn;/* check for STACK_FRAME_NON_STANDARD */ if (file->whitelist && file->whitelist->rela)
@@ -112,6 +113,11 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func)return true; } +/* check if it has a context switching instruction */ +func_for_each_insn (file, func, insn) +if (insn->type ==INSN_CONTEXT_SWITCH ) +return true; +return false; }
@@ -1446,14 +1452,6 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,return 0 ; -case INSN_CONTEXT_SWITCH : -if (func) { -WARN_FUNC ("unsupported instruction in callable function" , - sec, insn->offset); -return 1 ; - } -return 0 ; -case INSN_STACK :if (update_insn_state (insn, &state))return -1 ;
tip-bot for Josh Poimboeuf
merge bot[tip:core/objtool] objtool: Implement stack validation 2.0
In reply to Josh Poimboeufobjtool: Implement stack validation 2.0
Commit-ID: baa41469a7b992c1e3db2a39854219cc7442e48f Gitweb: http://git.kernel.org/tip/baa41469a7b992c1e3db2a39854219cc7442e48f Author: Josh Poimboeuf <[email protected]> AuthorDate: Wed, 28 Jun 2017 10:11:07 -0500 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 30 Jun 2017 10:19:19 +0200
objtool: Implement stack validation 2.0
This is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores.
In addition to making stack validation more robust, this also paves the way for undwarf generation.
--- tools/objtool/Documentation/stack-validation.txt | 153 +++-- tools/objtool/Makefile | 2 +- tools/objtool/arch.h | 64 ++- tools/objtool/arch/x86/decode.c | 400 ++++++++++++-- tools/objtool/cfi.h | 55 ++ tools/objtool/check.c | 676 ++++++++++++++++++----- tools/objtool/check.h | 19 +- tools/objtool/elf.c | 59 +- tools/objtool/elf.h | 6 +- tools/objtool/special.c | 6 +- tools/objtool/warn.h | 10 + 11 files changed, 1130 insertions(+), 320 deletions(-)
tools/objtool/Documentation/stack-validation.txt +65 -88
@@ -127,28 +127,13 @@ b) 100% reliable stack traces for DWARF enabled kernels c) Higher live patching compatibility rate - (NOTE: This is not yet implemented) - - Currently with CONFIG_LIVEPATCH there's a basic live patching - framework which is safe for roughly 85-90% of "security" fixes. But - patches can't have complex features like function dependency or - prototype changes, or data structure changes. - - There's a strong need to support patches which have the more complex - features so that the patch compatibility rate for security fixes can - eventually approach something resembling 100%. To achieve that, a - "consistency model" is needed, which allows tasks to be safely - transitioned from an unpatched state to a patched state. - - One of the key requirements of the currently proposed livepatch - consistency model [*] is that it needs to walk the stack of each - sleeping task to determine if it can be transitioned to the patched - state. If objtool can ensure that stack traces are reliable, this - consistency model can be used and the live patching compatibility - rate can be improved significantly. - - [*] https://lkml.kernel.org/r/[email protected] + Livepatch has an optional "consistency model", which is needed for + more complex patches. In order for the consistency model to work, + stack traces need to be reliable (or an unreliable condition needs to + be detectable). Objtool makes that possible. + For more details, see the livepatch documentation in the Linux kernel + source tree at Documentation/livepatch/livepatch.txt. Rules -----
@@ -201,80 +186,84 @@ To achieve the validation, objtool enforces the following rules: return normally. -Errors in .S files ------------------- +Objtool warnings +---------------- -If you're getting an error in a compiled .S file which you don't -understand, first make sure that the affected code follows the above -rules. +For asm files, if you're getting an error which doesn't make sense, +first make sure that the affected code follows the above rules. + +For C files, the common culprits are inline asm statements and calls to +"noreturn" functions. See below for more details. + +Another possible cause for errors in C code is if the Makefile removes +-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. Here are some examples of common warnings reported by objtool, what they mean, and suggestions for how to fix them. -1. asm_file.o: warning: objtool: func()+0x128: call without frame pointer save/setup +1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup The func() function made a function call without first saving and/or - updating the frame pointer. - - If func() is indeed a callable function, add proper frame pointer - logic using the FRAME_BEGIN and FRAME_END macros. Otherwise, remove - its ELF function annotation by changing ENDPROC to END. + updating the frame pointer, and CONFIG_FRAME_POINTER is enabled. - If you're getting this error in a .c file, see the "Errors in .c - files" section. + If the error is for an asm file, and func() is indeed a callable + function, add proper frame pointer logic using the FRAME_BEGIN and + FRAME_END macros. Otherwise, if it's not a callable function, remove + its ELF function annotation by changing ENDPROC to END, and instead + use the manual CFI hint macros in asm/undwarf.h. + If it's a GCC-compiled .c file, the error may be because the function + uses an inline asm() statement which has a "call" instruction. An + asm() statement with a call instruction must declare the use of the + stack pointer in its output operand. For example, on x86_64: -2. asm_file.o: warning: objtool: .text+0x53: return instruction outside of a callable function - - A return instruction was detected, but objtool couldn't find a way - for a callable function to reach the instruction. + register void *__sp asm("rsp"); + asm volatile("call func" : "+r" (__sp)); - If the return instruction is inside (or reachable from) a callable - function, the function needs to be annotated with the ENTRY/ENDPROC - macros. + Otherwise the stack frame may not get created before the call. - If you _really_ need a return instruction outside of a function, and - are 100% sure that it won't affect stack traces, you can tell - objtool to ignore it. See the "Adding exceptions" section below. +2. file.o: warning: objtool: .text+0x53: unreachable instruction -3. asm_file.o: warning: objtool: func()+0x9: function has unreachable instruction + Objtool couldn't find a code path to reach the instruction. - The instruction lives inside of a callable function, but there's no - possible control flow path from the beginning of the function to the - instruction. + If the error is for an asm file, and the instruction is inside (or + reachable from) a callable function, the function should be annotated + with the ENTRY/ENDPROC macros (ENDPROC is the important one). + Otherwise, the code should probably be annotated with the CFI hint + macros in asm/undwarf.h so objtool and the unwinder can know the + stack state associated with the code. - If the instruction is actually needed, and it's actually in a - callable function, ensure that its function is properly annotated - with ENTRY/ENDPROC. + If you're 100% sure the code won't affect stack traces, or if you're + a just a bad person, you can tell objtool to ignore it. See the + "Adding exceptions" section below. If it's not actually in a callable function (e.g. kernel entry code), change ENDPROC to END. -4. asm_file.o: warning: objtool: func(): can't find starting instruction +4. file.o: warning: objtool: func(): can't find starting instruction or - asm_file.o: warning: objtool: func()+0x11dd: can't decode instruction + file.o: warning: objtool: func()+0x11dd: can't decode instruction - Did you put data in a text section? If so, that can confuse + Does the file have data in a text section? If so, that can confuse objtool's instruction decoder. Move the data to a more appropriate section like .data or .rodata. -5. asm_file.o: warning: objtool: func()+0x6: kernel entry/exit from callable instruction - - This is a kernel entry/exit instruction like sysenter or sysret. - Such instructions aren't allowed in a callable function, and are most - likely part of the kernel entry code. +5. file.o: warning: objtool: func()+0x6: unsupported instruction in callable function - If the instruction isn't actually in a callable function, change - ENDPROC to END. + This is a kernel entry/exit instruction like sysenter or iret. Such + instructions aren't allowed in a callable function, and are most + likely part of the kernel entry code. They should usually not have + the callable function annotation (ENDPROC) and should always be + annotated with the CFI hint macros in asm/undwarf.h. -6. asm_file.o: warning: objtool: func()+0x26: sibling call from callable instruction with changed frame pointer +6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame - This is a dynamic jump or a jump to an undefined symbol. Stacktool + This is a dynamic jump or a jump to an undefined symbol. Objtool assumed it's a sibling call and detected that the frame pointer wasn't first restored to its original state.
@@ -282,24 +271,28 @@ they mean, and suggestions for how to fix them. destination code to the local file. If the instruction is not actually in a callable function (e.g. - kernel entry code), change ENDPROC to END. + kernel entry code), change ENDPROC to END and annotate manually with + the CFI hint macros in asm/undwarf.h. -7. asm_file: warning: objtool: func()+0x5c: frame pointer state mismatch +7. file: warning: objtool: func()+0x5c: stack state mismatch The instruction's frame pointer state is inconsistent, depending on which execution path was taken to reach the instruction. - Make sure the function pushes and sets up the frame pointer (for - x86_64, this means rbp) at the beginning of the function and pops it - at the end of the function. Also make sure that no other code in the - function touches the frame pointer. + Make sure that, when CONFIG_FRAME_POINTER is enabled, the function + pushes and sets up the frame pointer (for x86_64, this means rbp) at + the beginning of the function and pops it at the end of the function. + Also make sure that no other code in the function touches the frame + pointer. + Another possibility is that the code has some asm or inline asm which + does some unusual things to the stack or the frame pointer. In such + cases it's probably appropriate to use the CFI hint macros in + asm/undwarf.h. -Errors in .c files ------------------- -1. c_file.o: warning: objtool: funcA() falls through to next function funcB() +8. file.o: warning: objtool: funcA() falls through to next function funcB() This means that funcA() doesn't end with a return instruction or an unconditional jump, and that objtool has determined that the function
@@ -318,22 +311,6 @@ Errors in .c files might be corrupt due to a gcc bug. For more details, see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646 -2. If you're getting any other objtool error in a compiled .c file, it - may be because the file uses an asm() statement which has a "call" - instruction. An asm() statement with a call instruction must declare - the use of the stack pointer in its output operand. For example, on - x86_64: - - register void *__sp asm("rsp"); - asm volatile("call func" : "+r" (__sp)); - - Otherwise the stack frame may not get created before the call. - -3. Another possible cause for errors in C code is if the Makefile removes - -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. - -Also see the above section for .S file errors for more information what -the individual error messages mean. If the error doesn't seem to make sense, it could be a bug in objtool. Feel free to ask the objtool maintainer for help.
tools/objtool/Makefile +1 -1
@@ -25,7 +25,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o all: $(OBJTOOL) INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi -CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES) +CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES) LDFLAGS += -lelf $(LIBSUBCMD) # Allow old libelf to be used:
tools/objtool/arch.h +51 -13
76 lines not on this page · raw message
tools/objtool/arch/x86/decode.c +362 -38
476 lines not on this page · raw message
tools/objtool/cfi.h new file +55 -0
55 lines not on this page · raw message
tools/objtool/check.c +535 -141
943 lines not on this page · raw message
tools/objtool/check.h +17 -2
@@ -20,22 +20,34 @@#include <stdbool.h> #include "elf.h" +#include "cfi.h" #include "arch.h" #include <linux/hashtable.h> +struct insn_state { +struct cfi_reg cfa; +struct cfi_reg regs[CFI_NUM_REGS ]; +int stack_size; +bool bp_scratch; +bool drap; +int drap_reg; +}; +struct instruction {struct list_head list;struct hlist_node hash;struct section *sec;unsigned long offset; -unsigned int len, state; +unsigned int len;unsigned char type;unsigned long immediate; -bool alt_group, visited, dead_end; +bool alt_group, visited, dead_end, ignore;struct symbol *call_dest;struct instruction *jump_dest;struct list_head alts;struct symbol *func; +struct stack_op stack_op; +struct insn_state state; };struct objtool_file {
@@ -48,4 +60,7 @@ struct objtool_file {int check (const char *objname,bool nofp); +#define for_each_insn (file, insn) \ + list_for_each_entry(insn, &file->insn_list, list) +#endif /* _CHECK_H */
tools/objtool/elf.c +28 -31
147 lines not on this page · raw message
tools/objtool/elf.h +3 -3
@@ -37,10 +37,9 @@ struct section {DECLARE_HASHTABLE (rela_hash,16 );struct section *base, *rela;struct symbol *sym; -Elf_Data *elf_data; +Elf_Data *data;char *name;int idx; -unsigned long data;unsigned int len; };
@@ -86,6 +85,7 @@ struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,struct symbol *find_containing_func (struct section *sec,unsigned long offset);void elf_close (struct elf *elf); - +#define for_each_sec (file, sec) \ + list_for_each_entry(sec, &file->elf->sections, list)#endif /* _OBJTOOL_ELF_H */
tools/objtool/special.c +3 -3
@@ -91,16 +91,16 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry, alt->jump_or_nop = entry->jump_or_nop;if (alt->group) { - alt->orig_len = *(unsigned char *)(sec->data + offset + + alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset + entry->orig_len); - alt->new_len = *(unsigned char *)(sec->data + offset + + alt->new_len = *(unsigned char *)(sec->data->d_buf + offset + entry->new_len); }if (entry->feature) {unsigned short feature; - feature = *(unsigned short *)(sec->data + offset + + feature = *(unsigned short *)(sec->data->d_buf + offset + entry->feature);/*
tools/objtool/warn.h +10 -0
@@ -18,6 +18,13 @@#ifndef _WARN_H#define _WARN_H +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "elf.h" +extern const char *objname;static inline char *offstr (struct section *sec,unsigned long offset)
@@ -57,4 +64,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)free (_str); \ }) +#define WARN_ELF (format, ...) \ + WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) +#endif /* _WARN_H */
PATCH v2 4/8 objtool: add undwarf debuginfo generation
In reply to Josh PoimboeufNow that objtool knows the states of all registers on the stack for each instruction, it's straightforward to generate debuginfo for an unwinder to use.
Instead of generating DWARF, generate a new format called undwarf, which is more suitable for an in-kernel unwinder. See tools/objtool/Documentation/undwarf.txt for a more detailed description of this new debuginfo format and why it's preferable to DWARF.
---
tools/objtool/Build | 3 +
tools/objtool/Documentation/stack-validation.txt | 46 ++---
tools/objtool/builtin-check.c | 2 +-
tools/objtool/builtin-undwarf.c | 70 ++++++++
tools/objtool/builtin.h | 1 +
tools/objtool/check.c | 59 ++++++-
tools/objtool/check.h | 15 +-
tools/objtool/elf.c | 212 ++++++++++++++++++++--
tools/objtool/elf.h | 15 +-
tools/objtool/objtool.c | 3 +-
tools/objtool/undwarf-types.h | 81 +++++++++
tools/objtool/{builtin.h => undwarf.h} | 18 +-
tools/objtool/undwarf_dump.c | 212 ++++++++++++++++++++++
tools/objtool/undwarf_gen.c | 215 +++++++++++++++++++++++
14 files changed, 892 insertions(+), 60 deletions(-)
create mode 100644 tools/objtool/builtin-undwarf.c
create mode 100644 tools/objtool/undwarf-types.h
copy tools/objtool/{builtin.h => undwarf.h} (67%)
create mode 100644 tools/objtool/undwarf_dump.c
create mode 100644 tools/objtool/undwarf_gen.ctools/objtool/Build +3 -0
@@ -1,6 +1,9 @@ objtool-y += arch/$(SRCARCH)/ objtool-y += builtin-check.o +objtool-y += builtin-undwarf.o objtool-y += check.o +objtool-y += undwarf_gen.o +objtool-y += undwarf_dump.o objtool-y += elf.o objtool-y += special.o objtool-y += objtool.o
tools/objtool/Documentation/stack-validation.txt +12 -34
64 lines not on this page · raw message
tools/objtool/builtin-check.c +1 -1
@@ -52,5 +52,5 @@ int cmd_check(int argc, const char **argv) objname = argv[0 ]; -return check (objname, nofp); +return check (objname, nofp, false); }
tools/objtool/builtin-undwarf.c new file +70 -0
70 lines not on this page · raw message
tools/objtool/builtin.h +1 -0
@@ -18,5 +18,6 @@#define _BUILTIN_Hextern int cmd_check (int argc,const char **argv); +extern int cmd_undwarf (int argc,const char **argv);#endif /* _BUILTIN_H */
tools/objtool/check.c +55 -4
107 lines not on this page · raw message
tools/objtool/check.h +14 -1
@@ -22,12 +22,14 @@#include "elf.h" #include "cfi.h" #include "arch.h" +#include "undwarf.h" #include <linux/hashtable.h> struct insn_state {struct cfi_reg cfa;struct cfi_reg regs[CFI_NUM_REGS ];int stack_size; +unsigned char type;bool bp_scratch;bool drap;int drap_reg;
@@ -48,6 +50,7 @@ struct instruction {struct symbol *func;struct stack_op stack_op;struct insn_state state; +struct undwarf undwarf; };struct objtool_file {
@@ -58,9 +61,19 @@ struct objtool_file { bool ignore_unreachables, c_file; }; -int check (const char *objname,bool nofp); +int check (const char *objname,bool nofp,bool undwarf); + +struct instruction *find_insn (struct objtool_file *file, +struct section *sec,unsigned long offset);#define for_each_insn (file, insn) \ list_for_each_entry(insn, &file->insn_list, list) +#define sec_for_each_insn (file, sec, insn) \ + for (insn = find_insn(file, sec, 0); \ + insn && &insn->list != &file->insn_list && \ + insn->sec == sec; \ + insn = list_next_entry(insn, list)) + +#endif /* _CHECK_H */
tools/objtool/elf.c +199 -13
243 lines not on this page · raw message
tools/objtool/elf.h +14 -1
@@ -28,6 +28,13 @@# define elf_getshdrstrndx elf_getshstrndx#endif +/* +* Fallback for systems without this "read, mmaping if possible" cmd. +*/ +#ifndef ELF_C_READ_MMAP +#define ELF_C_READ_MMAP ELF_C_READ +#endif +struct section {struct list_head list;GElf_Shdr sh;
@@ -41,6 +48,7 @@ struct section {char *name;int idx;unsigned int len; +bool changed, text; };struct symbol {
@@ -75,7 +83,7 @@ struct elf { }; -struct elf *elf_open (const char *name); +struct elf *elf_open (const char *name,int flags);struct section *find_section_by_name (struct elf *elf,const char *name);struct symbol *find_symbol_by_offset (struct section *sec,unsigned long offset);struct symbol *find_symbol_containing (struct section *sec,unsigned long offset);
@@ -83,6 +91,11 @@ struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);struct rela *find_rela_by_dest_range (struct section *sec,unsigned long offset,unsigned int len);struct symbol *find_containing_func (struct section *sec,unsigned long offset); +struct section *elf_create_section (struct elf *elf,const char *name,size_t + entsize,int nr); +struct section *elf_create_rela_section (struct elf *elf,struct section *base); +int elf_rebuild_rela_section (struct section *sec); +int elf_write (struct elf *elf);void elf_close (struct elf *elf);#define for_each_sec (file, sec) \
tools/objtool/objtool.c +2 -1
@@ -42,10 +42,11 @@ struct cmd_struct { };static const char objtool_usage_string[] = -"objtool [OPTIONS] COMMAND [ARGS]" ; +"objtool COMMAND [ARGS]" ;static struct cmd_struct objtool_cmds[] = { {"check" , cmd_check,"Perform stack metadata validation on an object file" }, + {"undwarf" , cmd_undwarf,"Generate in-place undwarf metadata for an object file" }, };bool help;
tools/objtool/undwarf-types.h new file +81 -0
81 lines not on this page · raw message
tools/objtool/undwarf.h renamed from tools/objtool/builtin.h +13 -5
@@ -1,5 +1,5 @@/* -* Copyright (C) 2015 Josh Poimboeuf <[email protected]> +* Copyright (C) 2017 Josh Poimboeuf <[email protected]> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License
@@ -14,9 +14,17 @@* You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef _BUILTIN_H -#define _BUILTIN_H -extern int cmd_check (int argc,const char **argv); +#ifndef _UNDWARF_H +#define _UNDWARF_H -#endif /* _BUILTIN_H */ +#include "undwarf-types.h" + +struct objtool_file ; + +int create_undwarf (struct objtool_file *file); +int create_undwarf_sections (struct objtool_file *file); + +int undwarf_dump (const char *objname); + +#endif /* _UNDWARF_H */
tools/objtool/undwarf_dump.c new file +212 -0
212 lines not on this page · raw message
tools/objtool/undwarf_gen.c new file +215 -0
215 lines not on this page · raw message
Signature · 1 line
2.7.5
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 10 lines · Josh Poimboeuf
Now that objtool knows the states of all registers on the stack for each instruction, it's straightforward to generate debuginfo for an unwinder to use.
Instead of generating DWARF, generate a new format called undwarf, which is more suitable for an in-kernel unwinder. See
tools/objtool/Documentation/undwarf.txtfor a more detailed description of this new debuginfo format and why it's preferable to DWARF.Signed-off-by Josh Poimboeuf <[email protected]>
Quoted 12 lines
--- tools/objtool/Build | 3 + tools/objtool/Documentation/stack-validation.txt | 46 ++--- tools/objtool/builtin-check.c | 2 +- tools/objtool/builtin-undwarf.c | 70 ++++++++ tools/objtool/builtin.h | 1 + tools/objtool/check.c | 59 ++++++- tools/objtool/check.h | 15 +- tools/objtool/elf.c | 212 ++++++++++++++++++++-- tools/objtool/elf.h | 15 +- tools/objtool/objtool.c | 3 +- tools/objtool/undwarf-types.h | 81 +++++++++
Just a very quick stylistic suggestion: please name the header 'undwarf_types.h' (note the underscore versus hyphen), which is the common naming pattern used in the kernel.
Thanks,
Ingo
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 26 lines · Ingo Molnar
Quoted 10 lines · Josh Poimboeuf
Now that objtool knows the states of all registers on the stack for each instruction, it's straightforward to generate debuginfo for an unwinder to use.
Instead of generating DWARF, generate a new format called undwarf, which is more suitable for an in-kernel unwinder. See
tools/objtool/Documentation/undwarf.txtfor a more detailed description of this new debuginfo format and why it's preferable to DWARF.Signed-off-by Josh Poimboeuf <[email protected]>Quoted 12 lines
--- tools/objtool/Build | 3 + tools/objtool/Documentation/stack-validation.txt | 46 ++--- tools/objtool/builtin-check.c | 2 +- tools/objtool/builtin-undwarf.c | 70 ++++++++ tools/objtool/builtin.h | 1 + tools/objtool/check.c | 59 ++++++- tools/objtool/check.h | 15 +- tools/objtool/elf.c | 212 ++++++++++++++++++++-- tools/objtool/elf.h | 15 +- tools/objtool/objtool.c | 3 +- tools/objtool/undwarf-types.h | 81 +++++++++
Quoted 3 lines
Just a very quick stylistic suggestion: please name the header '
undwarf_types.h' (note the underscore versus hyphen), which is the common naming pattern used in the kernel.
Ok, will rename it.
Signature · 1 line
Josh
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
In reply to Josh PoimboeufQuoted 52 lines · Josh Poimboeuf
tools/objtool/undwarf-types.h new file +52
-0+#ifndef _UNDWARF_TYPES_H +#define _UNDWARF_TYPES_H + +/* +* The UNDWARF_REG_* registers are base registers which are used to find other +* registers on the stack. +* +* The CFA (call frame address) is the value of the stack pointer on the +* previous frame, i.e. the caller's SP before it called the callee. +* +* The CFA is usually based on SP, unless a frame pointer has been saved, in +* which case it's based on BP. +* +* BP is usually either based on CFA or is undefined (meaning its value didn't +* change for the current frame). +* +* So the CFA base is usually either SP or BP, and the FP base is usually either +* CFA or undefined. The rest of the base registers are needed for special +* cases like entry code and gcc aligned stacks. +*/ +#define UNDWARF_REG_UNDEFINED 0 +#define UNDWARF_REG_CFA 1 +#define UNDWARF_REG_DX 2 +#define UNDWARF_REG_DI 3 +#define UNDWARF_REG_BP 4 +#define UNDWARF_REG_SP 5 +#define UNDWARF_REG_R10 6 +#define UNDWARF_REG_R13 7 +#define UNDWARF_REG_BP_INDIRECT 8 +#define UNDWARF_REG_SP_INDIRECT 9 +#define UNDWARF_REG_MAX 15 + +/* +* UNDWARF_TYPE_CFA: Indicates that cfa_reg+cfa_offset points to the caller's +* stack pointer (aka the CFA in DWARF terms). Used for all callable +* functions, i.e. all C code and all callable asm functions. +* +* UNDWARF_TYPE_REGS: Used in entry code to indicate that cfa_reg+cfa_offset +* points to a fully populated pt_regs from a syscall, interrupt, or exception. +* +* UNDWARF_TYPE_REGS_IRET: Used in entry code to indicate that +* cfa_reg+cfa_offset points to the iret return frame. +*/ +#define UNDWARF_TYPE_CFA 0 +#define UNDWARF_TYPE_REGS 1 +#define UNDWARF_TYPE_REGS_IRET 2 + +/* +* This struct contains a simplified version of the DWARF Call Frame +* Information standard. It contains only the necessary parts of the real +* DWARF, simplified for ease of access by the in-kernel unwinder. It tells +* the unwinder how to find the previous SP and BP (and sometimes entry regs)
tools/objtool/undwarf-types.h line 79
Quoted 10 lines
tools/objtool/undwarf-types.h new file +10
-0+* on the stack for a given code address (IP). Each instance of the struct +* corresponds to one or more code locations. +*/ +struct undwarf { +short cfa_offset; +short bp_offset; +unsigned cfa_reg:4 ; +unsigned bp_reg:4 ; +unsigned type:2 ; +};
I never know straight away what 'CFA' stands for - could we please use natural names, i.e. something like:
struct undwarf {
u16 sp_offset;
u16 bp_offset;
unsigned sp_reg:4;
unsigned bp_reg:4;
unsigned type:2;
};...
struct unwind_hint {
u32 ip;
u16 sp_offset;
u8 sp_reg;
u8 type;
};?
Also note the slightly cleaner vertical alignment, plus the conversion to more stable data types: I believe various bits of tooling (perf and so) will eventually learn about undwarf, so having a well defined cross-arch data structure is probably of advantage.
Since we are not bound by DWARF anymore, we might as well use readable names and such?
Plus, shouldn't we use __packed for 'struct undwarf' to minimize the structure's size (to 6 bytes AFAICS?) - or is optimal packing of the main undwarf array already guaranteed on every platform with this layout?
Thanks,
Ingo
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 36 lines · Ingo Molnar
Quoted 5 lines
tools/objtool/undwarf-types.h new file +5
-0+/* +* This struct contains a simplified version of the DWARF Call Frame +* Information standard. It contains only the necessary parts of the real +* DWARF, simplified for ease of access by the in-kernel unwinder. It tells +* the unwinder how to find the previous SP and BP (and sometimes entry regs) tools/objtool/undwarf-types.h line 79
Quoted 10 lines
tools/objtool/undwarf-types.h new file +10
-0+* on the stack for a given code address (IP). Each instance of the struct +* corresponds to one or more code locations. +*/ +struct undwarf { +short cfa_offset; +short bp_offset; +unsigned cfa_reg:4 ; +unsigned bp_reg:4 ; +unsigned type:2 ; +};I never know straight away what 'CFA' stands for - could we please use natural names, i.e. something like:
struct undwarf { u16 sp_offset; u16 bp_offset; unsigned sp_reg:4; unsigned bp_reg:4; unsigned type:2; };...
struct unwind_hint { u32 ip; u16 sp_offset; u8 sp_reg; u8 type; };
Quoted 6 lines
?
Also note the slightly cleaner vertical alignment, plus the conversion to more stable data types: I believe various bits of tooling (perf and so) will eventually learn about undwarf, so having a well defined cross-arch data structure is probably of advantage.
I agree with all your suggestions.
(Though if we want to make it truly cross-arch, 'bp' should be 'fp', for frame pointer. But there were some objections to that, so I'll leave it 'bp' for now.)
Quoted 6 lines
Since we are not bound by DWARF anymore, we might as well use readable names and such?
Plus, shouldn't we use
__packedfor 'struct undwarf' to minimize the structure's size (to 6 bytes AFAICS?) - or is optimal packing of the main undwarf array already guaranteed on every platform with this layout?
Ah yes, it should definitely be packed (assuming that doesn't affect performance negatively).
Signature · 1 line
Josh
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 6 lines · Josh Poimboeuf
Quoted 3 lines
Plus, shouldn't we use
__packedfor 'struct undwarf' to minimize the structure's size (to 6 bytes AFAICS?) - or is optimal packing of the main undwarf array already guaranteed on every platform with this layout?Ah yes, it should definitely be packed (assuming that doesn't affect performance negatively).
So if I count that correctly that should shave another ~1MB off a typical ~4MB table size?
Thanks,
Ingo
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 7 lines · Ingo Molnar
Quoted 4 lines · Josh Poimboeuf
Quoted 3 lines
Plus, shouldn't we use
__packedfor 'struct undwarf' to minimize the structure's size (to 6 bytes AFAICS?) - or is optimal packing of the main undwarf array already guaranteed on every platform with this layout?
Quoted 5 lines
Quoted 2 lines
Ah yes, it should definitely be packed (assuming that doesn't affect performance negatively).
So if I count that correctly that should shave another ~1MB off a typical ~4MB table size?
Here's what my Fedora kernel looks like *before* the packed change:
$ eu-readelf -S vmlinux |grep undwarf [15] .undwarf_ip PROGBITS ffffffff81f776d0 011776d0 0012d9d0 0 A 0 0 1 [16] .undwarf PROGBITS ffffffff820a50a0 012a50a0 0025b3a0 0 A 0 0 1
The total undwarf data size is ~3.5MB.
There are 308852 entries of two parallel arrays:
* .undwarf (8 bytes/entry) = 2470816 bytes
* .undwarf_ip (4 bytes/entry) = 1235408 bytes
If we pack undwarf, reducing the size of the .undwarf entries by two bytes, it will save 308852 * 2 = 617704.
So the savings will be ~600k, and the typical size will be reduced to ~3MB.
Signature · 1 line
Josh
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 24 lines · Josh Poimboeuf
Quoted 7 lines · Ingo Molnar
Quoted 4 lines · Josh Poimboeuf
Quoted 3 lines
Plus, shouldn't we use
__packedfor 'struct undwarf' to minimize the structure's size (to 6 bytes AFAICS?) - or is optimal packing of the main undwarf array already guaranteed on every platform with this layout?Quoted 5 lines
Quoted 2 lines
Ah yes, it should definitely be packed (assuming that doesn't affect performance negatively).
So if I count that correctly that should shave another ~1MB off a typical ~4MB table size?
Here's what my Fedora kernel looks like *before* the packed change:
$ eu-readelf -S vmlinux |grep undwarf [15] .undwarf_ip PROGBITS ffffffff81f776d0 011776d0 0012d9d0 0 A 0 0 1 [16] .undwarf PROGBITS ffffffff820a50a0 012a50a0 0025b3a0 0 A 0 0 1The total undwarf data size is ~3.5MB.
There are 308852 entries of two parallel arrays:
Quoted 7 lines
* .undwarf (8 bytes/entry) = 2470816 bytes
* .undwarf_ip (4 bytes/entry) = 1235408 bytesIf we pack undwarf, reducing the size of the .undwarf entries by two bytes, it will save 308852 * 2 = 617704.
So the savings will be ~600k, and the typical size will be reduced to ~3MB.
Just for the record, while packing the struct from 8 to 6 bytes did save 600k, it also made the unwinder ~7% slower. I think that's probably an ok tradeoff, so I'll leave it packed in v3.
Signature · 1 line
Josh
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 28 lines · Josh Poimboeuf
Quoted 27 lines · Josh Poimboeuf
Quoted 7 lines · Ingo Molnar
Quoted 4 lines · Josh Poimboeuf
Quoted 3 lines
Plus, shouldn't we use
__packedfor 'struct undwarf' to minimize the structure's size (to 6 bytes AFAICS?) - or is optimal packing of the main undwarf array already guaranteed on every platform with this layout?Quoted 5 lines
Quoted 2 lines
Ah yes, it should definitely be packed (assuming that doesn't affect performance negatively).
So if I count that correctly that should shave another ~1MB off a typical ~4MB table size?
Here's what my Fedora kernel looks like *before* the packed change:
$ eu-readelf -S vmlinux |grep undwarf [15] .undwarf_ip PROGBITS ffffffff81f776d0 011776d0 0012d9d0 0 A 0 0 1 [16] .undwarf PROGBITS ffffffff820a50a0 012a50a0 0025b3a0 0 A 0 0 1The total undwarf data size is ~3.5MB.
There are 308852 entries of two parallel arrays:
* .undwarf (8 bytes/entry) = 2470816 bytes
* .undwarf_ip (4 bytes/entry) = 1235408 bytes
Quoted 8 lines
Quoted 4 lines
If we pack undwarf, reducing the size of the .undwarf entries by two bytes, it will save 308852 * 2 = 617704.
So the savings will be ~600k, and the typical size will be reduced to ~3MB.
Just for the record, while packing the struct from 8 to 6 bytes did save 600k, it also made the unwinder ~7% slower. I think that's probably an ok tradeoff, so I'll leave it packed in v3.
So, out of curiosity, I'm wondering where that slowdown comes from: on modern x86 CPUs indexing by units of 6 bytes ought to be just as fast as indexing by 8 bytes, unless I'm missing something? Is it maybe the not naturally aligned 32-bit words?
Or maybe there's some bad case of a 32-bit word crossing a 64-byte cache line boundary that hits some pathological aspect of the CPU? We could probably get around any such problems by padding by 2 bytes on 64-byte boundaries - that's only a ~3% data size increase. The flip side would be a complication of the data structure and its accessors - which might cost more in terms of code generation efficiency than it buys us to begin with ...
Also, there's another aspect besides RAM footprint: a large data structure that is ~20% smaller means 20% less cache footprint: which for cache cold lookups might matter more than the direct computational cost.
Thanks,
Ingo
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 51 lines · Ingo Molnar
Quoted 28 lines · Josh Poimboeuf
Quoted 27 lines · Josh Poimboeuf
Quoted 7 lines · Ingo Molnar
Quoted 4 lines · Josh Poimboeuf
Quoted 3 lines
Plus, shouldn't we use
__packedfor 'struct undwarf' to minimize the structure's size (to 6 bytes AFAICS?) - or is optimal packing of the main undwarf array already guaranteed on every platform with this layout?Quoted 5 lines
Quoted 2 lines
Ah yes, it should definitely be packed (assuming that doesn't affect performance negatively).
So if I count that correctly that should shave another ~1MB off a typical ~4MB table size?
Here's what my Fedora kernel looks like *before* the packed change:
$ eu-readelf -S vmlinux |grep undwarf [15] .undwarf_ip PROGBITS ffffffff81f776d0 011776d0 0012d9d0 0 A 0 0 1 [16] .undwarf PROGBITS ffffffff820a50a0 012a50a0 0025b3a0 0 A 0 0 1The total undwarf data size is ~3.5MB.
There are 308852 entries of two parallel arrays:
* .undwarf (8 bytes/entry) = 2470816 bytes
* .undwarf_ip (4 bytes/entry) = 1235408 bytesQuoted 8 lines
Quoted 4 lines
If we pack undwarf, reducing the size of the .undwarf entries by two bytes, it will save 308852 * 2 = 617704.
So the savings will be ~600k, and the typical size will be reduced to ~3MB.
Just for the record, while packing the struct from 8 to 6 bytes did save 600k, it also made the unwinder ~7% slower. I think that's probably an ok tradeoff, so I'll leave it packed in v3.
So, out of curiosity, I'm wondering where that slowdown comes from: on modern x86 CPUs indexing by units of 6 bytes ought to be just as fast as indexing by 8 bytes, unless I'm missing something? Is it maybe the not naturally aligned 32-bit words?
Or maybe there's some bad case of a 32-bit word crossing a 64-byte cache line boundary that hits some pathological aspect of the CPU? We could probably get around any such problems by padding by 2 bytes on 64-byte boundaries - that's only a ~3% data size increase. The flip side would be a complication of the data structure and its accessors - which might cost more in terms of code generation efficiency than it buys us to begin with ...
Quoted 3 lines
Also, there's another aspect besides RAM footprint: a large data structure that is ~20% smaller means 20% less cache footprint: which for cache cold lookups might matter more than the direct computational cost.
tl;dr: Packed really seems to be more like ~2% slower, time for an adult beverage.
So I tested again with the latest version of my code, and this time packed was 5% *faster* than unpacked, rather than 7% slower. 'perf stat' showed that, in both cases, most of the difference was caused by branch misses in the binary search code. But that code doesn't even touch the packed struct...
After some hair-pulling/hand-wringing I realized that changing the struct packing caused GCC to change some of the unwinder code a bit, which shifted the rest of the kernel's function offsets enough that it changed the behavior of the unwind table binary search in a way that affected the CPU's branch prediction. And my crude benchmark was just unwinding the same stack on repeat, so a small change in the loop behavior had a big impact on the overall branch predictability.
Anyway, I used some linker magic to temporarily move the unwinder code to the end of .text, so that unwinder changes don't add unexpected side effects to the microbenchmark behavior. Now I'm getting more consistent
results: the packed struct is measuring ~2% slower. The slight slowdown might just be explained by the fact that GCC generates some extra instructions for extracting the fields out of the packed struct.
In the meantime, I found a ~10% speedup by making the "fast lookup table" block size a power-of-two (256) to get rid of the need for a slow 'div' instruction.
I think I'm done performance tweaking for now. I'll keep the packed struct, and add the code for the 'div' removal, and hope to submit v3 soon.
Signature · 1 line
Josh
Re: [PATCH v2 4/8] objtool: add undwarf debuginfo generation
Quoted 6 lines · Josh Poimboeuf
Anyway, I used some linker magic to temporarily move the unwinder code to the end of .text, so that unwinder changes don't add unexpected side effects to the microbenchmark behavior. Now I'm getting more consistent results: the packed struct is measuring ~2% slower. The slight slowdown might just be explained by the fact that GCC generates some extra instructions for extracting the fields out of the packed struct.
Yeah, the 16-bit field accesses versus a zero-extended 32-bit field are more complex to access even on x86 that has a fair amount of 16-bit legacy.
Quoted 5 lines
In the meantime, I found a ~10% speedup by making the "fast lookup table" block size a power-of-two (256) to get rid of the need for a slow 'div' instruction.
I think I'm done performance tweaking for now. I'll keep the packed struct, and add the code for the 'div' removal, and hope to submit v3 soon.
Sounds good to me!
~2% slowdown for ~30% RAM savings for a debug data structure that is about as large as a typical kernel's total .text is a decent trade-off.
Thanks,
Ingo
PATCH v2 5/8 objtool, x86: add facility for asm code to provide unwind hints
In reply to Josh PoimboeufSome asm (and inline asm) code does special things to the stack which objtool can't understand. (Nor can GCC or GNU assembler, for that matter.) In such cases we need a facility for the code to provide annotations, so the unwinder can unwind through it.
This provides such a facility, in the form of unwind hints. They're similar to the GNU assembler .cfi* directives, but they give more information, and are needed in far fewer places, because objtool can fill in the blanks by following branches and adjusting the stack pointer for pushes and pops.
---
.../x86/include/asm}/undwarf-types.h | 18 +++
arch/x86/include/asm/undwarf.h | 103 ++++++++++++
tools/objtool/Makefile | 3 +
tools/objtool/check.c | 179 ++++++++++++++++++++-
tools/objtool/check.h | 4 +-
tools/objtool/undwarf-types.h | 18 +++
6 files changed, 317 insertions(+), 8 deletions(-)
copy {tools/objtool => arch/x86/include/asm}/undwarf-types.h (84%)
create mode 100644 arch/x86/include/asm/undwarf.harch/x86/include/asm/undwarf-types.h renamed from tools/objtool/undwarf-types.h +18 -0
@@ -57,11 +57,17 @@* * UNDWARF_TYPE_REGS_IRET: Used in entry code to indicate that * cfa_reg+cfa_offset points to the iret return frame. +* +* The UNWIND_HINT macros are only used for the unwind_hint struct. They are +* not used for the undwarf struct due to size and complexity constraints. */ #define UNDWARF_TYPE_CFA 0#define UNDWARF_TYPE_REGS 1#define UNDWARF_TYPE_REGS_IRET 2 +#define UNWIND_HINT_TYPE_SAVE 3 +#define UNWIND_HINT_TYPE_RESTORE 4 +#ifndef __ASSEMBLY__/* * This struct contains a simplified version of the DWARF Call Frame * Information standard. It contains only the necessary parts of the real
@@ -78,4 +84,16 @@ struct undwarf { unsigned type:2; }; +/* +* This struct is used by asm and inline asm code to manually annotate the +* location of registers on the stack for the undwarf unwinder. +*/ +struct unwind_hint { +unsigned int ip; +short cfa_offset; +unsigned char cfa_reg; +unsigned char type; +}; +#endif /* __ASSEMBLY__ */ +#endif /* _UNDWARF_TYPES_H */
arch/x86/include/asm/undwarf.h new file +103 -0
103 lines not on this page · raw message
tools/objtool/Makefile +3 -0
@@ -52,6 +52,9 @@ $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN) diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \ diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \ || echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true + @(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \ + diff ../../arch/x86/include/asm/undwarf-types.h undwarf-types.h >/dev/null) \ + || echo "warning: objtool: undwarf-types.h differs from kernel" >&2 )) || true $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
tools/objtool/check.c +173 -6
245 lines not on this page · raw message
tools/objtool/check.h +2 -2
@@ -43,7 +43,7 @@ struct instruction {unsigned int len;unsigned char type;unsigned long immediate; -bool alt_group, visited, dead_end, ignore; +bool alt_group, visited, dead_end, ignore, hint, save, restore;struct symbol *call_dest;struct instruction *jump_dest;struct list_head alts;
@@ -58,7 +58,7 @@ struct objtool_file {struct list_head insn_list;DECLARE_HASHTABLE (insn_hash,16 );struct section *rodata, *whitelist; -bool ignore_unreachables, c_file; +bool ignore_unreachables, c_file, hints; };int check (const char *objname,bool nofp,bool undwarf);
tools/objtool/undwarf-types.h +18 -0
@@ -57,11 +57,17 @@* * UNDWARF_TYPE_REGS_IRET: Used in entry code to indicate that * cfa_reg+cfa_offset points to the iret return frame. +* +* The UNWIND_HINT macros are only used for the unwind_hint struct. They are +* not used for the undwarf struct due to size and complexity constraints. */ #define UNDWARF_TYPE_CFA 0#define UNDWARF_TYPE_REGS 1#define UNDWARF_TYPE_REGS_IRET 2 +#define UNWIND_HINT_TYPE_SAVE 3 +#define UNWIND_HINT_TYPE_RESTORE 4 +#ifndef __ASSEMBLY__/* * This struct contains a simplified version of the DWARF Call Frame * Information standard. It contains only the necessary parts of the real
@@ -78,4 +84,16 @@ struct undwarf { unsigned type:2; }; +/* +* This struct is used by asm and inline asm code to manually annotate the +* location of registers on the stack for the undwarf unwinder. +*/ +struct unwind_hint { +unsigned int ip; +short cfa_offset; +unsigned char cfa_reg; +unsigned char type; +}; +#endif /* __ASSEMBLY__ */ +#endif /* _UNDWARF_TYPES_H */
Signature · 1 line
2.7.5
PATCH v2 6/8 x86/entry: add unwind hint annotations
In reply to Josh PoimboeufAdd unwind hint annotations to entry_64.S. This will enable the undwarf unwinder to unwind through any location in the entry code including syscalls, interrupts, and exceptions.
--- arch/x86/entry/Makefile | 1 - arch/x86/entry/calling.h | 6 +++++ arch/x86/entry/entry_64.S | 56 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 7 deletions(-)
arch/x86/entry/Makefile +0 -1
@@ -2,7 +2,6 @@ # Makefile for the x86 low level entry code # -OBJECT_FILES_NON_STANDARD_entry_$(BITS).o := y OBJECT_FILES_NON_STANDARD_entry_64_compat.o := y CFLAGS_syscall_64.o += $(call cc-option,-Wno-override-init,)
arch/x86/entry/calling.h +6 -0
@@ -1,4 +1,6 @@#include <linux/jump_label.h> +#include <asm/undwarf.h> +/*
@@ -112,6 +114,7 @@ For 32-bit we have the following conventions - kernel is built with movq %rdx, 12*8+\offset(%rsp) movq %rsi, 13*8+\offset(%rsp) movq %rdi, 14*8+\offset(%rsp) + UNWIND_HINT_REGS offset=\offset extra=0 .endm .macro SAVE_C_REGS offset=0 SAVE_C_REGS_HELPER \offset, 1, 1, 1, 1
@@ -136,6 +139,7 @@ For 32-bit we have the following conventions - kernel is built with movq %r12, 3*8+\offset(%rsp) movq %rbp, 4*8+\offset(%rsp) movq %rbx, 5*8+\offset(%rsp) + UNWIND_HINT_REGS offset=\offset .endm .macro RESTORE_EXTRA_REGS offset=0
@@ -145,6 +149,7 @@ For 32-bit we have the following conventions - kernel is built with movq 3*8+\offset(%rsp), %r12 movq 4*8+\offset(%rsp), %rbp movq 5*8+\offset(%rsp), %rbx + UNWIND_HINT_REGS offset=\offset extra=0 .endm .macro RESTORE_C_REGS_HELPER rstor_rax=1, rstor_rcx=1, rstor_r11=1, rstor_r8910=1, rstor_rdx=1
@@ -167,6 +172,7 @@ For 32-bit we have the following conventions - kernel is built with .endif movq 13*8(%rsp), %rsi movq 14*8(%rsp), %rdi +UNWIND_HINT_IRET_REGS offset=16 *8 .endm .macro RESTORE_C_REGS RESTORE_C_REGS_HELPER 1,1,1,1,1
arch/x86/entry/entry_64.S +50 -6
305 lines not on this page · raw message
Signature · 1 line
2.7.5
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
There's a bug here that will need a small change to the entry code.
Mike Galbraith reported:
WARNING: can't dereference registers at ffffc900089d7e08 for ip ffffffff81740bbb
After some looking I found that it's caused by the following code snippet in the 'interrupt' macro in entry_64.S:
/*
* Save previous stack pointer, optionally switch to interrupt stack.
* irq_count is used to check if a CPU is already on an interrupt stack
* or not. While this is essentially redundant with preempt_count it is
* a little cheaper to use a separate counter in the PDA (short of
* moving irq_enter into assembly, which would be too much work) */
movq %rsp, %rdi
incl PER_CPU_VAR(irq_count)
cmovzq PER_CPU_VAR(irq_stack_ptr), %rsp
UNWIND_HINT_REGS base=rdi
pushq %rdi
UNWIND_HINT_REGS indirect=1
The problem is that it's changing the stack pointer *before* writing the previous stack pointer (push %rdi). So when unwinding from an NMI which hit between the rsp write and the rdi push, the unwinder tries to access the regs on the previous stack (by reading rdi), but the previous stack pointer isn't there yet, so the access is considered out of bounds.
Signature · 1 line
Josh
Andy Lutomirski
maintainerRe: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 23 lines · Josh Poimboeuf
There's a bug here that will need a small change to the entry code.
Mike Galbraith reported:
WARNING: can't dereference registers at
ffffc900089d7e08for ipffffffff81740bbbAfter some looking I found that it's caused by the following code snippet in the 'interrupt' macro in
entry_64.S:/*
* Save previous stack pointer, optionally switch to interrupt stack.
*irq_countis used to check if a CPU is already on an interrupt stack
* or not. While this is essentially redundant withpreempt_countit is
* a little cheaper to use a separate counter in the PDA (short of
* movingirq_enterinto assembly, which would be too much work) */
movq %rsp, %rdi
inclPER_CPU_VAR(irq_count)
cmovzqPER_CPU_VAR(irq_stack_ptr), %rsp
UNWIND_HINT_REGSbase=rdi
pushq %rdi
UNWIND_HINT_REGSindirect=1
Quoted 5 lines
The problem is that it's changing the stack pointer *before* writing the previous stack pointer (push %rdi). So when unwinding from an NMI which hit between the rsp write and the rdi push, the unwinder tries to access the regs on the previous stack (by reading rdi), but the previous stack pointer isn't there yet, so the access is considered out of bounds.
Ugh, that code. Does this problem go away with this patch applied:
If so, want to update the patch for new kernels (shouldn't conflict with anything except your unwind hints)?
--Andy
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 30 lines · Andy Lutomirski
Quoted 23 lines · Josh Poimboeuf
There's a bug here that will need a small change to the entry code.
Mike Galbraith reported:
WARNING: can't dereference registers at
ffffc900089d7e08for ipffffffff81740bbbAfter some looking I found that it's caused by the following code snippet in the 'interrupt' macro in
entry_64.S:/*
* Save previous stack pointer, optionally switch to interrupt stack.
*irq_countis used to check if a CPU is already on an interrupt stack
* or not. While this is essentially redundant withpreempt_countit is
* a little cheaper to use a separate counter in the PDA (short of
* movingirq_enterinto assembly, which would be too much work) */
movq %rsp, %rdi
inclPER_CPU_VAR(irq_count)
cmovzqPER_CPU_VAR(irq_stack_ptr), %rsp
UNWIND_HINT_REGSbase=rdi
pushq %rdi
UNWIND_HINT_REGSindirect=1Quoted 5 lines
The problem is that it's changing the stack pointer *before* writing the previous stack pointer (push %rdi). So when unwinding from an NMI which hit between the rsp write and the rdi push, the unwinder tries to access the regs on the previous stack (by reading rdi), but the previous stack pointer isn't there yet, so the access is considered out of bounds.
Quoted 6 lines
Ugh, that code. Does this problem go away with this patch applied:
If so, want to update the patch for new kernels (shouldn't conflict with anything except your unwind hints)?
I don't think that patch will fix it, because it still updates rsp *before* writing the old rsp on the new stack. So there's still a window where the "previous stack" pointer is missing.
Signature · 1 line
Josh
Andy Lutomirski
maintainerRe: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 33 lines · Josh Poimboeuf
Quoted 32 lines · Andy Lutomirski
Quoted 23 lines · Josh Poimboeuf
There's a bug here that will need a small change to the entry code.
Mike Galbraith reported:
WARNING: can't dereference registers at
ffffc900089d7e08for ipffffffff81740bbbAfter some looking I found that it's caused by the following code snippet in the 'interrupt' macro in
entry_64.S:/*
* Save previous stack pointer, optionally switch to interrupt stack.
*irq_countis used to check if a CPU is already on an interrupt stack
* or not. While this is essentially redundant withpreempt_countit is
* a little cheaper to use a separate counter in the PDA (short of
* movingirq_enterinto assembly, which would be too much work) */
movq %rsp, %rdi
inclPER_CPU_VAR(irq_count)
cmovzqPER_CPU_VAR(irq_stack_ptr), %rsp
UNWIND_HINT_REGSbase=rdi
pushq %rdi
UNWIND_HINT_REGSindirect=1Quoted 5 lines
The problem is that it's changing the stack pointer *before* writing the previous stack pointer (push %rdi). So when unwinding from an NMI which hit between the rsp write and the rdi push, the unwinder tries to access the regs on the previous stack (by reading rdi), but the previous stack pointer isn't there yet, so the access is considered out of bounds.
Ugh, that code. Does this problem go away with this patch applied:
Quoted 8 lines
Quoted 4 lines
If so, want to update the patch for new kernels (shouldn't conflict with anything except your unwind hints)?
I don't think that patch will fix it, because it still updates rsp *before* writing the old rsp on the new stack. So there's still a window where the "previous stack" pointer is missing.
But it's in a register. Is undwarf not able to grok that? I have no fundamental problem with pushing it to the new stack first, but the actual asm is nastier because we don't have an addressing mode that's *(*(gs:blahblahblah)) = reg.
At least my patch makes all the copied of this code identical so the problem can be solved only once.
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 36 lines · Andy Lutomirski
Quoted 35 lines · Josh Poimboeuf
Quoted 34 lines · Andy Lutomirski
Quoted 23 lines · Josh Poimboeuf
There's a bug here that will need a small change to the entry code.
Mike Galbraith reported:
WARNING: can't dereference registers at
ffffc900089d7e08for ipffffffff81740bbbAfter some looking I found that it's caused by the following code snippet in the 'interrupt' macro in
entry_64.S:/*
* Save previous stack pointer, optionally switch to interrupt stack.
*irq_countis used to check if a CPU is already on an interrupt stack
* or not. While this is essentially redundant withpreempt_countit is
* a little cheaper to use a separate counter in the PDA (short of
* movingirq_enterinto assembly, which would be too much work) */
movq %rsp, %rdi
inclPER_CPU_VAR(irq_count)
cmovzqPER_CPU_VAR(irq_stack_ptr), %rsp
UNWIND_HINT_REGSbase=rdi
pushq %rdi
UNWIND_HINT_REGSindirect=1Quoted 5 lines
The problem is that it's changing the stack pointer *before* writing the previous stack pointer (push %rdi). So when unwinding from an NMI which hit between the rsp write and the rdi push, the unwinder tries to access the regs on the previous stack (by reading rdi), but the previous stack pointer isn't there yet, so the access is considered out of bounds.
Ugh, that code. Does this problem go away with this patch applied:
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
If so, want to update the patch for new kernels (shouldn't conflict with anything except your unwind hints)?
I don't think that patch will fix it, because it still updates rsp *before* writing the old rsp on the new stack. So there's still a window where the "previous stack" pointer is missing.
But it's in a register. Is undwarf not able to grok that?
Sorry, I didn't explain it very well. Undwarf can find the regs pointer in rdi, it just doesn't trust its value.
See the stack_info.next_sp field, which is set in in_irq_stack():
/* * The next stack pointer is the first thing pushed by the entry code * after switching to the irq stack. */ info->next_sp = (unsigned long *)*(end - 1);
It's a safety mechanism. The unwinder needs the last word of the irq stack page to point to the previous stack. That way it can double check that the stack pointer it calculates is within the bounds of either the current stack or the previous stack.
In the above code, the previous stack pointer (or next stack pointer, depending on your perspective) hasn't been set up before it switches stacks. So the unwinder reads an uninitialized value into info->next_sp, and compares that with the regs pointer, and then stops the unwind because it thinks it went off into the weeds.
Signature · 1 line
Josh
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
--Andy
Quoted 64 lines · Josh Poimboeuf
Quoted 38 lines · Andy Lutomirski
Quoted 36 lines · Josh Poimboeuf
Quoted 35 lines · Andy Lutomirski
Quoted 24 lines · Josh Poimboeuf
There's a bug here that will need a small change to the entry code.
Mike Galbraith reported:
WARNING: can't dereference registers at
ffffc900089d7e08for ipffffffff81740bbbAfter some looking I found that it's caused by the following code snippet in the 'interrupt' macro in
entry_64.S:/*
* Save previous stack pointer, optionally switch to interrupt stack.
*irq_countis used to check if a CPU is already on an interrupt stack
* or not. While this is essentially redundant withpreempt_countit is
* a little cheaper to use a separate counter in the PDA (short of
* movingirq_enterinto assembly, which would be too much work) */
movq %rsp, %rdi
inclPER_CPU_VAR(irq_count)
cmovzqPER_CPU_VAR(irq_stack_ptr), %rsp
UNWIND_HINT_REGSbase=rdi
pushq %rdi
UNWIND_HINT_REGSindirect=1Quoted 5 lines
The problem is that it's changing the stack pointer *before* writing the previous stack pointer (push %rdi). So when unwinding from an NMI which hit between the rsp write and the rdi push, the unwinder tries to access the regs on the previous stack (by reading rdi), but the previous stack pointer isn't there yet, so the access is considered out of bounds.
Ugh, that code. Does this problem go away with this patch applied:
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
If so, want to update the patch for new kernels (shouldn't conflict with anything except your unwind hints)?
I don't think that patch will fix it, because it still updates rsp *before* writing the old rsp on the new stack. So there's still a window where the "previous stack" pointer is missing.
But it's in a register. Is undwarf not able to grok that?
Sorry, I didn't explain it very well. Undwarf can find the regs pointer in rdi, it just doesn't trust its value.
See the stack_info.next_sp field, which is set in
in_irq_stack():/* * The next stack pointer is the first thing pushed by the entry code * after switching to the irq stack. */ info->next_sp = (unsigned long *)*(end - 1);It's a safety mechanism. The unwinder needs the last word of the irq stack page to point to the previous stack. That way it can double check that the stack pointer it calculates is within the bounds of either the current stack or the previous stack.
Quoted 6 lines
In the above code, the previous stack pointer (or next stack pointer, depending on your perspective) hasn't been set up before it switches stacks. So the unwinder reads an uninitialized value into
info->next_sp, and compares that with the regs pointer, and then stops the unwind because it thinks it went off into the weeds.
That should be manageable, though, I think. With my patch applied (and maybe even without it), the only exception to that rule is if regs->sp points just above the top of the IRQ stack and the next instruction is push reg. In that case, the reg is exactly as trustworthy as the normal rule.* Can you teach the unwinding code that this is okay?
* If an NMI hits right there, then it relies on unwinding out of the NMI correctly. But the usual checks that the target stack is a valid stack should prevent us from going off into the weeds regardless.
Quoted 2 lines
Signature · 1 line
Josh
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 77 lines · Andy Lutomirski
Quoted 63 lines · Josh Poimboeuf
Quoted 37 lines · Andy Lutomirski
Quoted 36 lines · Josh Poimboeuf
Quoted 35 lines · Andy Lutomirski
Quoted 24 lines · Josh Poimboeuf
There's a bug here that will need a small change to the entry code.
Mike Galbraith reported:
WARNING: can't dereference registers at
ffffc900089d7e08for ipffffffff81740bbbAfter some looking I found that it's caused by the following code snippet in the 'interrupt' macro in
entry_64.S:/*
* Save previous stack pointer, optionally switch to interrupt stack.
*irq_countis used to check if a CPU is already on an interrupt stack
* or not. While this is essentially redundant withpreempt_countit is
* a little cheaper to use a separate counter in the PDA (short of
* movingirq_enterinto assembly, which would be too much work) */
movq %rsp, %rdi
inclPER_CPU_VAR(irq_count)
cmovzqPER_CPU_VAR(irq_stack_ptr), %rsp
UNWIND_HINT_REGSbase=rdi
pushq %rdi
UNWIND_HINT_REGSindirect=1Quoted 5 lines
The problem is that it's changing the stack pointer *before* writing the previous stack pointer (push %rdi). So when unwinding from an NMI which hit between the rsp write and the rdi push, the unwinder tries to access the regs on the previous stack (by reading rdi), but the previous stack pointer isn't there yet, so the access is considered out of bounds.
Ugh, that code. Does this problem go away with this patch applied:
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
If so, want to update the patch for new kernels (shouldn't conflict with anything except your unwind hints)?
I don't think that patch will fix it, because it still updates rsp *before* writing the old rsp on the new stack. So there's still a window where the "previous stack" pointer is missing.
But it's in a register. Is undwarf not able to grok that?
Sorry, I didn't explain it very well. Undwarf can find the regs pointer in rdi, it just doesn't trust its value.
See the stack_info.next_sp field, which is set in
in_irq_stack():/* * The next stack pointer is the first thing pushed by the entry code * after switching to the irq stack. */ info->next_sp = (unsigned long *)*(end - 1);It's a safety mechanism. The unwinder needs the last word of the irq stack page to point to the previous stack. That way it can double check that the stack pointer it calculates is within the bounds of either the current stack or the previous stack.
Quoted 6 lines
In the above code, the previous stack pointer (or next stack pointer, depending on your perspective) hasn't been set up before it switches stacks. So the unwinder reads an uninitialized value into
info->next_sp, and compares that with the regs pointer, and then stops the unwind because it thinks it went off into the weeds.That should be manageable, though, I think. With my patch applied (and maybe even without it), the only exception to that rule is if
regs->sppoints just above the top of the IRQ stack and the next instruction is push reg. In that case, the reg is exactly as trustworthy as the normal rule.* Can you teach the unwinding code that this is okay?
Quoted 3 lines
* If an NMI hits right there, then it relies on unwinding out of the NMI correctly. But the usual checks that the target stack is a valid stack should prevent us from going off into the weeds regardless.
But that would remove a safeguard against the undwarf data being corrupt. Sure, it would only affect the rare case where the stack pointer is at the top of the IRQ stack, but still...
Also, the frame pointer and guess unwinders have the same issue, and this solution wouldn't work for them.
And, worst of all, the oops stack dumping code in show_trace_log_lvl() also has this issue . It relies on those previous stack pointers. And it's separated from the unwinder logic by design, so it can't ask the unwinder where the next stack is.
Signature · 1 line
Josh
Andy Lutomirski
maintainerRe: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 40 lines · Josh Poimboeuf
Quoted 31 lines · Andy Lutomirski
Quoted 17 lines
Sorry, I didn't explain it very well. Undwarf can find the regs pointer in rdi, it just doesn't trust its value.
See the stack_info.next_sp field, which is set in
in_irq_stack():/* * The next stack pointer is the first thing pushed by the entry code * after switching to the irq stack. */ info->next_sp = (unsigned long *)*(end - 1);It's a safety mechanism. The unwinder needs the last word of the irq stack page to point to the previous stack. That way it can double check that the stack pointer it calculates is within the bounds of either the current stack or the previous stack.
Quoted 6 lines
In the above code, the previous stack pointer (or next stack pointer, depending on your perspective) hasn't been set up before it switches stacks. So the unwinder reads an uninitialized value into
info->next_sp, and compares that with the regs pointer, and then stops the unwind because it thinks it went off into the weeds.That should be manageable, though, I think. With my patch applied (and maybe even without it), the only exception to that rule is if
regs->sppoints just above the top of the IRQ stack and the next instruction is push reg. In that case, the reg is exactly as trustworthy as the normal rule.* Can you teach the unwinding code that this is okay?Quoted 3 lines
* If an NMI hits right there, then it relies on unwinding out of the NMI correctly. But the usual checks that the target stack is a valid stack should prevent us from going off into the weeds regardless.
But that would remove a safeguard against the undwarf data being corrupt. Sure, it would only affect the rare case where the stack pointer is at the top of the IRQ stack, but still...
Quoted 7 lines
Also, the frame pointer and guess unwinders have the same issue, and this solution wouldn't work for them.
And, worst of all, the oops stack dumping code in
show_trace_log_lvl()also has this issue . It relies on those previous stack pointers. And it's separated from the unwinder logic by design, so it can't ask the unwinder where the next stack is.
Ugh.
I feel like we had this debate before, and I thought it was rather silly that the unwinder cared. After all, we already have separate safety mechanisms to make sure that the unwinder never wanders off of the valid stacks and that it never touches any given stack more than once. But it is indeed useful for the oops unwinder, so c'est la vie.
That being said, I bet we could get away with this (sorry for immense whitespace damage):
.macro ENTER_IRQ_STACK old_rsp scratch_reg
DEBUG_ENTRY_ASSERT_IRQS_OFF
movq %rsp, \old_rsp
incl PER_CPU_VAR(irq_count)
jnz .Lrecurse_irq_stack_\@
/*
* Right now, we just incremented irq_count to zero, so we've
* claimed the IRQ stack but we haven't switched to it yet.
* Anything that can interrupt us here without using IST
* must be *extremely* careful to limit its stack usage. *
* We write old_rsp to the IRQ stack before switching to
* %rsp for the benefit of the OOPS unwinder.
*/
movq PER_CPU_VAR(irq_stack_ptr), \scratch_reg
movq \old_rsp, -8(\scratch_reg)
leaq -8(\old_rsp), %rsp
jmp .Lout_\@
.Lrecurse_irq_stack_\@:
pushq \old_rsp
.Lout_\@:
.endm
After all, it looks like all the users have a scratch reg available.
Hmm. There's another option that might be considerably nicer, though:
put the IRQ stack at a known (at link time) position *in percpu space*. (Presumably it already is -- I haven't checked.) Then we do:
.macro ENTER_IRQ_STACK old_rsp
DEBUG_ENTRY_ASSERT_IRQS_OFF
movq %rsp, \old_rsp
incl PER_CPU_VAR(irq_count)
/*
* Right now, if we just incremented irq_count to zero, we've
* claimed the IRQ stack but we haven't switched to it yet.
* Anything that can interrupt us here without using IST
* must be *extremely* careful to limit its stack usage. */
jnz .Lpush_old_rsp_\@
movq \old_rsp, PER_CPU_VAR(top_word_in_irq_stack)
movq PER_CPU_VAR(irq_stack_ptr), %rsp
.Lpush_old_rsp_\@:
pushq \old_rsp
.endm
This pushes the old pointer twice, but that's easy enough to fix if we really cared. I think I like this variant better. What do you think?
--Andy
Andy Lutomirski
maintainerRe: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 9 lines · Andy Lutomirski
Hmm. There's another option that might be considerably nicer, though:
put the IRQ stack at a known (at link time) position *in percpu space*. (Presumably it already is -- I haven't checked.) Then we do:.macro
ENTER_IRQ_STACKold_rsp
DEBUG_ENTRY_ASSERT_IRQS_OFF
movq %rsp, \old_rsp
inclPER_CPU_VAR(irq_count)
Quoted 13 lines
/*
* Right now, if we just incrementedirq_countto zero, we've
* claimed the IRQ stack but we haven't switched to it yet.
* Anything that can interrupt us here without using IST
* must be *extremely* careful to limit its stack usage. */
jnz .Lpush_old_rsp_\@
movq \old_rsp,PER_CPU_VAR(top_word_in_irq_stack)
movqPER_CPU_VAR(irq_stack_ptr), %rsp
.Lpush_old_rsp_\@:
pushq \old_rsp
.endm
How about the two commits here (well, soon to be there once gitweb catches up):
If you like them, want to add them to your series?
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 24 lines · Andy Lutomirski
Quoted 9 lines · Andy Lutomirski
Hmm. There's another option that might be considerably nicer, though:
put the IRQ stack at a known (at link time) position *in percpu space*. (Presumably it already is -- I haven't checked.) Then we do:.macro
ENTER_IRQ_STACKold_rsp
DEBUG_ENTRY_ASSERT_IRQS_OFF
movq %rsp, \old_rsp
inclPER_CPU_VAR(irq_count)Quoted 13 lines
/*
* Right now, if we just incrementedirq_countto zero, we've
* claimed the IRQ stack but we haven't switched to it yet.
* Anything that can interrupt us here without using IST
* must be *extremely* careful to limit its stack usage. */
jnz .Lpush_old_rsp_\@
movq \old_rsp,PER_CPU_VAR(top_word_in_irq_stack)
movqPER_CPU_VAR(irq_stack_ptr), %rsp
.Lpush_old_rsp_\@:
pushq \old_rsp
.endm
Quoted 5 lines
How about the two commits here (well, soon to be there once gitweb catches up):
If you like them, want to add them to your series?
The second patch looks good to me, thanks. I can pick up the patches.
A few comments about the first patch:
- It uses a '693:' label instead of '.Lirqs_off_\@:'
- There's a comment I don't follow:
"Anything that can interrupt us here without using IST must be *extremely* careful to limit its stack usage."
What specifically could interrupt there without using IST?
- Since do_softirq_own_stack() is a callable function, I think it still needs to save rbp.
- Why change the "jmp error_exit" to "ret" in
xen_do_hypervisor_callback()?
Signature · 1 line
Josh
Andy Lutomirski
maintainerRe: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 31 lines · Josh Poimboeuf
Quoted 24 lines · Andy Lutomirski
Quoted 9 lines · Andy Lutomirski
Hmm. There's another option that might be considerably nicer, though:
put the IRQ stack at a known (at link time) position *in percpu space*. (Presumably it already is -- I haven't checked.) Then we do:.macro
ENTER_IRQ_STACKold_rsp
DEBUG_ENTRY_ASSERT_IRQS_OFF
movq %rsp, \old_rsp
inclPER_CPU_VAR(irq_count)Quoted 13 lines
/*
* Right now, if we just incrementedirq_countto zero, we've
* claimed the IRQ stack but we haven't switched to it yet.
* Anything that can interrupt us here without using IST
* must be *extremely* careful to limit its stack usage. */
jnz .Lpush_old_rsp_\@
movq \old_rsp,PER_CPU_VAR(top_word_in_irq_stack)
movqPER_CPU_VAR(irq_stack_ptr), %rsp
.Lpush_old_rsp_\@:
pushq \old_rsp
.endmQuoted 5 lines
How about the two commits here (well, soon to be there once gitweb catches up):
If you like them, want to add them to your series?
Quoted 7 lines
The second patch looks good to me, thanks. I can pick up the patches.
A few comments about the first patch:
- It uses a '693:' label instead of '.Lirqs_off_\@:'
Touché!
Quoted 7 lines
- There's a comment I don't follow:
"Anything that can interrupt us here without using IST must be *extremely* careful to limit its stack usage."
What specifically could interrupt there without using IST?
#DB, later on in the series. I'll update the comment.
Quoted 3 lines
- Since
do_softirq_own_stack()is a callable function, I think it still needs to save rbp.
Whoops.
Quoted 3 lines
- Why change the "jmp
error_exit" to "ret" in
xen_do_hypervisor_callback()?
To match the other change I made there. I removed both.
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 22 lines · Andy Lutomirski
Quoted 5 lines
A few comments about the first patch:
- It uses a '693:' label instead of '.Lirqs_off_\@:'
Touché!
Quoted 7 lines
- There's a comment I don't follow:
"Anything that can interrupt us here without using IST must be *extremely* careful to limit its stack usage."
What specifically could interrupt there without using IST?
#DB, later on in the series. I'll update the comment.
Quoted 3 lines
- Since
do_softirq_own_stack()is a callable function, I think it still needs to save rbp.
Quoted 7 lines
Whoops.
Quoted 3 lines
- Why change the "jmp
error_exit" to "ret" in
xen_do_hypervisor_callback()?To match the other change I made there. I removed both.
One more thing I forgot to mention: if you could use r10 instead of r11, that would be helpful because it means one less register undwarf needs to know about. (It already deals with r10 because of GCC stack realignment).
Signature · 1 line
Josh
Andy Lutomirski
maintainerRe: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 29 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 5 lines
A few comments about the first patch:
- It uses a '693:' label instead of '.Lirqs_off_\@:'
Touché!
Quoted 7 lines
- There's a comment I don't follow:
"Anything that can interrupt us here without using IST must be *extremely* careful to limit its stack usage."
What specifically could interrupt there without using IST?
#DB, later on in the series. I'll update the comment.
Quoted 3 lines
- Since
do_softirq_own_stack()is a callable function, I think it still needs to save rbp.Whoops.
Quoted 3 lines
- Why change the "jmp
error_exit" to "ret" in
xen_do_hypervisor_callback()?
Quoted 6 lines
Quoted 1 line
To match the other change I made there. I removed both.
One more thing I forgot to mention: if you could use r10 instead of r11, that would be helpful because it means one less register undwarf needs to know about. (It already deals with r10 because of GCC stack realignment).
I'll let you figure that one out :)
(Although I think I agree with hpa: why not let it support all regs? Or am I missing something still?)
Quoted 3 lines
--
Josh
Re: [PATCH v2 6/8] x86/entry: add unwind hint annotations
Quoted 37 lines · Andy Lutomirski
Quoted 29 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 5 lines
A few comments about the first patch:
- It uses a '693:' label instead of '.Lirqs_off_\@:'
Touché!
Quoted 7 lines
- There's a comment I don't follow:
"Anything that can interrupt us here without using IST must be *extremely* careful to limit its stack usage."
What specifically could interrupt there without using IST?
#DB, later on in the series. I'll update the comment.
Quoted 3 lines
- Since
do_softirq_own_stack()is a callable function, I think it still needs to save rbp.Whoops.
Quoted 3 lines
- Why change the "jmp
error_exit" to "ret" in
xen_do_hypervisor_callback()?Quoted 6 lines
Quoted 1 line
To match the other change I made there. I removed both.
One more thing I forgot to mention: if you could use r10 instead of r11, that would be helpful because it means one less register undwarf needs to know about. (It already deals with r10 because of GCC stack realignment).
Quoted 4 lines
I'll let you figure that one out :)
(Although I think I agree with hpa: why not let it support all regs? Or am I missing something still?)
Sure, it can support all regs, but as the code is structured now, doing so means adding more switch cases, e.g.:
https://github.com/jpoimboe/linux/blob/undwarf-v2/arch/x86/kernel/unwind_undwarf.c#L388
And that makes the balrog angry!
Signature · 1 line
Josh
PATCH v2 7/8 x86/asm: add unwind hint annotations to sync_core()
In reply to Josh PoimboeufThis enables the undwarf unwinder to grok the iret in the middle of a C function.
--- arch/x86/include/asm/processor.h | 3 +++ 1 file changed, 3 insertions(+)
arch/x86/include/asm/processor.h +3 -0
@@ -22,6 +22,7 @@ struct vm86;#include <asm/nops.h> #include <asm/special_insns.h> #include <asm/fpu/types.h> +#include <asm/undwarf.h> #include <linux/personality.h> #include <linux/cache.h>
@@ -684,6 +685,7 @@ static inline void sync_core(void)unsigned int tmp; asmvolatile ( +UNWIND_HINT_SAVE "mov %%ss, %0\n\t" "pushq %q0\n\t" "pushq %%rsp\n\t"
@@ -693,6 +695,7 @@ static inline void sync_core(void)"pushq %q0\n\t" "pushq $1f\n\t" "iretq\n\t" +UNWIND_HINT_RESTORE "1:" : "=&r" (tmp), "+r" (__sp) : : "cc", "memory"); #endif
Signature · 1 line
2.7.5
PATCH v2 8/8 x86/unwind: add undwarf unwinder
In reply to Josh PoimboeufAdd 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.
--- 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
Documentation/x86/undwarf.txt new file +146 -0
@@ -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/[email protected] +[2] https://lkml.kernel.org/r/[email protected]
arch/um/include/asm/unwind.h new file +8 -0
@@ -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 */
arch/x86/Kconfig +1 -0
@@ -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
arch/x86/Kconfig.debug +25 -0
@@ -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
arch/x86/include/asm/module.h +9 -0
@@ -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 */
arch/x86/include/asm/unwind.h +48 -29
120 lines not on this page · raw message
arch/x86/kernel/Makefile +3 -5
@@ -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
arch/x86/kernel/module.c +11 -1
@@ -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 ; }
arch/x86/kernel/setup.c +3 -0
@@ -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
arch/x86/kernel/unwind_frame.c +16 -23
@@ -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: */
arch/x86/kernel/unwind_guess.c +5 -0
@@ -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;
arch/x86/kernel/unwind_undwarf.c new file +589 -0
589 lines not on this page · raw message
arch/x86/kernel/vmlinux.lds.S +2 -0
@@ -148,6 +148,8 @@ SECTIONS BUG_TABLE + UNDWARF_TABLE + . = ALIGN(PAGE_SIZE); __vvar_page = .;
include/asm-generic/vmlinux.lds.h +19 -1
@@ -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); \
lib/Kconfig.debug +3 -0
@@ -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.
scripts/Makefile.build +10 -4
@@ -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)
Signature · 1 line
2.7.5
Quoted 13 lines · Josh Poimboeuf
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.
Quoted 8 lines
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 3-5MB depending on the kernel config.
Note that it's not just a performance improvement, but also an instruction cache locality improvement: 3.2% .text savings almost directly transform into a similarly sized reduction in cache footprint. That can transform to even higher speedups for workloads whose cache locality is borderline.
I _really_ like this feature, and the independence of the debuginfo data format.
Logistically it's too bad we are 3 days away from the merge window to be able to pick this up:
Quoted 1 line
56 files changed, 3466 insertions(+), 1765 deletions(-)
OTOH most of the diffstat is in objtool.
Any objections to applying the first 3 objtool patches straight away and see whether anything breaks? That would significantly reduce the size of the rest of the patch set.
Quoted 1 line
I'm not tied to the 'undwarf' name, other naming ideas are welcome.
Ha, a new bike shed painting job! ;-)
I think 'undwarf' isn't a bad name, it's short, catchy and describes the purpose of the effort.
But I cannot resist some other suggestions, after 'elf' and 'dwarf' the obvious candidates from the peoples of Middle-earth would be:
- 'Hobbit'
- 'Eagle'
- 'Ent'
- 'Dragon'
- 'Troll'
- 'Ainur'
'struct troll_entry' has a certain charm to it.
'Eagle' is even nicer IMHO: larger than a dwarf but so much faster - and eagles are beautiful! Plus the name is 2 letters shorter than 'unwdwarf', win-win.
Thanks,
Ingo
Quoted 21 lines · Ingo Molnar
Quoted 18 lines · Josh Poimboeuf
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.
Quoted 8 lines
Quoted 3 lines
The main disadvantage of undwarf compared to frame pointers is that it needs more memory to store the undwarf table: roughly 3-5MB depending on the kernel config.
Note that it's not just a performance improvement, but also an instruction cache locality improvement: 3.2% .text savings almost directly transform into a similarly sized reduction in cache footprint. That can transform to even higher speedups for workloads whose cache locality is borderline.
I'll add that detail to the docs.
Quoted 5 lines
I _really_ like this feature, and the independence of the debuginfo data format.
Logistically it's too bad we are 3 days away from the merge window to be able to pick this up:
Quoted 7 lines
Quoted 1 line
56 files changed, 3466 insertions(+), 1765 deletions(-)OTOH most of the diffstat is in objtool.
Any objections to applying the first 3 objtool patches straight away and see whether anything breaks? That would significantly reduce the size of the rest of the patch set.
Merging the first 3 patches now sounds good to me. They implement "stack validation 2.0" which is a good standalone improvement even without undwarf. I think I've already ironed out all the issues reported by the build bot.
Quoted 17 lines
Quoted 1 line
I'm not tied to the 'undwarf' name, other naming ideas are welcome.
Ha, a new bike shed painting job! ;-)
I think 'undwarf' isn't a bad name, it's short, catchy and describes the purpose of the effort.
But I cannot resist some other suggestions, after 'elf' and 'dwarf' the obvious candidates from the peoples of Middle-earth would be:
- 'Hobbit'
- 'Eagle'
- 'Ent'
- 'Dragon'
- 'Troll'
- 'Ainur'
Quoted 4 lines
'struct
troll_entry' has a certain charm to it.'Eagle' is even nicer IMHO: larger than a dwarf but so much faster - and eagles are beautiful! Plus the name is 2 letters shorter than 'unwdwarf', win-win.
Finally, we get to the important part ;-)
Thus far I've been partial to undwarf, and I haven't been able to shake it.
But I like some of your suggestions. Especially troll and hobbit. Will need to do some more deep thinking about it :-)
Signature · 1 line
Josh
Quoted 22 lines · Josh Poimboeuf
Quoted 17 lines
Quoted 1 line
I'm not tied to the 'undwarf' name, other naming ideas are welcome.
Ha, a new bike shed painting job! ;-)
I think 'undwarf' isn't a bad name, it's short, catchy and describes the purpose of the effort.
But I cannot resist some other suggestions, after 'elf' and 'dwarf' the obvious candidates from the peoples of Middle-earth would be:
- 'Hobbit'
- 'Eagle'
- 'Ent'
- 'Dragon'
- 'Troll'
- 'Ainur'Quoted 4 lines
'struct
troll_entry' has a certain charm to it.'Eagle' is even nicer IMHO: larger than a dwarf but so much faster - and eagles are beautiful! Plus the name is 2 letters shorter than 'unwdwarf', win-win.
Quoted 7 lines
Finally, we get to the important part ;-)
Thus far I've been partial to undwarf, and I haven't been able to shake it.
But I like some of your suggestions. Especially troll and hobbit. Will need to do some more deep thinking about it :-)
After doing some research (i.e., skimming the "Middle-earth peoples" article on Wikipedia), my favorite is "Orc".
I don't have a reason other than the fact that "orc unwinder" sounds badass. And it's short. And also, orcs are enemies of dwarves :-)
I did like the symbolism of "Eagle", but unfortunately our own universe also has eagles, which diminishes down the word's Tolkien and Germanic mythology connotations. And I think we can all agree that such connotations are extremely important for an unwinder data format.
That said, while I like "orc", I also still like "undwarf", since I've been staring at that name for a few months, and as you said, it does describe its purpose.
So I'm leaning towards either "orc" or "undwarf".
Signature · 1 line
Josh