Skip to contentSkip to conversation

x86: undwarf unwinder

RFC PATCH Superseded Josh Poimboeuf 7 participants 55 messages Jun 1, 2017 – Jun 22, 2017

Subsystems (18)
Documentation x86 Kbuild ACPI +14 more subsystems

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover x86: undwarf unwinder

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-rfc

Here's the contents of the undwarf.txt file which explains the 'why' in more detail:

Undwarf 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.

Objtool generates the undwarf data by piggybacking on the compile-time stack metadata validation work described in stack-validation.txt. After analyzing all the code paths of a .o file, it creates an array of 'struct undwarf's and writes them to the .undwarf section.

Then at vmlinux link time, the .undwarf section is sorted by the sorttable script. The resulting sorted array of undwarf structs is used by the unwinder at runtime to correlate a given text address with its stack state.

Why not just use DWARF?
-----------------------

Undwarf has some of the same benefits as DWARF. Unlike frame pointers, the debuginfo is out-of-band. so it has no effect on runtime performance. Another benefit is that it's possible to reliably unwind across interrupts and exceptions.

Undwarf debuginfo's advantage over DWARF itself is that it's much simpler. It gets rid of the 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 relatively fast, which is important for perf and lockdep.

The undwarf format does have a few downsides. The undwarf table takes up extra memory -- something in the ballpark of 3-5MB, depending on the kernel config. In the future we may try to rearrange the data to compress that a bit.

Another 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. Will we end up having to track the state of more registers and eventually end up reinventing DWARF?

I think this is unlikely because GCC seems to save 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 control the format, e.g. no complex state machines.

Why generate undwarf with objtool?
----------------------------------

It should be possible to 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.

With compile-time stack metadata validation, objtool already follows all the code paths and 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.

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 it's much more maintainable than DWARF CFI annotations.

So the advantages of using objtool to generate undwarf are that it gives more accurate debuginfo, with close to zero annotations. It also insulates the kernel from toolchain bugs which can be very painful to deal with in the kernel since it often has 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 flows. If GCC optimizations become too complicated for objtool to follow, the undwarf generation might stop working or become incomplete. In such a case 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.

Josh Poimboeuf (10):
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 CFI hints x86/entry: add CFI hint undwarf annotations
x86/asm: add CFI hint annotations to sync_core()
extable: rename 'sortextable' script to 'sorttable'
extable: add undwarf table sorting ability to sorttable script x86/unwind: add undwarf unwinder

 Documentation/dontdiff                           |    2 +-
 arch/um/include/asm/unwind.h                     |    7 +
 arch/x86/Kconfig                                 |    1 +
 arch/x86/Kconfig.debug                           |   26 +
 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                         |    5 +
 arch/x86/entry/entry_64.S                        |   56 +-
 arch/x86/include/asm/module.h                    |    8 +
 arch/x86/include/asm/processor.h                 |    3 +
 arch/x86/include/asm/undwarf-types.h             |  100 ++
 arch/x86/include/asm/undwarf.h                   |   97 ++
 arch/x86/include/asm/unwind.h                    |   64 +-
 arch/x86/kernel/Makefile                         |    9 +-
 arch/x86/kernel/acpi/Makefile                    |    2 +
 arch/x86/kernel/kprobes/opt.c                    |    9 +-
 arch/x86/kernel/module.c                         |    9 +-
 arch/x86/kernel/reboot.c                         |    2 +
 arch/x86/kernel/unwind_frame.c                   |   39 +-
 arch/x86/kernel/unwind_guess.c                   |    5 +
 arch/x86/kernel/unwind_undwarf.c                 |  402 +++++++
 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                |   14 +
 init/Kconfig                                     |    4 +
 kernel/kexec_core.c                              |    4 +-
 lib/Kconfig.debug                                |    3 +
 scripts/.gitignore                               |    2 +-
 scripts/Makefile                                 |    4 +-
 scripts/Makefile.build                           |    3 +-
 scripts/link-vmlinux.sh                          |   12 +-
 scripts/{sortextable.c => sorttable.c}           |  182 +--
 scripts/{sortextable.h => sorttable.h}           |   69 +-
 tools/objtool/Build                              |    3 +
 tools/objtool/Documentation/stack-validation.txt |  194 ++--
 tools/objtool/Documentation/undwarf.txt          |   99 ++
 tools/objtool/Makefile                           |    5 +-
 tools/objtool/arch.h                             |   64 +-
 tools/objtool/arch/x86/decode.c                  |  400 ++++++-
 tools/objtool/builtin-check.c                    | 1280 +---------------------
 tools/objtool/builtin-undwarf.c                  |   70 ++
 tools/objtool/builtin.h                          |    1 +
 tools/objtool/cfi.h                              |   55 +
 tools/objtool/{builtin-check.c => check.c}       |  868 +++++++++++----
 tools/objtool/check.h                            |   69 ++
 tools/objtool/elf.c                              |  247 ++++-
 tools/objtool/elf.h                              |    8 +-
 tools/objtool/objtool.c                          |    3 +-
 tools/objtool/special.c                          |    6 +-
 tools/objtool/undwarf-types.h                    |  100 ++
 tools/objtool/undwarf.c                          |  308 ++++++
 tools/objtool/{builtin.h => undwarf.h}           |   19 +-
 59 files changed, 3124 insertions(+), 1846 deletions(-)
 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
 rename scripts/{sortextable.c => sorttable.c} (71%)
 rename scripts/{sortextable.h => sorttable.h} (77%)
 create mode 100644 tools/objtool/Documentation/undwarf.txt
 create mode 100644 tools/objtool/builtin-undwarf.c
 create mode 100644 tools/objtool/cfi.h
 copy tools/objtool/{builtin-check.c => check.c} (62%)
 create mode 100644 tools/objtool/check.h
 create mode 100644 tools/objtool/undwarf-types.h
 create mode 100644 tools/objtool/undwarf.c
 copy tools/objtool/{builtin.h => undwarf.h} (64%)
Signature · 1 line
2.7.4
#1 lore raw

RFC PATCH 1/10 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              | 1280 +---------------------------
 tools/objtool/{builtin-check.c => check.c} |   58 +-
 tools/objtool/check.h                      |   52 ++
 4 files changed, 71 insertions(+), 1320 deletions(-)
 copy tools/objtool/{builtin-check.c => check.c} (95%)
 create mode 100644 tools/objtool/check.h
tools/objtool/Build +1 -0
index d6cdece..6f2e198 100644
@@ -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 -1267

1,304 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 +52 -0

52 lines not on this page · raw message

Signature · 1 line
2.7.4
Reviewed-by on v1 1/10
#2 lore raw

Re: [RFC PATCH 01/10] objtool: move checking code to check.c

Quoted 5 lines · Josh Poimboeuf

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.

thanks,

Signature · 2 lines
js
suse labs
#3 lore raw

RFC PATCH 2/10 objtool, x86: add several functions and files to the objtool whitelist

In reply to Josh Poimboeuf

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
index 34b3fa2..9e32d40 100644
@@ -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
index 2f87563..2e14acc 100644
@@ -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
index 41089e7..45b4fca 100644
@@ -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
index 4b99423..3c7c419 100644
@@ -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
index 26b78d8..85a9e17 100644
@@ -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
index 901c640..69ea0bc 100644
@@ -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
index 2544700..67393fc 100644
@@ -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
index 183ddb2..bb7a502 100644
@@ -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>
@@ -4908,6 +4909,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
index 72f7839..bc8b933 100644
@@ -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"
 
@@ -8675,6 +8676,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)
 {
@@ -9051,6 +9053,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
index c815564..10ffa7e 100644
@@ -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
index 90568c3..fefb4b6 100644
@@ -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
index f1d83b3..2f56e1e 100644
@@ -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
index a6a198c..0504187 100644
@@ -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
index fffb0a1..bced7a3 100644
@@ -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
index ae1a3ba..154ffb4 100644
@@ -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.4
#4 lore raw

Re: [RFC PATCH 02/10] objtool, x86: add several functions and files to the objtool whitelist

On 06/01/2017, 07:44 AM, Josh Poimboeuf wrote:
...

Quoted 11 lines
arch/x86/kernel/kprobes/opt.c +1 -0
@@ -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>

arch/x86/kernel/kprobes/opt.c static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val) · line 98

Quoted 4 lines
arch/x86/kernel/kprobes/opt.c +1 -0
@@ -94,6 +95,7 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
 }
 
 asm (
+			"optprobe_template_func:\n"

Why do you add another symbol here? What's wrong with optprobe_template_entry?

Quoted 7 lines
arch/x86/kernel/kprobes/opt.c +0 -0
 			".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"

arch/x86/kernel/kprobes/opt.c asm ( · line 144

Quoted 10 lines
arch/x86/kernel/kprobes/opt.c +6 -1
-			"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)

thanks,

Signature · 2 lines
js
suse labs
#5 lore raw

Re: [RFC PATCH 02/10] objtool, x86: add several functions and files to the objtool whitelist

Quoted 15 lines · Jiri Slaby

On 06/01/2017, 07:44 AM, Josh Poimboeuf wrote:
...

Quoted 13 lines
arch/x86/kernel/kprobes/opt.c +1 -0
@@ -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)
 }
 
Quoted 5 lines

arch/x86/kernel/kprobes/opt.c static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val) · line 98

Quoted 2 lines
arch/x86/kernel/kprobes/opt.c +1 -0
 asm (
+			"optprobe_template_func:\n"

Why do you add another symbol here? What's wrong with optprobe_template_entry?

I tried to do that, but the STACK_FRAME_NON_STANDARD macro needs a function, and optprobe_template_entry is defined elsewhere as a data symbol with a type of kprobe_opcode_t. So I had to wrap the asm code inside a function.

Signature · 1 line
Josh
#6 lore raw

RFC PATCH 3/10 objtool: stack validation 2.0

In reply to 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.

---
 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                            | 595 ++++++++++++++++++-----
 tools/objtool/check.h                            |  18 +-
 tools/objtool/elf.c                              |  23 +-
 tools/objtool/elf.h                              |   3 +-
 tools/objtool/special.c                          |   6 +-
 10 files changed, 1023 insertions(+), 296 deletions(-)
 create mode 100644 tools/objtool/cfi.h
tools/objtool/Documentation/stack-validation.txt +65 -88
index 55a60d3..17c1195 100644
@@ -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
index 27e019c..0e2765e 100644
@@ -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 +462 -133

832 lines not on this page · raw message

tools/objtool/check.h +16 -2
index b0ac3ba..da85f5b 100644
@@ -24,19 +24,30 @@
 #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 {
@@ -49,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 +7 -16
index d897702..3fb0747 100644
@@ -182,20 +182,19 @@ static int read_sections(struct elf *elf)
 			return -1;
 		}
 
-		sec->elf_data = elf_getdata(s, NULL);
-		if (!sec->elf_data) {
+		sec->data = elf_getdata(s, NULL);
+		if (!sec->data) {
 			perror("elf_getdata");
 			return -1;
 		}
 
-		if (sec->elf_data->d_off != 0 ||
-		    sec->elf_data->d_size != sec->sh.sh_size) {
+		if (sec->data->d_off != 0 ||
+		    sec->data->d_size != sec->sh.sh_size) {
 			WARN("unexpected data attributes for %s", sec->name);
 			return -1;
 		}
 
-		sec->data = (unsigned long)sec->elf_data->d_buf;
-		sec->len = sec->elf_data->d_size;
+		sec->len = sec->data->d_size;
 	}
 
 	/* sanity check, one more call to elf_nextscn() should return NULL */
@@ -232,7 +231,7 @@ static int read_symbols(struct elf *elf)
 
 		sym->idx = i;
 
-		if (!gelf_getsym(symtab->elf_data, i, &sym->sym)) {
+		if (!gelf_getsym(symtab->data, i, &sym->sym)) {
 			perror("gelf_getsym");
 			goto err;
 		}
@@ -322,7 +321,7 @@ static int read_relas(struct elf *elf)
 			}
 			memset(rela, 0, sizeof(*rela));
 
-			if (!gelf_getrela(sec->elf_data, i, &rela->rela)) {
+			if (!gelf_getrela(sec->data, i, &rela->rela)) {
 				perror("gelf_getrela");
 				return -1;
 			}
@@ -362,12 +361,6 @@ struct elf *elf_open(const char *name)
 
 	INIT_LIST_HEAD(&elf->sections);
 
-	elf->name = strdup(name);
-	if (!elf->name) {
-		perror("strdup");
-		goto err;
-	}
-
 	elf->fd = open(name, O_RDONLY);
 	if (elf->fd == -1) {
 		perror("open");
@@ -421,8 +414,6 @@ void elf_close(struct elf *elf)
 		list_del(&sec->list);
 		free(sec);
 	}
-	if (elf->name)
-		free(elf->name);
 	if (elf->fd > 0)
 		close(elf->fd);
 	if (elf->elf)
tools/objtool/elf.h +1 -2
index 731973e..75e44c7 100644
@@ -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;
 };
 
tools/objtool/special.c +3 -3
index bff8abb..84f001d 100644
@@ -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);
 
 		/*
Signature · 1 line
2.7.4
#7 lore raw

RFC PATCH 4/10 objtool: add undwarf debuginfo generation

In reply to 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.txt for a more detailed description of this new debuginfo format and why it's preferable to DWARF.

---
 tools/objtool/Build                              |   2 +
 tools/objtool/Documentation/stack-validation.txt |  45 +---
 tools/objtool/Documentation/undwarf.txt          |  99 ++++++++
 tools/objtool/builtin-check.c                    |   2 +-
 tools/objtool/builtin-undwarf.c                  |  70 ++++++
 tools/objtool/builtin.h                          |   1 +
 tools/objtool/check.c                            |  57 ++++-
 tools/objtool/check.h                            |   7 +-
 tools/objtool/elf.c                              | 224 +++++++++++++++++
 tools/objtool/elf.h                              |   5 +
 tools/objtool/objtool.c                          |   3 +-
 tools/objtool/undwarf-types.h                    | 100 ++++++++
 tools/objtool/undwarf.c                          | 308 +++++++++++++++++++++++
 tools/objtool/{builtin.h => undwarf.h}           |  19 +-
 14 files changed, 898 insertions(+), 44 deletions(-)
 create mode 100644 tools/objtool/Documentation/undwarf.txt
 create mode 100644 tools/objtool/builtin-undwarf.c
 create mode 100644 tools/objtool/undwarf-types.h
 create mode 100644 tools/objtool/undwarf.c
 copy tools/objtool/{builtin.h => undwarf.h} (64%)
tools/objtool/Build +2 -0
index 6f2e198..845c879 100644
@@ -1,6 +1,8 @@
 objtool-y += arch/$(SRCARCH)/
 objtool-y += builtin-check.o
+objtool-y += builtin-undwarf.o
 objtool-y += check.o
+objtool-y += undwarf.o
 objtool-y += elf.o
 objtool-y += special.o
 objtool-y += objtool.o
tools/objtool/Documentation/stack-validation.txt +11 -34

63 lines not on this page · raw message

tools/objtool/Documentation/undwarf.txt new file +99 -0

99 lines not on this page · raw message

tools/objtool/builtin-check.c +1 -1
index 365c34e..eedf089 100644
@@ -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
index 34d2ba7..0b9722f 100644
@@ -18,5 +18,6 @@
 #define _BUILTIN_H
 
 extern int cmd_check(int argc, const char **argv);
+extern int cmd_undwarf(int argc, const char **argv);
 
 #endif /* _BUILTIN_H */
tools/objtool/check.c +56 -1

105 lines not on this page · raw message

tools/objtool/check.h +5 -2
index da85f5b..e56bb1c 100644
@@ -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;
@@ -41,13 +43,14 @@ 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, needs_cfi;
 	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 undwarf undwarf;
 };
 
 struct objtool_file {
@@ -58,7 +61,7 @@ struct objtool_file {
 	bool ignore_unreachables, c_file;
 };
 
-int check(const char *objname, bool nofp);
+int check(const char *objname, bool nofp, bool undwarf);
 
 #define for_each_insn(file, insn)					\
 	list_for_each_entry(insn, &file->insn_list, list)
tools/objtool/elf.c +224 -0

230 lines not on this page · raw message

tools/objtool/elf.h +5 -0
index 75e44c7..f9d68ea 100644
@@ -83,6 +83,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_to_file(struct elf *elf, char *outfile);
 void elf_close(struct elf *elf);
 
 
tools/objtool/objtool.c +2 -1
index ecc5b1b..b2051d1 100644
@@ -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 +100 -0

100 lines not on this page · raw message

tools/objtool/undwarf.c new file +308 -0

308 lines not on this page · raw message

tools/objtool/undwarf.h renamed from tools/objtool/builtin.h +14 -5
similarity index 64%
copy from tools/objtool/builtin.h
copy to tools/objtool/undwarf.h
index 34d2ba7..76346e7 100644
@@ -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,18 @@
  * 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_section(struct objtool_file *file);
+int update_file(struct objtool_file *file);
+
+int undwarf_dump(const char *objname);
+
+#endif /* _UNDWARF_H */
Signature · 1 line
2.7.4
#8 lore raw

Re: [RFC PATCH 04/10] objtool: add undwarf debuginfo generation

On 06/01/2017, 07:44 AM, Josh Poimboeuf wrote:
...

Quoted 3 lines

index 3fb0747..1ca5d9a 100644

tools/objtool/elf.c +0 -0

...

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 536

Quoted 11 lines
tools/objtool/elf.c +11 -0
+int elf_write_to_file(struct elf *elf, char *outfile)
+{
+	int fd;
+	struct section *sec;
+	Elf *elfout;
+	GElf_Ehdr eh, ehout;
+	Elf_Scn *scn;
+	Elf_Data *data;
+	GElf_Shdr sh;
+
+	fd = creat(outfile, 0777);

0755 even though it is umasked?

Quoted 7 lines
tools/objtool/elf.c +7 -0
+	if (fd == -1) {
+		perror("creat");
+		return -1;
+	}
+
+	elfout = elf_begin(fd, ELF_C_WRITE, NULL);
+	if (!elfout) {

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 553

Quoted 10 lines
tools/objtool/elf.c +10 -0
+		perror("elf_begin");
+		return -1;
+	}
+
+	if (!gelf_newehdr(elfout, gelf_getclass(elf->elf))) {
+		perror("gelf_newehdr");
+		return -1;
+	}
+
+	if (!gelf_getehdr(elfout, &ehout)) {

This does not make much sense to do. You memset(0) it below.

Quoted 26 lines
tools/objtool/elf.c +26 -0
+		perror("gelf_getehdr");
+		return -1;
+	}
+
+	if (!gelf_getehdr(elf->elf, &eh)) {
+		perror("gelf_getehdr");
+		return -1;
+	}
+
+	memset(&ehout, 0, sizeof(ehout));
+	ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA];
+	ehout.e_machine = eh.e_machine;
+	ehout.e_type = eh.e_type;
+	ehout.e_version = EV_CURRENT;
+	ehout.e_shstrndx = find_section_by_name(elf, ".shstrtab")->idx;
+
+	list_for_each_entry(sec, &elf->sections, list) {
+		if (sec->idx == 0)
+			continue;
+
+		scn = elf_newscn(elfout);
+		if (!scn) {
+			perror("elf_newscn");
+			return -1;
+		}
+

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 589

Quoted 10 lines
tools/objtool/elf.c +10 -0
+		data = elf_newdata(scn);
+		if (!data) {
+			perror("elf_newdata");
+			return -1;
+		}
+
+		if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) {
+			perror("elf_flagdata");
+			return -1;
+		}

There is not much point setting DIRTY flag here. elf_newdata does so.

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 598

Quoted 8 lines
tools/objtool/elf.c +8 -0
+		data->d_type = sec->data->d_type;
+		data->d_buf = sec->data->d_buf;
+		data->d_size = sec->data->d_size;
+
+		if(!gelf_getshdr(scn, &sh)) {
+			perror("gelf_getshdr");
+			return -1;
+		}

This does not make much sense to do again. You overwrite the content right away:

Quoted 7 lines
tools/objtool/elf.c +7 -0
+		sh = sec->sh;
+
+		if (!gelf_update_shdr(scn, &sh)) {
+			perror("gelf_update_shdr");
+			return -1;
+		}
+	}

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 616

Quoted 10 lines
tools/objtool/elf.c +10 -0
+
+	if (!gelf_update_ehdr(elfout, &ehout)) {
+		perror("gelf_update_ehdr");
+		return -1;
+	}
+
+	if (elf_update(elfout, ELF_C_WRITE) < 0) {
+		perror("elf_update");
+		return -1;
+	}

elf_end() + close() ?

Quoted 7 lines
tools/objtool/elf.c +4 -0
+
+	return 0;
+}
+
 void elf_close(struct elf *elf)
 {
 	struct section *sec, *tmpsec;

...

Quoted 3 lines
tools/objtool/undwarf.c new file +0 -0
@@ -0,0 +1,308 @@

...

Quoted 22 lines
tools/objtool/undwarf.c new file +22 -0
+int undwarf_dump(const char *_objname)
+{
+	struct elf *elf;
+	struct section *sec;
+	struct rela *rela;
+	struct undwarf *undwarf;
+	int nr, i;
+
+	objname = _objname;
+
+	elf = elf_open(objname);
+	if (!elf) {
+		WARN("error reading elf file %s\n", objname);
+		return 1;
+	}
+
+	sec = find_section_by_name(elf, ".undwarf");
+	if (!sec || !sec->rela)
+		return 0;
+
+	nr = sec->len / sizeof(*undwarf);
+	for (i = 0; i < nr; i++) {

...

tools/objtool/undwarf.c

Quoted 1 line
tools/objtool/undwarf.c new file +1 -0
+	}

elf_close() ?

tools/objtool/undwarf.c

Quoted 3 lines
tools/objtool/undwarf.c new file +3 -0
+
+	return 0;
+}

thanks,

Signature · 2 lines
js
suse labs
#9 lore raw

Re: [RFC PATCH 04/10] objtool: add undwarf debuginfo generation

Quoted 148 lines · Jiri Slaby

On 06/01/2017, 07:44 AM, Josh Poimboeuf wrote:
...

Quoted 3 lines

index 3fb0747..1ca5d9a 100644

tools/objtool/elf.c +0 -0

...

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 536

Quoted 11 lines
tools/objtool/elf.c +11 -0
+int elf_write_to_file(struct elf *elf, char *outfile)
+{
+	int fd;
+	struct section *sec;
+	Elf *elfout;
+	GElf_Ehdr eh, ehout;
+	Elf_Scn *scn;
+	Elf_Data *data;
+	GElf_Shdr sh;
+
+	fd = creat(outfile, 0777);

0755 even though it is umasked?

Quoted 7 lines
tools/objtool/elf.c +7 -0
+	if (fd == -1) {
+		perror("creat");
+		return -1;
+	}
+
+	elfout = elf_begin(fd, ELF_C_WRITE, NULL);
+	if (!elfout) {

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 553

Quoted 10 lines
tools/objtool/elf.c +10 -0
+		perror("elf_begin");
+		return -1;
+	}
+
+	if (!gelf_newehdr(elfout, gelf_getclass(elf->elf))) {
+		perror("gelf_newehdr");
+		return -1;
+	}
+
+	if (!gelf_getehdr(elfout, &ehout)) {

This does not make much sense to do. You memset(0) it below.

Quoted 26 lines
tools/objtool/elf.c +26 -0
+		perror("gelf_getehdr");
+		return -1;
+	}
+
+	if (!gelf_getehdr(elf->elf, &eh)) {
+		perror("gelf_getehdr");
+		return -1;
+	}
+
+	memset(&ehout, 0, sizeof(ehout));
+	ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA];
+	ehout.e_machine = eh.e_machine;
+	ehout.e_type = eh.e_type;
+	ehout.e_version = EV_CURRENT;
+	ehout.e_shstrndx = find_section_by_name(elf, ".shstrtab")->idx;
+
+	list_for_each_entry(sec, &elf->sections, list) {
+		if (sec->idx == 0)
+			continue;
+
+		scn = elf_newscn(elfout);
+		if (!scn) {
+			perror("elf_newscn");
+			return -1;
+		}
+

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 589

Quoted 10 lines
tools/objtool/elf.c +10 -0
+		data = elf_newdata(scn);
+		if (!data) {
+			perror("elf_newdata");
+			return -1;
+		}
+
+		if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) {
+			perror("elf_flagdata");
+			return -1;
+		}

There is not much point setting DIRTY flag here. elf_newdata does so.

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 598

Quoted 8 lines
tools/objtool/elf.c +8 -0
+		data->d_type = sec->data->d_type;
+		data->d_buf = sec->data->d_buf;
+		data->d_size = sec->data->d_size;
+
+		if(!gelf_getshdr(scn, &sh)) {
+			perror("gelf_getshdr");
+			return -1;
+		}

This does not make much sense to do again. You overwrite the content right away:

Quoted 7 lines
tools/objtool/elf.c +7 -0
+		sh = sec->sh;
+
+		if (!gelf_update_shdr(scn, &sh)) {
+			perror("gelf_update_shdr");
+			return -1;
+		}
+	}

tools/objtool/elf.c struct elf *elf_open(const char *name) · line 616

Quoted 10 lines
tools/objtool/elf.c +10 -0
+
+	if (!gelf_update_ehdr(elfout, &ehout)) {
+		perror("gelf_update_ehdr");
+		return -1;
+	}
+
+	if (elf_update(elfout, ELF_C_WRITE) < 0) {
+		perror("elf_update");
+		return -1;
+	}

elf_end() + close() ?

Quoted 7 lines
tools/objtool/elf.c +4 -0
+
+	return 0;
+}
+
 void elf_close(struct elf *elf)
 {
 	struct section *sec, *tmpsec;

...

Quoted 3 lines
tools/objtool/undwarf.c new file +0 -0
@@ -0,0 +1,308 @@

...

Quoted 22 lines
tools/objtool/undwarf.c new file +22 -0
+int undwarf_dump(const char *_objname)
+{
+	struct elf *elf;
+	struct section *sec;
+	struct rela *rela;
+	struct undwarf *undwarf;
+	int nr, i;
+
+	objname = _objname;
+
+	elf = elf_open(objname);
+	if (!elf) {
+		WARN("error reading elf file %s\n", objname);
+		return 1;
+	}
+
+	sec = find_section_by_name(elf, ".undwarf");
+	if (!sec || !sec->rela)
+		return 0;
+
+	nr = sec->len / sizeof(*undwarf);
+	for (i = 0; i < nr; i++) {

...

Quoted 7 lines

tools/objtool/undwarf.c

Quoted 1 line
tools/objtool/undwarf.c new file +1 -0
+	}

elf_close() ?

tools/objtool/undwarf.c

Quoted 3 lines
tools/objtool/undwarf.c new file +3 -0
+
+	return 0;
+}

I agree with all your comments, will fix them all. Thanks for the review.

Signature · 1 line
Josh
#10 lore raw

Re: [RFC PATCH 04/10] objtool: add undwarf debuginfo generation

Quoted 2 lines · Josh Poimboeuf

I agree with all your comments, will fix them all. Thanks for the review.

This is not the correct way:
++ if (flags & O_WRONLY)
++ cmd = ELF_C_WRITE;
++ else if (flags & O_RDWR)
++ cmd = ELF_C_RDWR;
++ else
++ cmd = ELF_C_READ_MMAP;

For this particular codeflow, it works, but it should be:
(flags & O_ACCMODE) == O_WRONLY

                    == O_RDWR

thanks,

Signature · 2 lines
js
suse labs
#11 lore raw

Re: [RFC PATCH 04/10] objtool: add undwarf debuginfo generation

Quoted 12 lines · Jiri Slaby
Quoted 2 lines · Josh Poimboeuf

I agree with all your comments, will fix them all. Thanks for the review.

This is not the correct way:
++ if (flags & O_WRONLY)
++ cmd = ELF_C_WRITE;
++ else if (flags & O_RDWR)
++ cmd = ELF_C_RDWR;
++ else
++ cmd = ELF_C_READ_MMAP;

Quoted 3 lines

For this particular codeflow, it works, but it should be:
(flags & O_ACCMODE) == O_WRONLY

                    == O_RDWR

Ok, thanks. I see you started reviewing v2 early ;-) It's almost ready to post, will send the patches next week.

Signature · 1 line
Josh
#12 lore raw

RFC PATCH 5/10 objtool, x86: add facility for asm code to provide CFI hints

In reply to Josh Poimboeuf

Some 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 CFI 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.

---
 arch/x86/include/asm/undwarf-types.h | 100 ++++++++++++++++++++
 arch/x86/include/asm/undwarf.h       |  97 +++++++++++++++++++
 tools/objtool/Makefile               |   3 +
 tools/objtool/check.c                | 176 ++++++++++++++++++++++++++++++++++-
 tools/objtool/check.h                |   4 +-
 5 files changed, 373 insertions(+), 7 deletions(-)
 create mode 100644 arch/x86/include/asm/undwarf-types.h
 create mode 100644 arch/x86/include/asm/undwarf.h
arch/x86/include/asm/undwarf-types.h new file +100 -0
new file mode 100644
index 0000000..b624188
@@ -0,0 +1,100 @@
+/*
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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 _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.
+ *
+ * The CFI_HINT macros are only used for the undwarf_cfi_hints 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 CFI_HINT_TYPE_SAVE		3
+#define CFI_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
+ * 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)
+ * on the stack, given a code address (IP).
+ */
+struct undwarf {
+	int ip;
+	unsigned int len;
+	short cfa_offset;
+	short bp_offset;
+	unsigned cfa_reg:4;
+	unsigned bp_reg:4;
+	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 undwarf_cfi_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 +97 -0

97 lines not on this page · raw message

tools/objtool/Makefile +3 -0
index 0e2765e..7997d5c 100644
@@ -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 +171 -5

235 lines not on this page · raw message

tools/objtool/check.h +2 -2
index e56bb1c..d711336 100644
@@ -43,7 +43,7 @@ struct instruction {
 	unsigned int len;
 	unsigned char type;
 	unsigned long immediate;
-	bool alt_group, visited, dead_end, ignore, needs_cfi;
+	bool alt_group, visited, dead_end, ignore, needs_cfi, 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);
Signature · 1 line
2.7.4
maintainer
#13 lore raw

Re: [RFC PATCH 05/10] objtool, x86: add facility for asm code to provide CFI hints

Quoted 10 lines · Josh Poimboeuf

Some 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 CFI 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.

Two minor suggestions:

Could you prefix these with something other than "CFI_"? For those of use who have read the binutils manual, using "CFI_" sounds awfully like .cfi_, and people might expect the semantics to be the same.

arch/x86/include/asm/undwarf.h line 80

Quoted 2 lines
arch/x86/include/asm/undwarf.h new file +2 -0
+#define CFI_HINT(cfa_reg, cfa_offset, type)                    \
+       "999: \n\t"                                             \

Have you checked if 999 is used elsewhere? My personal preference is to use:

.Ldescriptive_text_\@:

instead of a hopefully-unique number. I never researched the history, but I suspect that the convention of using large numbers came from early binutils versions that didn't have \@, but we use \@ fairly extensively in the kernel these days, so it would seem that we no longer support binutils versions that old.

#14 lore raw

Re: [RFC PATCH 05/10] objtool, x86: add facility for asm code to provide CFI hints

Quoted 12 lines · Andy Lutomirski
Quoted 10 lines · Josh Poimboeuf

Some 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 CFI 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.

Quoted 5 lines

Two minor suggestions:

Could you prefix these with something other than "CFI_"? For those of use who have read the binutils manual, using "CFI_" sounds awfully like .cfi_, and people might expect the semantics to be the same.

The intention was that even if this undwarf thing doesn't work out, the CFI_ macros could still be used by objtool to generate proper DWARF. Would prefixing them with CFI_HINT_ be better? Or UNWIND_HINT_?

Quoted 5 lines

arch/x86/include/asm/undwarf.h line 80

Quoted 2 lines
arch/x86/include/asm/undwarf.h new file +2 -0
+#define CFI_HINT(cfa_reg, cfa_offset, type)                    \
+       "999: \n\t"                                             \

Have you checked if 999 is used elsewhere? My personal preference is to use:

Quoted 7 lines

.Ldescriptive_text_\@:

instead of a hopefully-unique number. I never researched the history, but I suspect that the convention of using large numbers came from early binutils versions that didn't have \@, but we use \@ fairly extensively in the kernel these days, so it would seem that we no longer support binutils versions that old.

Yeah, that would be a lot better, thanks.

Signature · 1 line
Josh
maintainer
#15 lore raw

Re: [RFC PATCH 05/10] objtool, x86: add facility for asm code to provide CFI hints

Quoted 15 lines · Josh Poimboeuf
Quoted 14 lines · Andy Lutomirski
Quoted 10 lines · Josh Poimboeuf

Some 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 CFI 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.

Two minor suggestions:

Quoted 7 lines
Quoted 3 lines

Could you prefix these with something other than "CFI_"? For those of use who have read the binutils manual, using "CFI_" sounds awfully like .cfi_, and people might expect the semantics to be the same.

The intention was that even if this undwarf thing doesn't work out, the CFI_ macros could still be used by objtool to generate proper DWARF. Would prefixing them with CFI_HINT_ be better? Or UNWIND_HINT_?

This has nothing to do with the data format or implementation. I
just think that "CFI_" suggests that they're semantically equivalent to binutils' .cfi directives. If they're not, then maybe UNWIND_HINT is better.

#16 lore raw

Re: [RFC PATCH 05/10] objtool, x86: add facility for asm code to provide CFI hints

Quoted 20 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 17 lines · Andy Lutomirski
Quoted 10 lines · Josh Poimboeuf

Some 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 CFI 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.

Two minor suggestions:

Could you prefix these with something other than "CFI_"? For those of use who have read the binutils manual, using "CFI_" sounds awfully like .cfi_, and people might expect the semantics to be the same.

Quoted 8 lines
Quoted 3 lines

The intention was that even if this undwarf thing doesn't work out, the CFI_ macros could still be used by objtool to generate proper DWARF. Would prefixing them with CFI_HINT_ be better? Or UNWIND_HINT_?

This has nothing to do with the data format or implementation. I
just think that "CFI_" suggests that they're semantically equivalent to binutils' .cfi directives. If they're not, then maybe UNWIND_HINT is better.

Ok, I'll go with the UNWIND_HINT_ prefix.

Signature · 1 line
Josh
#17 lore raw

RFC PATCH 6/10 x86/entry: add CFI hint undwarf annotations

In reply to Josh Poimboeuf

Add CFI hint undwarf 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  |  5 +++++
 arch/x86/entry/entry_64.S | 56 ++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 55 insertions(+), 7 deletions(-)
arch/x86/entry/Makefile +0 -1
index 9976fce..af28a8a 100644
@@ -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 +5 -0
index 05ed3d3..bbec02e 100644
@@ -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)
+	CFI_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)
+	CFI_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
+	CFI_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
arch/x86/entry/entry_64.S +50 -6

305 lines not on this page · raw message

Signature · 1 line
2.7.4
maintainer
#18 lore raw

Re: [RFC PATCH 06/10] x86/entry: add CFI hint undwarf annotations

Quoted 28 lines · Josh Poimboeuf

Add CFI hint undwarf 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  |  5 +++++
 arch/x86/entry/entry_64.S | 56 ++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 55 insertions(+), 7 deletions(-)
arch/x86/entry/Makefile +0 -1
index 9976fce..af28a8a 100644
@@ -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 +0 -0
index 05ed3d3..bbec02e 100644

arch/x86/entry/calling.h line 6

Quoted 6 lines
arch/x86/entry/calling.h +2 -0
@@ -1,4 +1,6 @@
 #include <linux/jump_label.h>
+#include <asm/undwarf.h>
+
 
 /*
 

Just to make sure I understand this, if we unwind from...

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 115

Quoted 3 lines
arch/x86/entry/calling.h +0 -0
@@ -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)

...here..., will objtool think that rdx and rsi (etc) still live in their respective regs, or will it find them in the on-stack data given by CFI_REGS? If the former, how does undwarf deal with the corresponding pops?

arch/x86/entry/calling.h ENTRY(ret_from_fork) · line 427

Quoted 8 lines
arch/x86/entry/calling.h +2 -0
        movq %rdi, 14*8+\offset(%rsp)
+       CFI_REGS offset=\offset extra=0
 
@@ -414,6 +424,7 @@ ENTRY(ret_from_fork)
 2:
        movq    %rsp, %rdi
        call    syscall_return_slowpath /* returns with IRQs disabled */
+       CFI_REGS

I'm confused. syscall_return_slowpath didn't change anything relevant to unwinding, right? What's CFI_REGS here for?

#19 lore raw

Re: [RFC PATCH 06/10] x86/entry: add CFI hint undwarf annotations

Quoted 38 lines · Andy Lutomirski
Quoted 28 lines · Josh Poimboeuf

Add CFI hint undwarf 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  |  5 +++++
 arch/x86/entry/entry_64.S | 56 ++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 55 insertions(+), 7 deletions(-)
arch/x86/entry/Makefile +0 -1
index 9976fce..af28a8a 100644
@@ -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 +0 -0
index 05ed3d3..bbec02e 100644

arch/x86/entry/calling.h line 6

Quoted 6 lines
arch/x86/entry/calling.h +2 -0
@@ -1,4 +1,6 @@
 #include <linux/jump_label.h>
+#include <asm/undwarf.h>
+
 
 /*
 

Just to make sure I understand this, if we unwind from...

Quoted 8 lines

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 115

Quoted 3 lines
arch/x86/entry/calling.h +0 -0
@@ -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)

...here..., will objtool think that rdx and rsi (etc) still live in their respective regs, or will it find them in the on-stack data given by CFI_REGS? If the former, how does undwarf deal with the corresponding pops?

It will find them in their respective registers, which is fine because they haven't been clobbered yet.

Quoted 3 lines

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 117

Quoted 3 lines
arch/x86/entry/calling.h +1 -0
        movq %rdi, 14*8+\offset(%rsp)
+       CFI_REGS offset=\offset extra=0

And here it will find them on the stack.

Quoted 8 lines

arch/x86/entry/calling.h ENTRY(ret_from_fork) · line 427

Quoted 5 lines
arch/x86/entry/calling.h +1 -0
@@ -414,6 +424,7 @@ ENTRY(ret_from_fork)
 2:
        movq    %rsp, %rdi
        call    syscall_return_slowpath /* returns with IRQs disabled */
+       CFI_REGS

I'm confused. syscall_return_slowpath didn't change anything relevant to unwinding, right? What's CFI_REGS here for?

Yes, you're right, this CFI_REGS should be right at the '2' label.

Signature · 1 line
Josh
#20 lore raw

Re: [RFC PATCH 06/10] x86/entry: add CFI hint undwarf annotations

Quoted 13 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski

On Wed, May 31, 2017 at 10:44 PM, Josh Poimboeuf <[email protected]> wrote:
Just to make sure I understand this, if we unwind from...

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 115

Quoted 3 lines
arch/x86/entry/calling.h +0 -0
@@ -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)

...here..., will objtool think that rdx and rsi (etc) still live in their respective regs, or will it find them in the on-stack data given by CFI_REGS? If the former, how does undwarf deal with the corresponding pops?

Quoted 2 lines

It will find them in their respective registers, which is fine because they haven't been clobbered yet.

Sorry, I hit send too soon. Which pops are you referring to?

Signature · 1 line
Josh
maintainer
#21 lore raw

Re: [RFC PATCH 06/10] x86/entry: add CFI hint undwarf annotations

Quoted 14 lines · Josh Poimboeuf
Quoted 13 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski

On Wed, May 31, 2017 at 10:44 PM, Josh Poimboeuf <[email protected]> wrote:
Just to make sure I understand this, if we unwind from...

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 115

Quoted 3 lines
arch/x86/entry/calling.h +0 -0
@@ -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)

...here..., will objtool think that rdx and rsi (etc) still live in their respective regs, or will it find them in the on-stack data given by CFI_REGS? If the former, how does undwarf deal with the corresponding pops?

Quoted 5 lines
Quoted 2 lines

It will find them in their respective registers, which is fine because they haven't been clobbered yet.

Sorry, I hit send too soon. Which pops are you referring to?

If we do push, push, push, CFI_REGS, and then, later, we pop all those saved regs, how does undwarf figure out that those pops are moving a saved reg from the stack back to a register? Is objtool just that smart, or did I fail to notice an annotation somewhere, or does it not matter?

#22 lore raw

Re: [RFC PATCH 06/10] x86/entry: add CFI hint undwarf annotations

Quoted 18 lines · Andy Lutomirski
Quoted 17 lines · Josh Poimboeuf
Quoted 15 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski

On Wed, May 31, 2017 at 10:44 PM, Josh Poimboeuf <[email protected]> wrote:
Just to make sure I understand this, if we unwind from...

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 115

Quoted 3 lines
arch/x86/entry/calling.h +0 -0
@@ -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)

...here..., will objtool think that rdx and rsi (etc) still live in their respective regs, or will it find them in the on-stack data given by CFI_REGS? If the former, how does undwarf deal with the corresponding pops?

It will find them in their respective registers, which is fine because they haven't been clobbered yet.

Quoted 8 lines
Quoted 2 lines

Sorry, I hit send too soon. Which pops are you referring to?

If we do push, push, push, CFI_REGS, and then, later, we pop all those saved regs, how does undwarf figure out that those pops are moving a saved reg from the stack back to a register? Is objtool just that smart, or did I fail to notice an annotation somewhere, or does it not matter?

RESTORE_EXTRA_REGS has an annotation that attempts to do that, though CFI_REGS ignores the 'extra' arg so there's a bug there. It should resolve to a CFI_IRET_REGS annotation with an offset because the unwinder doesn't care about C regs.

I'll fix that and make the save/restore annotations more symmetrical.

Signature · 1 line
Josh
#23 lore raw

RFC PATCH 7/10 x86/asm: add CFI hint annotations to sync_core()

In reply to Josh Poimboeuf

This enables the 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
index 3cada99..9b90129 100644
@@ -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>
@@ -676,6 +677,7 @@ static inline void sync_core(void)
 	unsigned int tmp;
 
 	asm volatile (
+		CFI_SAVE
 		"mov %%ss, %0\n\t"
 		"pushq %q0\n\t"
 		"pushq %%rsp\n\t"
@@ -685,6 +687,7 @@ static inline void sync_core(void)
 		"pushq %q0\n\t"
 		"pushq $1f\n\t"
 		"iretq\n\t"
+		CFI_RESTORE
 		"1:"
 		: "=&r" (tmp), "+r" (__sp) : : "cc", "memory");
 #endif
Signature · 1 line
2.7.4
#24 lore raw

RFC PATCH 8/10 extable: rename 'sortextable' script to 'sorttable'

In reply to Josh Poimboeuf

Soon it will be used to sort the undwarf table as well.

---
 Documentation/dontdiff                 | 2 +-
 scripts/.gitignore                     | 2 +-
 scripts/Makefile                       | 4 ++--
 scripts/link-vmlinux.sh                | 2 +-
 scripts/{sortextable.c => sorttable.c} | 8 ++++----
 scripts/{sortextable.h => sorttable.h} | 2 +-
 6 files changed, 10 insertions(+), 10 deletions(-)
 rename scripts/{sortextable.c => sorttable.c} (98%)
 rename scripts/{sortextable.h => sorttable.h} (99%)
Documentation/dontdiff +1 -1
index 77b9222..b270d98 100644
@@ -217,7 +217,7 @@ series
 setup
 setup.bin
 setup.elf
-sortextable
+sorttable
 sImage
 sm_tbl*
 split-include
scripts/.gitignore +1 -1
index e063daa..14b108e 100644
@@ -9,7 +9,7 @@ ihex2fw
 recordmcount
 docproc
 check-lc_ctype
-sortextable
+sorttable
 asn1_compiler
 extract-cert
 sign-file
scripts/Makefile +2 -2
index 1d80897..a7b700f 100644
@@ -15,13 +15,13 @@ hostprogs-$(CONFIG_KALLSYMS)     += kallsyms
 hostprogs-$(CONFIG_LOGO)         += pnmtologo
 hostprogs-$(CONFIG_VT)           += conmakehash
 hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
-hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
+hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sorttable
 hostprogs-$(CONFIG_ASN1)	 += asn1_compiler
 hostprogs-$(CONFIG_MODULE_SIG)	 += sign-file
 hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
 hostprogs-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
 
-HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
+HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
 HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
 HOSTLOADLIBES_sign-file = -lcrypto
 HOSTLOADLIBES_extract-cert = -lcrypto
scripts/link-vmlinux.sh +1 -1
index c802913..18dd369 100755
@@ -154,7 +154,7 @@ mksysmap()
 
 sortextable()
 {
-	${objtree}/scripts/sortextable ${1}
+	${objtree}/scripts/sorttable ${1}
 }
 
 # Delete output files in case of error
scripts/sorttable.c renamed from scripts/sortextable.c +4 -4
similarity index 98%
rename from scripts/sortextable.c
rename to scripts/sorttable.c
index 365a907..17324dd 100644
@@ -1,5 +1,5 @@
 /*
- * sortextable.c: Sort the kernel's exception table
+ * sorttable.c: Sort the kernel's exception table
  *
  * Copyright 2011 - 2012 Cavium, Inc.
  *
@@ -193,9 +193,9 @@ static inline unsigned int get_secindex(unsigned int shndx,
 }
 
 /* 32 bit and 64 bit are very similar */
-#include "sortextable.h"
+#include "sorttable.h"
 #define SORTEXTABLE_64
-#include "sortextable.h"
+#include "sorttable.h"
 
 static int compare_relative_table(const void *a, const void *b)
 {
@@ -367,7 +367,7 @@ main(int argc, char *argv[])
 	int i;
 
 	if (argc < 2) {
-		fprintf(stderr, "usage: sortextable vmlinux...\n");
+		fprintf(stderr, "usage: sorttable vmlinux...\n");
 		return 0;
 	}
 
scripts/sorttable.h renamed from scripts/sortextable.h +1 -1
similarity index 99%
rename from scripts/sortextable.h
rename to scripts/sorttable.h
index ba87004..0de9488 100644
@@ -1,5 +1,5 @@
 /*
- * sortextable.h
+ * sorttable.h
  *
  * Copyright 2011 - 2012 Cavium, Inc.
  *
Signature · 1 line
2.7.4
#25 lore raw

RFC PATCH 9/10 extable: add undwarf table sorting ability to sorttable script

In reply to Josh Poimboeuf

The undwarf table needs to be sorted at vmlinux link time, just like the extable. Extend sorttable's functionality to do so.

---
 init/Kconfig            |   4 ++
 scripts/Makefile        |   2 +-
 scripts/link-vmlinux.sh |   7 +-
 scripts/sorttable.c     | 178 +++++++++++++++++++++++++-----------------------
 scripts/sorttable.h     |  71 ++++++++++---------
 5 files changed, 142 insertions(+), 120 deletions(-)
init/Kconfig +4 -0
index 1d3475f..4c096f0 100644
@@ -25,6 +25,10 @@ config IRQ_WORK
 
 config BUILDTIME_EXTABLE_SORT
 	bool
+	select SORTTABLE
+
+config SORTTABLE
+	bool
 
 config THREAD_INFO_IN_TASK
 	bool
scripts/Makefile +1 -1
index a7b700f..99c05de 100644
@@ -15,7 +15,7 @@ hostprogs-$(CONFIG_KALLSYMS)     += kallsyms
 hostprogs-$(CONFIG_LOGO)         += pnmtologo
 hostprogs-$(CONFIG_VT)           += conmakehash
 hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
-hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sorttable
+hostprogs-$(CONFIG_SORTTABLE)    += sorttable
 hostprogs-$(CONFIG_ASN1)	 += asn1_compiler
 hostprogs-$(CONFIG_MODULE_SIG)	 += sign-file
 hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
scripts/link-vmlinux.sh +6 -1
index 18dd369..f4eb9dc 100755
@@ -154,7 +154,12 @@ mksysmap()
 
 sortextable()
 {
-	${objtree}/scripts/sorttable ${1}
+	${objtree}/scripts/sorttable ${1} extable
+}
+
+sortundwarf()
+{
+	${objtree}/scripts/sorttable ${1} undwarf
 }
 
 # Delete output files in case of error
scripts/sorttable.c +94 -84

332 lines not on this page · raw message

scripts/sorttable.h +37 -34

163 lines not on this page · raw message

Signature · 1 line
2.7.4
#26 lore raw

RFC PATCH 10/10 x86/unwind: add undwarf unwinder

In reply to Josh Poimboeuf

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

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

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

---
 arch/um/include/asm/unwind.h      |   7 +
 arch/x86/Kconfig                  |   1 +
 arch/x86/Kconfig.debug            |  26 +++
 arch/x86/include/asm/module.h     |   8 +
 arch/x86/include/asm/unwind.h     |  64 +++---
 arch/x86/kernel/Makefile          |   8 +-
 arch/x86/kernel/module.c          |   9 +-
 arch/x86/kernel/unwind_frame.c    |  39 ++--
 arch/x86/kernel/unwind_guess.c    |   5 +
 arch/x86/kernel/unwind_undwarf.c  | 402 ++++++++++++++++++++++++++++++++++++++
 include/asm-generic/vmlinux.lds.h |  14 ++
 lib/Kconfig.debug                 |   3 +
 scripts/Makefile.build            |   3 +-
 scripts/link-vmlinux.sh           |   5 +
 14 files changed, 534 insertions(+), 60 deletions(-)
 create mode 100644 arch/um/include/asm/unwind.h
 create mode 100644 arch/x86/kernel/unwind_undwarf.c
arch/um/include/asm/unwind.h new file +7 -0
new file mode 100644
index 0000000..4e3f719
@@ -0,0 +1,7 @@
+#ifndef _ASM_UML_UNWIND_H
+#define _ASM_UML_UNWIND_H
+
+static inline void
+unwind_module_init(struct module *mod, void *undwarf, size_t size) {}
+
+#endif /* _ASM_UML_UNWIND_H */
arch/x86/Kconfig +1 -0
index 4ccfacc..869fbc5 100644
@@ -151,6 +151,7 @@ config X86
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP
 	select HAVE_MIXED_BREAKPOINTS_REGS
+	select HAVE_MOD_ARCH_SPECIFIC
 	select HAVE_NMI
 	select HAVE_OPROFILE
 	select HAVE_OPTPROBES
arch/x86/Kconfig.debug +26 -0
index fcb7604..6717463 100644
@@ -357,4 +357,30 @@ config PUNIT_ATOM_DEBUG
 	  The current power state can be read from
 	  /sys/kernel/debug/punit_atom/dev_power_state
 
+config UNDWARF_UNWINDER
+	bool "undwarf unwinder"
+	depends on X86_64
+	select STACK_VALIDATION
+	select SORTTABLE
+	---help---
+	  This option enables the "undwarf" unwinder for unwinding kernel stack
+	  traces.  It uses a custom data format which is a simplified version
+	  of the DWARF Call Frame Information standard.
+
+	  This unwinder is more accurate across interrupt entry frames than the
+	  frame pointer unwinder.  This also can enable a small performance
+	  improvement across the entire kernel if CONFIG_FRAME_POINTER is
+	  disabled.
+
+	  Enabling this option will increase the kernel's runtime memory usage
+	  by roughly 3-5MB, depending on the kernel config.
+
+config FRAME_POINTER_UNWINDER
+	def_bool y
+	depends on !UNDWARF_UNWINDER && FRAME_POINTER
+
+config GUESS_UNWINDER
+	def_bool y
+	depends on !UNDWARF_UNWINDER && !FRAME_POINTER
+
 endmenu
arch/x86/include/asm/module.h +8 -0
index e3b7819..454eeea 100644
@@ -2,6 +2,14 @@
 #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;
+	struct undwarf *undwarf;
+#endif
+};
 
 #ifdef CONFIG_X86_64
 /* X86_64 does not define MODULE_PROC_FAMILY */
arch/x86/include/asm/unwind.h +34 -30

106 lines not on this page · raw message

arch/x86/kernel/Makefile +3 -5
index 3c7c419..4865889 100644
@@ -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 +8 -1
index f67bd32..6756070 100644
@@ -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;
 	char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 
 	for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
@@ -225,6 +226,8 @@ int module_finalize(const Elf_Ehdr *hdr,
 			locks = s;
 		if (!strcmp(".parainstructions", secstrings + s->sh_name))
 			para = s;
+		if (!strcmp(".undwarf", secstrings + s->sh_name))
+			undwarf = s;
 	}
 
 	if (alt) {
@@ -248,6 +251,10 @@ int module_finalize(const Elf_Ehdr *hdr,
 	/* make jump label nops */
 	jump_label_apply_nops(me);
 
+	if (undwarf)
+		unwind_module_init(me, (void *)undwarf->sh_addr,
+				   undwarf->sh_size);
+
 	return 0;
 }
 
arch/x86/kernel/unwind_frame.c +16 -23
index b9389d7..7574ef5 100644
@@ -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
index 039f367..4f0e17b 100644
@@ -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 +402 -0

402 lines not on this page · raw message

include/asm-generic/vmlinux.lds.h +14 -0
index 314a0b9..e350116 100644
@@ -324,6 +324,8 @@
 									\
 	TRACEDATA							\
 									\
+	UNDWARF_TABLE							\
+									\
 	/* Kernel symbol table: Normal symbols */			\
 	__ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {		\
 		VMLINUX_SYMBOL(__start___ksymtab) = .;			\
@@ -669,6 +671,18 @@
 #define BUG_TABLE
 #endif
 
+#ifdef CONFIG_UNDWARF_UNWINDER
+#define UNDWARF_TABLE							\
+	. = ALIGN(16);							\
+	.undwarf : AT(ADDR(.undwarf) - LOAD_OFFSET) {			\
+		VMLINUX_SYMBOL(__undwarf_start) = .;			\
+		KEEP(*(.undwarf))					\
+		VMLINUX_SYMBOL(__undwarf_end) = .;			\
+	}
+#else
+#define UNDWARF_TABLE
+#endif
+
 #ifdef CONFIG_PM_TRACE
 #define TRACEDATA							\
 	. = ALIGN(4);							\
lib/Kconfig.debug +3 -0
index e4587eb..31f73d9 100644
@@ -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 +2 -1
index 733e044..b43dddf 100644
@@ -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
scripts/link-vmlinux.sh +5 -0
index f4eb9dc..286ea8d 100755
@@ -296,6 +296,11 @@ if [ -n "${CONFIG_BUILDTIME_EXTABLE_SORT}" ]; then
 	sortextable vmlinux
 fi
 
+if [ -n "${CONFIG_UNDWARF_UNWINDER}" ]; then
+	info SORTUD vmlinux
+	sortundwarf vmlinux
+fi
+
 info SYSMAP System.map
 mksysmap vmlinux System.map
 
Signature · 1 line
2.7.4
maintainer
#27 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 12 lines · Josh Poimboeuf
arch/x86/kernel/unwind_undwarf.c new file +12 -0
+static struct undwarf *__undwarf_lookup(struct undwarf *undwarf,
+					unsigned int num, unsigned long ip)
+{
+	struct undwarf *first = undwarf;
+	struct undwarf *last = undwarf + num - 1;
+	struct undwarf *mid;
+	unsigned long u_ip;
+
+	while (first <= last) {
+		mid = first + ((last - first) / 2);
+		u_ip = undwarf_ip(mid);
+

arch/x86/kernel/unwind_undwarf.c line 63

Quoted 10 lines
arch/x86/kernel/unwind_undwarf.c new file +10 -0
+		if (ip >= u_ip) {
+			if (ip < u_ip + mid->len)
+				return mid;
+			first = mid + 1;
+		} else
+			last = mid - 1;
+	}
+
+	return NULL;
+}

That's a bog standard binary search thing, don't we have a helper for that someplace?

Quoted 11 lines
arch/x86/kernel/unwind_undwarf.c new file +11 -0
+static struct undwarf *undwarf_lookup(unsigned long ip)
+{
+	struct undwarf *undwarf;
+	struct module *mod;
+
+	/* Look in vmlinux undwarf section: */
+	undwarf = __undwarf_lookup(__undwarf_start, __undwarf_end - __undwarf_start, ip);
+	if (undwarf)
+		return undwarf;
+
+	/* Look in module undwarf sections: */

arch/x86/kernel/unwind_undwarf.c line 85

Quoted 10 lines
arch/x86/kernel/unwind_undwarf.c new file +10 -0
+	preempt_disable();
+	mod = __module_address(ip);
+	if (!mod || !mod->arch.undwarf)
+		goto module_out;
+	undwarf = __undwarf_lookup(mod->arch.undwarf, mod->arch.num_undwarves, ip);
+
+module_out:
+	preempt_enable();
+	return undwarf;
+}

A few points here:

- that first lookup is entirely pointless if !core_kernel_text(ip)

- that preempt_{dis,en}able() muck is 'pointless', for while it shuts up the warnings from __modules_address(), nothing preserves the struct undwarf you get a pointer to after the preempt_enable().

- what about 'interesting' things like, ftrace_trampoline, kprobe insn slots and bpf text?

Quoted 6 lines
arch/x86/kernel/unwind_undwarf.c new file +6 -0
+static bool stack_access_ok(struct unwind_state *state, unsigned long addr,
+			    size_t len)
+{
+	struct stack_info *info = &state->stack_info;
+
+	/*

arch/x86/kernel/unwind_undwarf.c line 102

Quoted 10 lines
arch/x86/kernel/unwind_undwarf.c new file +10 -0
+	 * If the next bp isn't on the current stack, switch to the next one.
+	 *
+	 * We may have to traverse multiple stacks to deal with the possibility
+	 * that info->next_sp could point to an empty stack and the next bp
+	 * could be on a subsequent stack.
+	 */
+	while (!on_stack(info, (void *)addr, len)) {
+		if (get_stack_info(info->next_sp, state->task, info,
+				   &state->stack_mask))
+			return false;
	}
Quoted 3 lines
arch/x86/kernel/unwind_undwarf.c new file +3 -0
+
+	return true;
+}
#28 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 19 lines · Peter Zijlstra
Quoted 17 lines · Josh Poimboeuf
arch/x86/kernel/unwind_undwarf.c new file +17 -0
+static struct undwarf *__undwarf_lookup(struct undwarf *undwarf,
+					unsigned int num, unsigned long ip)
+{
+	struct undwarf *first = undwarf;
+	struct undwarf *last = undwarf + num - 1;
+	struct undwarf *mid;
+	unsigned long u_ip;
+
+	while (first <= last) {
+		mid = first + ((last - first) / 2);
+		u_ip = undwarf_ip(mid);
+
+		if (ip >= u_ip) {
+			if (ip < u_ip + mid->len)
+				return mid;
+			first = mid + 1;
+		} else
Quoted 8 lines

arch/x86/kernel/unwind_undwarf.c line 63

Quoted 5 lines
arch/x86/kernel/unwind_undwarf.c new file +5 -0
+			last = mid - 1;
+	}
+
+	return NULL;
+}

That's a bog standard binary search thing, don't we have a helper for that someplace?

I wasn't able to find one...

Quoted 24 lines
Quoted 11 lines
arch/x86/kernel/unwind_undwarf.c new file +11 -0
+static struct undwarf *undwarf_lookup(unsigned long ip)
+{
+	struct undwarf *undwarf;
+	struct module *mod;
+
+	/* Look in vmlinux undwarf section: */
+	undwarf = __undwarf_lookup(__undwarf_start, __undwarf_end - __undwarf_start, ip);
+	if (undwarf)
+		return undwarf;
+
+	/* Look in module undwarf sections: */

arch/x86/kernel/unwind_undwarf.c line 85

Quoted 10 lines
arch/x86/kernel/unwind_undwarf.c new file +10 -0
+	preempt_disable();
+	mod = __module_address(ip);
+	if (!mod || !mod->arch.undwarf)
+		goto module_out;
+	undwarf = __undwarf_lookup(mod->arch.undwarf, mod->arch.num_undwarves, ip);
+
+module_out:
+	preempt_enable();
+	return undwarf;
+}

A few points here:

Quoted 1 line

- that first lookup is entirely pointless if !core_kernel_text(ip)

True.

Quoted 3 lines

- that preempt_{dis,en}able() muck is 'pointless', for while it shuts up the warnings from __modules_address(), nothing preserves the struct undwarf you get a pointer to after the preempt_enable().

Oops!

Quoted 2 lines

- what about 'interesting' things like, ftrace_trampoline, kprobe insn slots and bpf text?

I think support for generated code can come later. My current plan is to have some kind of registration interface for associating debuginfo with generated code. Maybe as part of the generated code management thing we talked about before:

https://lkml.kernel.org/r/[email protected]

Signature · 1 line
Josh
#29 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 29 lines · Josh Poimboeuf
Quoted 19 lines · Peter Zijlstra
Quoted 17 lines · Josh Poimboeuf
arch/x86/kernel/unwind_undwarf.c new file +17 -0
+static struct undwarf *__undwarf_lookup(struct undwarf *undwarf,
+					unsigned int num, unsigned long ip)
+{
+	struct undwarf *first = undwarf;
+	struct undwarf *last = undwarf + num - 1;
+	struct undwarf *mid;
+	unsigned long u_ip;
+
+	while (first <= last) {
+		mid = first + ((last - first) / 2);
+		u_ip = undwarf_ip(mid);
+
+		if (ip >= u_ip) {
+			if (ip < u_ip + mid->len)
+				return mid;
+			first = mid + 1;
+		} else
Quoted 8 lines

arch/x86/kernel/unwind_undwarf.c line 63

Quoted 5 lines
arch/x86/kernel/unwind_undwarf.c new file +5 -0
+			last = mid - 1;
+	}
+
+	return NULL;
+}

That's a bog standard binary search thing, don't we have a helper for that someplace?

Quoted 1 line

I wasn't able to find one...

There is bsearch, but that doesn't support searching for a value in between of 2 keys. I.e. what we typically have is these keys:
some_function1 at 0x1000
some_function2 at 0x2000
and we look for IP which can be e.g. 0x1010. The bsearch's cmp function currently has no option to say, yes, the last one you asked me was the right one, this one is after it already.

thanks,

Signature · 2 lines
js
suse labs
#30 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 32 lines · Jiri Slaby
Quoted 29 lines · Josh Poimboeuf
Quoted 19 lines · Peter Zijlstra
Quoted 17 lines · Josh Poimboeuf
arch/x86/kernel/unwind_undwarf.c new file +17 -0
+static struct undwarf *__undwarf_lookup(struct undwarf *undwarf,
+					unsigned int num, unsigned long ip)
+{
+	struct undwarf *first = undwarf;
+	struct undwarf *last = undwarf + num - 1;
+	struct undwarf *mid;
+	unsigned long u_ip;
+
+	while (first <= last) {
+		mid = first + ((last - first) / 2);
+		u_ip = undwarf_ip(mid);
+
+		if (ip >= u_ip) {
+			if (ip < u_ip + mid->len)
+				return mid;
+			first = mid + 1;
+		} else
Quoted 8 lines

arch/x86/kernel/unwind_undwarf.c line 63

Quoted 5 lines
arch/x86/kernel/unwind_undwarf.c new file +5 -0
+			last = mid - 1;
+	}
+
+	return NULL;
+}

That's a bog standard binary search thing, don't we have a helper for that someplace?

Quoted 1 line

I wasn't able to find one...

Quoted 7 lines

There is bsearch, but that doesn't support searching for a value in between of 2 keys. I.e. what we typically have is these keys:
some_function1 at 0x1000
some_function2 at 0x2000
and we look for IP which can be e.g. 0x1010. The bsearch's cmp function currently has no option to say, yes, the last one you asked me was the right one, this one is after it already.

Actually, I think bsearch would work with the latest version of the data structure. It now has a len field, so we can do a self-contained compare.

Signature · 1 line
Josh
maintainer
#31 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

In reply to Jiri Slaby
Quoted 1 line · Jiri Slaby

There is bsearch,

Shiny, should we move that into lib/sort.h and maybe get more people to use it? search_extable() seems like something that could use it.

And __bug_table is something that seems to want all things sort applied.

maintainer
#32 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

In reply to Josh Poimboeuf
Quoted 29 lines · Josh Poimboeuf
Quoted 19 lines · Peter Zijlstra
Quoted 17 lines · Josh Poimboeuf
arch/x86/kernel/unwind_undwarf.c new file +17 -0
+static struct undwarf *__undwarf_lookup(struct undwarf *undwarf,
+					unsigned int num, unsigned long ip)
+{
+	struct undwarf *first = undwarf;
+	struct undwarf *last = undwarf + num - 1;
+	struct undwarf *mid;
+	unsigned long u_ip;
+
+	while (first <= last) {
+		mid = first + ((last - first) / 2);
+		u_ip = undwarf_ip(mid);
+
+		if (ip >= u_ip) {
+			if (ip < u_ip + mid->len)
+				return mid;
+			first = mid + 1;
+		} else
Quoted 8 lines

arch/x86/kernel/unwind_undwarf.c line 63

Quoted 5 lines
arch/x86/kernel/unwind_undwarf.c new file +5 -0
+			last = mid - 1;
+	}
+
+	return NULL;
+}

That's a bog standard binary search thing, don't we have a helper for that someplace?

Quoted 1 line

I wasn't able to find one...

Yeah, I looked too and couldn't find it either. A well.

maintainer
#33 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

In reply to Josh Poimboeuf
Quoted 32 lines · Josh Poimboeuf
(quoted diff) +31 -0
+static struct undwarf *undwarf_lookup(unsigned long ip)
+{
+	struct undwarf *undwarf;
+	struct module *mod;
+
+	/* Look in vmlinux undwarf section: */
+	undwarf = __undwarf_lookup(__undwarf_start, __undwarf_end - __undwarf_start, ip);
+	if (undwarf)
+		return undwarf;
+
+	/* Look in module undwarf sections: */
+	preempt_disable();
+	mod = __module_address(ip);
+	if (!mod || !mod->arch.undwarf)
+		goto module_out;
+	undwarf = __undwarf_lookup(mod->arch.undwarf, mod->arch.num_undwarves, ip);
+
+module_out:
+	preempt_enable();
+	return undwarf;
+}
 
+bool unwind_next_frame(struct unwind_state *state)
+{
+	struct undwarf *undwarf;
+	unsigned long cfa;
+	bool indirect = false;
+	enum stack_type prev_type = state->stack_info.type;
+	unsigned long ip_p, prev_sp = state->sp;
+
+	if (unwind_done(state))
+		return false;

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+	/* Have we reached the end? */
+	if (state->regs && user_mode(state->regs))
+		goto done;
+
+	/* Look up the instruction address in the .undwarf table: */
+	undwarf = undwarf_lookup(state->ip);
+	if (!undwarf || undwarf->cfa_reg == UNDWARF_REG_UNDEFINED)
+		goto done;
+
	....

Review

Quoted 6 lines
(quoted diff) +6 -0
+}
+EXPORT_SYMBOL_GPL(unwind_next_frame);
+
+void __unwind_start(struct unwind_state *state, struct task_struct *task,
+		    struct pt_regs *regs, unsigned long *first_frame)
+{
	...

Review

Quoted 5 lines
(quoted diff) +5 -0
+	while (!unwind_done(state) &&
+	       (!on_stack(&state->stack_info, first_frame, sizeof(long)) ||
+			state->sp <= (unsigned long)first_frame))
+		unwind_next_frame(state);
+}

So we do that lookup for every single frame. That's going to hurt.

Would it make sense to cache the last 'module' in an attempt to at least avoid that lookup again? Something like so:

---

arch/x86/include/asm/unwind.h +1 -0
@@ -15,6 +15,7 @@ struct unwind_state {
 #if defined(CONFIG_UNDWARF_UNWINDER)
 	unsigned long sp, bp, ip;
 	struct pt_regs *regs;
+	struct module *mod;
 #elif defined(CONFIG_FRAME_POINTER)
 	bool got_irq;
 	unsigned long *bp, *orig_sp, ip;
arch/x86/kernel/unwind_undwarf.c +32 -13

65 lines not on this page · raw message

#34 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 4 lines · Peter Zijlstra

So we do that lookup for every single frame. That's going to hurt.

Would it make sense to cache the last 'module' in an attempt to at least avoid that lookup again? Something like so:

The only thing with caching the module is, what if the module goes away?

Based on your previous comment I was thinking I would disable preemption for the entire unwind_next_frame() step, but not *between* steps. I suppose we could require the unwind caller to disable preemption but I'd like to avoid that if possible.

Signature · 1 line
Josh
maintainer
#35 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 7 lines · Josh Poimboeuf
Quoted 4 lines · Peter Zijlstra

So we do that lookup for every single frame. That's going to hurt.

Would it make sense to cache the last 'module' in an attempt to at least avoid that lookup again? Something like so:

The only thing with caching the module is, what if the module goes away?

Yeah.. *boom* ;-) We could of course play games with module_get() and module_put(), but meh.

Quoted 4 lines

Based on your previous comment I was thinking I would disable preemption for the entire unwind_next_frame() step, but not *between* steps. I suppose we could require the unwind caller to disable preemption but I'd like to avoid that if possible.

Right, keeping it disabled across a frame should be ok I suppose.

#36 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 6 lines · Peter Zijlstra
Quoted 4 lines

Based on your previous comment I was thinking I would disable preemption for the entire unwind_next_frame() step, but not *between* steps. I suppose we could require the unwind caller to disable preemption but I'd like to avoid that if possible.

Right, keeping it disabled across a frame should be ok I suppose.

But then we'd either have to require the unwind user to explicitly disable preemption, or we'd need to add a new unwind_end() interface which the caller would be required to use when they're done unwinding. Neither is ideal.

Signature · 1 line
Josh
#37 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

In reply to Josh Poimboeuf
Quoted 3 lines · Josh Poimboeuf
arch/x86/kernel/unwind_undwarf.c new file +0 -0
@@ -0,0 +1,402 @@

...

Quoted 15 lines
arch/x86/kernel/unwind_undwarf.c new file +15 -0
+void __unwind_start(struct unwind_state *state, struct task_struct *task,
+		    struct pt_regs *regs, unsigned long *first_frame)
+{
+	memset(state, 0, sizeof(*state));
+	state->task = task;
+
+	if (regs) {
+		if (user_mode(regs)) {
+			state->stack_info.type = STACK_TYPE_UNKNOWN;
+			return;
+		}
+
+		state->ip = regs->ip;
+		state->sp = kernel_stack_pointer(regs);
+		state->bp = regs->bp;

arch/x86/kernel/unwind_undwarf.c line 337

Quoted 10 lines
arch/x86/kernel/unwind_undwarf.c new file +10 -0
+		state->regs = regs;
+
+	} else if (task == current) {
+		register void *__sp asm(_ASM_SP);
+
+		asm volatile("lea (%%rip), %0\n\t"
+			     "mov %%rsp, %1\n\t"
+			     "mov %%rbp, %2\n\t"
+			     : "=r" (state->ip), "=r" (state->sp),
+			       "=r" (state->bp), "+r" (__sp));

Maybe I don't understand this completely, but what is __sp used for here?

arch/x86/kernel/unwind_undwarf.c line 341

Quoted 3 lines
arch/x86/kernel/unwind_undwarf.c new file +3 -0
+		state->regs = NULL;
+
+	} else {

In DWARF unwinder, we also used to do here:

+#ifdef CONFIG_SMP
+ } else if (task->on_cpu) {
+ return;
+#endif

        } else {

arch/x86/kernel/unwind_undwarf.c line 342

Quoted 1 line
arch/x86/kernel/unwind_undwarf.c new file +1 -0
+		struct inactive_task_frame *frame = (void *)task->thread.sp;

Since there is no inactive_task_frame for tasks currently running (on other CPUs). At least this always held in the past.

Though, the test is indeed racy.

arch/x86/kernel/unwind_undwarf.c line 348

Quoted 5 lines
arch/x86/kernel/unwind_undwarf.c new file +5 -0
+		state->ip = frame->ret_addr;
+		state->sp = task->thread.sp;
+		state->bp = frame->bp;
+		state->regs = NULL;
+	}

thanks,

Signature · 2 lines
js
suse labs
#38 lore raw

Re: [RFC PATCH 10/10] x86/unwind: add undwarf unwinder

Quoted 24 lines · Jiri Slaby
Quoted 3 lines · Josh Poimboeuf
arch/x86/kernel/unwind_undwarf.c new file +0 -0
@@ -0,0 +1,402 @@

...

Quoted 19 lines
arch/x86/kernel/unwind_undwarf.c new file +19 -0
+void __unwind_start(struct unwind_state *state, struct task_struct *task,
+		    struct pt_regs *regs, unsigned long *first_frame)
+{
+	memset(state, 0, sizeof(*state));
+	state->task = task;
+
+	if (regs) {
+		if (user_mode(regs)) {
+			state->stack_info.type = STACK_TYPE_UNKNOWN;
+			return;
+		}
+
+		state->ip = regs->ip;
+		state->sp = kernel_stack_pointer(regs);
+		state->bp = regs->bp;
+		state->regs = regs;
+
+	} else if (task == current) {
+		register void *__sp asm(_ASM_SP);
Quoted 8 lines

arch/x86/kernel/unwind_undwarf.c line 337

Quoted 6 lines
arch/x86/kernel/unwind_undwarf.c new file +6 -0
+
+		asm volatile("lea (%%rip), %0\n\t"
+			     "mov %%rsp, %1\n\t"
+			     "mov %%rbp, %2\n\t"
+			     : "=r" (state->ip), "=r" (state->sp),
+			       "=r" (state->bp), "+r" (__sp));

Maybe I don't understand this completely, but what is __sp used for here?

This tells gcc "if this function saves the frame pointer, make sure it's saved before inserting this inline asm."

But on second thought, it shouldn't be needed. Either way it can use the undwarf data to find the previous bp. I'm struggling to remember why I thought this was needed in the first place...

Quoted 12 lines

arch/x86/kernel/unwind_undwarf.c line 341

Quoted 3 lines
arch/x86/kernel/unwind_undwarf.c new file +3 -0
+		state->regs = NULL;
+
+	} else {

In DWARF unwinder, we also used to do here:

(quoted diff) +4 -0
+#ifdef CONFIG_SMP
+       } else if (task->on_cpu) {
+               return;
+#endif
        } else {
Quoted 6 lines

arch/x86/kernel/unwind_undwarf.c line 342

Quoted 1 line
arch/x86/kernel/unwind_undwarf.c new file +1 -0
+		struct inactive_task_frame *frame = (void *)task->thread.sp;

Since there is no inactive_task_frame for tasks currently running (on other CPUs). At least this always held in the past.

Though, the test is indeed racy.

Yeah, it's indeed racy, but it's probably a good idea to add the check anyway. There are other checks to prevent going off the rails, but we should try to detect it early when we can. The frame pointer unwinder could probably use a similar check.

Signature · 1 line
Josh
#39 lore raw
In reply to Josh Poimboeuf
Quoted 2 lines · Josh Poimboeuf

Here's the contents of the undwarf.txt file which explains the 'why' in more detail:

Ok, so the code quality looks pretty convincing to me - the new core 'undwarf' unwinder code is a _lot_ more readable than any of the Dwarf based attempts before.

That we control the debug info generation at build time is icing on the cake to me.

One thing I'd like to see on the list of benefits side of the equation is a size comparison of kernel .text, with frame pointers vs. undwarf, on 64-bit kernels.

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Thanks,

Ingo

#40 lore raw
Quoted 10 lines · Ingo Molnar
Quoted 2 lines · Josh Poimboeuf

Here's the contents of the undwarf.txt file which explains the 'why' in more detail:

Ok, so the code quality looks pretty convincing to me - the new core 'undwarf' unwinder code is a _lot_ more readable than any of the Dwarf based attempts before.

Quoted 5 lines

That we control the debug info generation at build time is icing on the cake to me.

One thing I'd like to see on the list of benefits side of the equation is a size comparison of kernel .text, with frame pointers vs. undwarf, on 64-bit kernels.

Ok, will do a text size comparison. The only difficulty I encountered there is that the 'size' tool considers the .undwarf section to be text for some reason. So the "text" size grew considerably :-)

Quoted 4 lines

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

But I'll try to highlight that a little more.

Signature · 1 line
Josh
maintainer
#41 lore raw
Quoted 5 lines · Josh Poimboeuf
Quoted 4 lines

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Quoted 7 lines

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

But I'll try to highlight that a little more.

That's relative to a DWARF unwinder. It doesn't appear to be possible to get anywhere near a frame-pointer unwinder due to having to do this log(n) lookup for every single frame.

#42 lore raw
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Josh Poimboeuf
Quoted 4 lines

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

Quoted 8 lines
Quoted 4 lines

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

But I'll try to highlight that a little more.

That's relative to a DWARF unwinder. It doesn't appear to be possible to get anywhere near a frame-pointer unwinder due to having to do this log(n) lookup for every single frame.

This is ~ 20 times faster than my DWARF unwinder by a quick measurement (20000 calls to save_stack_trace via single vfs_write).

perf profile, if you care:

__save_stack_trace

|
|--65.89%--unwind_next_frame
|          |
|          |--53.64%--__undwarf_lookup
|          |
|           --5.30%--deref_stack_reg
|                     |
|                      --2.32%--stack_access_ok
|
|--24.17%--__unwind_start
|          |
|          |--21.52%--unwind_next_frame
|          |          |
|          |          |--14.24%--__undwarf_lookup
|          |          |
|          |           --2.98%--deref_stack_reg
|          |                     |
|          |                      --1.32%--stack_access_ok
|          |
|           --1.32%--get_stack_info
|                     |
|                      --0.66%--in_task_stack
|
|--3.31%--unwind_get_return_address
|          __kernel_text_address
|          |
|          |--0.99%--is_ftrace_trampoline
|          |
|          |--0.99%--__is_insn_slot_addr
|          |          |
|          |           --0.66%--__rcu_read_unlock
|          |
|           --0.66%--is_bpf_text_address
|

--1.66%--save_stack_address

Signature · 2 lines
js
suse labs
#43 lore raw
Quoted 13 lines · Jiri Slaby
Quoted 12 lines · Peter Zijlstra
Quoted 11 lines · Josh Poimboeuf
Quoted 4 lines

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

Quoted 8 lines
Quoted 5 lines
Quoted 1 line

But I'll try to highlight that a little more.

That's relative to a DWARF unwinder. It doesn't appear to be possible to get anywhere near a frame-pointer unwinder due to having to do this log(n) lookup for every single frame.

This is ~ 20 times faster than my DWARF unwinder by a quick measurement (20000 calls to save_stack_trace via single vfs_write).

Wow! Thanks for quantifying that. Looks like the lookup is indeed the bottleneck as expected.

Signature · 1 line
Josh
#44 lore raw
Quoted 4 lines · Josh Poimboeuf
Quoted 2 lines

This is ~ 20 times faster than my DWARF unwinder by a quick measurement (20000 calls to save_stack_trace via single vfs_write).

Wow!

BTW the most time spent when unwinding DWARF was in those routines decrypting uleb/sleb128 into u/longs.

thanks,

Signature · 2 lines
js
suse labs
#45 lore raw
In reply to Peter Zijlstra
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Josh Poimboeuf
Quoted 4 lines

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

Quoted 6 lines
Quoted 4 lines

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

But I'll try to highlight that a little more.

That's relative to a DWARF unwinder.

Yes.

Quoted 3 lines

It doesn't appear to be possible to get anywhere near a frame-pointer unwinder due to having to do this log(n) lookup for every single frame.

Hm, is there something faster, yet not substantially bigger? Hash? Trie?

Signature · 1 line
Josh
maintainer
#46 lore raw
Quoted 6 lines · Josh Poimboeuf
Quoted 3 lines

It doesn't appear to be possible to get anywhere near a frame-pointer unwinder due to having to do this log(n) lookup for every single frame.

Hm, is there something faster, yet not substantially bigger? Hash? Trie?

Not sure how to make a Hash work with nearest neighbour searches. And a trie will only give you a constant speedup over the binary search but not an improvement in complexity IIRC.

#47 lore raw

On (06/01/17 15:25), Peter Zijlstra wrote:
[..]

Quoted 6 lines · Josh Poimboeuf
Quoted 5 lines
Quoted 4 lines

It doesn't appear to be possible to get anywhere near a frame-pointer unwinder due to having to do this log(n) lookup for every single frame.

Quoted 6 lines
Quoted 2 lines

Hm, is there something faster, yet not substantially bigger? Hash? Trie?

Not sure how to make a Hash work with nearest neighbour searches. And a trie will only give you a constant speedup over the binary search but not an improvement in complexity IIRC.

by the way, as far as I know, there is *a bit* faster bsearch(). basically there is a way to calculate pivot (middle element) using less instructions.

something like below, perhaps... may be can give some extra performance. (not really tested. but I believe this is close to what gcc does in libstdc++).

======

./scripts/bloat-o-meter lib/bsearch.o.old lib/bsearch.o.new add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-24 (-24)
function old new delta
bsearch 122 98 -24

---
 lib/bsearch.c | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)
lib/bsearch.c +12 -10
index e33c179089db..18b445b010c3 100644
@@ -33,19 +33,21 @@
 void *bsearch(const void *key, const void *base, size_t num, size_t size,
 	      int (*cmp)(const void *key, const void *elt))
 {
-	size_t start = 0, end = num;
+	const char *pivot;
 	int result;
 
-	while (start < end) {
-		size_t mid = start + (end - start) / 2;
+	while (num > 0) {
+		pivot = base + (num >> 1) * size;
+		result = cmp(key, pivot);
 
-		result = cmp(key, base + mid * size);
-		if (result < 0)
-			end = mid;
-		else if (result > 0)
-			start = mid + 1;
-		else
-			return (void *)base + mid * size;
+		if (result == 0)
+			return (void *)pivot;
+
+		if (result > 0) {
+			base = pivot + size;
+			num--;
+		}
+		num >>= 1;
 	}
 
 	return NULL;
Signature · 1 line
2.13.1
maintainer
#48 lore raw
In reply to Josh Poimboeuf
Quoted 17 lines · Josh Poimboeuf
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Josh Poimboeuf
Quoted 4 lines

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

Quoted 6 lines
Quoted 4 lines

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

But I'll try to highlight that a little more.

That's relative to a DWARF unwinder.

Quoted 8 lines

Yes.

Quoted 3 lines

It doesn't appear to be possible to get anywhere near a frame-pointer unwinder due to having to do this log(n) lookup for every single frame.

Hm, is there something faster, yet not substantially bigger? Hash? Trie?

You have, roughly, a set of (key_start, value) pairs where, for any given key, you want to find the (key_start, value) with the largest key_start that doesn't exceed key. Binary search gives you log_2(n) queries, but its locality of reference sucks. Here are two suggestions for improving it:

1. Change the data layout. Instead of having an array of undwarf entries, have two parallel arrays, one with the ip addresses and one with everything else. This has no effect on the amount of space used, but it makes the part used during search more compact.

2. Your key space is fairly small and your table entries should be reasonably uniformly distributed. Let the first IP you have unwind data for be IP0. Make an array mapping (IP - IP0) / B to the index of the unwind entry for that IP for some suitable block size B. Then, to look up an IP, you'd find the indices of the unwind entries for (IP - IP0) / B and (IP - IP0) / B + 1 and binary search between them. With constant B, this gives you O(1) performance instead of O(log(n)). With B = 1, it's very fast, but the table is huge. With B = 64k or so, maybe you'd get a nice tradeoff of speedup vs size. (With modules, you'd presumably first search an rbtree to find which instance of this data structure you're using and then do the lookup.)

3. Use a B-tree. B-trees are simple if you don't need to deal with insertion and deletion. Presumably you'd choose your internal node size so each internal node is exactly 64 or 128 bytes for good cache performance. This is still O(log(n)) and it uses more comparisons than binary search, but you touch many fewer cache lines.

I expect that, if you do #1 and #2, you'd get excellent performance at very little cost to the complexity of the code.

#49 lore raw
In reply to Josh Poimboeuf
Quoted 14 lines · Josh Poimboeuf
Quoted 13 lines · Ingo Molnar
Quoted 2 lines · Josh Poimboeuf

Here's the contents of the undwarf.txt file which explains the 'why' in more detail:

Ok, so the code quality looks pretty convincing to me - the new core 'undwarf' unwinder code is a _lot_ more readable than any of the Dwarf based attempts before.

That we control the debug info generation at build time is icing on the cake to me.

Quoted 6 lines
Quoted 2 lines

One thing I'd like to see on the list of benefits side of the equation is a size comparison of kernel .text, with frame pointers vs. undwarf, on 64-bit kernels.

Ok, will do a text size comparison. The only difficulty I encountered there is that the 'size' tool considers the .undwarf section to be text for some reason. So the "text" size grew considerably :-)

One trick I sometimes use is to only size some of the key builtin.o files.

Quoted 5 lines
Quoted 4 lines

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Quoted 7 lines

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

But I'll try to highlight that a little more.

That's not what I meant! The speedup comes from (hopefully) being able to disable CONFIG_FRAME_POINTER, which:

- creates simpler/faster function prologues and epilogues - no managing of RBP needed

- gives one more generic purpose register to work from. This matters less on 64-bit kernels but it's a small effect.

I've seen numbers of 1-2% of instruction count reduction in common kernel workloads, which would be pretty significant on well cached workloads.

Thanks,

Ingo

#50 lore raw
Quoted 2 lines · Ingo Molnar

That's not what I meant! The speedup comes from (hopefully) being able to disable CONFIG_FRAME_POINTER, which:

BTW when you are mentioning this -- my measurements were with FP disabled.

Is there any reasonably simple-to-use benchmark I could run with FP=y and FP=n quickly?

thanks,

Signature · 2 lines
js
suse labs
#51 lore raw
Quoted 8 lines · Jiri Slaby
Quoted 2 lines · Ingo Molnar

That's not what I meant! The speedup comes from (hopefully) being able to disable CONFIG_FRAME_POINTER, which:

BTW when you are mentioning this -- my measurements were with FP disabled.

Is there any reasonably simple-to-use benchmark I could run with FP=y and FP=n quickly?

Nevermind, I tried 10000000 stack unwindings several times and ran netperf too. Both on the same virtual machine. On these microbenchmarks, the former performs ~ 1.03 times better, the latter ~ 1.3 times. When Mel measured the difference, it was around 10 % overall using more sophisticated benchmarks.

With FP=n:

# time echo 10000000 > /dev/test_dwarf
real    0m6.659s
user    0m0.000s
sys     0m6.655s
# for aa in 1 0 0 0 0; do netperf -P $aa ; done
MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to
localhost () port 0 AF_INET
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec
 87380  16384  16384    10.00    47819.30
 87380  16384  16384    10.00    41991.01
 87380  16384  16384    10.00    43607.82
 87380  16384  16384    10.00    42208.44
 87380  16384  16384    10.00    44383.92

With FP=y:

# time echo 10000000 > /dev/test_dwarf
real    0m6.869s
user    0m0.000s
sys     0m6.868s
# for aa in 1 0 0 0 0; do netperf -P $aa ; done
MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to
localhost () port 0 AF_INET
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec
 87380  16384  16384    10.00    37807.90
 87380  16384  16384    10.00    32246.67
 87380  16384  16384    10.00    31358.76
 87380  16384  16384    10.00    32450.00
 87380  16384  16384    10.00    31326.70

thanks,

Signature · 2 lines
js
suse labs
#52 lore raw
In reply to Ingo Molnar
Quoted 18 lines · Ingo Molnar
Quoted 6 lines · Josh Poimboeuf
Quoted 4 lines · Ingo Molnar

Being able to generate more optimal code in the hottest code paths of the kernel is the _real_, primary upstream kernel benefit of a different debuginfo method - which has to be weighed against the pain of introducing a new unwinder. But this submission does not talk about that aspect at all, which should be fixed I think.

Quoted 7 lines

Actually I devoted an entire one-sentence paragraph to performance in the documentation:

The simpler debuginfo format also enables the unwinder to be relatively fast, which is important for perf and lockdep.

But I'll try to highlight that a little more.

That's not what I meant! The speedup comes from (hopefully) being able to disable CONFIG_FRAME_POINTER, which:

Quoted 8 lines

- creates simpler/faster function prologues and epilogues - no managing of RBP needed

- gives one more generic purpose register to work from. This matters less on 64-bit kernels but it's a small effect.

I've seen numbers of 1-2% of instruction count reduction in common kernel workloads, which would be pretty significant on well cached workloads.

Ah, you meant runtime performance with FP disabled. I also dedicated a whole sentence to that one :-)

Unlike frame pointers, the debuginfo is out-of-band, so it has no effect on runtime performance.

I'll try to flesh that out and maybe come up with some numbers.

Signature · 1 line
Josh
#53 lore raw
In reply to Ingo Molnar

Ccing Mel who did proper measurements and can hopefully comment on his results.

Quoted 11 lines · Ingo Molnar

That's not what I meant! The speedup comes from (hopefully) being able to disable CONFIG_FRAME_POINTER, which:

- creates simpler/faster function prologues and epilogues - no managing of RBP needed

- gives one more generic purpose register to work from. This matters less on 64-bit kernels but it's a small effect.

I've seen numbers of 1-2% of instruction count reduction in common kernel workloads, which would be pretty significant on well cached workloads.

thanks,

Signature · 2 lines
js
suse labs
#54 lore raw
Quoted 16 lines · Jiri Slaby

Ccing Mel who did proper measurements and can hopefully comment on his results.

Quoted 11 lines · Ingo Molnar

That's not what I meant! The speedup comes from (hopefully) being able to disable CONFIG_FRAME_POINTER, which:

- creates simpler/faster function prologues and epilogues - no managing of RBP needed

- gives one more generic purpose register to work from. This matters less on 64-bit kernels but it's a small effect.

I've seen numbers of 1-2% of instruction count reduction in common kernel workloads, which would be pretty significant on well cached workloads.

I didn't preserve the data involved but in a variety of workloads including netperf, page allocator microbenchmark, pgbench and sqlite, enabling framepointer introduced overhead of around the 5-10% mark. According to an internal report I gave at the time, hackbench-thread-sockets was around the 5% mark and a perf run showed "3.49% more cache misses with framepointer enabled and 6.59% more cycles". Additional notes I made at the time although again, without the original data is

---8<---
It looks like a small amount of overhead added everywhere and the size of the vmlinux files supports that

text data bss dec hex filename
8143072 6480614 11153408 25777094 18953c6 vmlinux/decker/vmlinux-4.8.0-disable-fp 8396698 6480614 11153408 26030720 18d3280 vmlinux/decker/vmlinux-4.8.0-enable-fp

I also took a closer look at the pagealloc microbenchmarks because they rely on so few functions. Profiles were not always captured due to the short-lived nature of some of the tests so I looked at batches of 16384 allocation/frees of order-0 pages. Overall it showed 4.46% decline with framepointer enabled and profiling. 3.89% more cycles and 24.94% more cache misses.

As before, the framepointer cache miss overhead is not that obvious as the bulk of samples take place elsewhere -- in this case, in checking whether pages are buddies when merging. It's slightly clearer in __rmqueue where 17.9% of cache misses are in the function entry point with framepointer enabled vs 4.04% with framepointer disabled. ---8<---

Granted, the check was done back in 4.8, but I've no reason to believe that 4.12 is any different and enabling framepointer does have a quite substantial hit to workloads that spent a lot of time in the kernel.

Signature · 2 lines
Mel Gorman
SUSE Labs