lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 10 Feb 2012 12:25:18 +0100
From:	Jiri Olsa <jolsa@...hat.com>
To:	acme@...hat.com, a.p.zijlstra@...llo.nl, mingo@...e.hu,
	paulus@...ba.org, cjashfor@...ux.vnet.ibm.com, fweisbec@...il.com
Cc:	linux-kernel@...r.kernel.org
Subject: [PATCH 4/5] unwind, api: Add unwind interface and implementation for x86_64

Adding unwind interface with x86_64 implementation.
The interface consists of following functions:

struct unw_t;
- single backtrace handle

void unw_init(struct unw_t *u);
- initialize the handle

void unw_regs(struct unw_t *u, struct pt_regs *regs);
- returns current struct pt_regs registers data

int  unw_step(struct unw_t *u);
- makes single backtrace step

void unw_backtrace(void);
- runs the backtrace unwind and printk it out

The example usage is shown in unw_backtrace function.
---
 arch/x86/include/asm/unwind.h    |   14 +++
 arch/x86/kernel/Makefile         |    1 +
 arch/x86/kernel/unwind_init_64.S |   31 +++++++
 include/linux/unwind.h           |   23 +++++
 kernel/Makefile                  |    1 +
 kernel/unwind.c                  |  180 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 250 insertions(+), 0 deletions(-)
 create mode 100644 arch/x86/include/asm/unwind.h
 create mode 100644 arch/x86/kernel/unwind_init_64.S
 create mode 100644 include/linux/unwind.h
 create mode 100644 kernel/unwind.c

diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h
new file mode 100644
index 0000000..beab2f7
--- /dev/null
+++ b/arch/x86/include/asm/unwind.h
@@ -0,0 +1,14 @@
+#ifndef _ARCH_X86_KERNEL_UNWIND_H
+#define _ARCH_X86_KERNEL_UNWIND_H
+
+#include <asm/ptrace-abi.h>
+
+#ifdef __ASSEMBLY__
+#ifdef __i386__
+#define UNW_X86_CFA_OFF 	(FRAME_SIZE + 0x0)
+#else
+#define UNW_X86_64_CFA_OFF 	(FRAME_SIZE + 0x0)
+#endif /* __i386__ */
+#endif /* __ASSEMBLY__ */
+
+#endif  /* _ARCH_X86_KERNEL_UNWIND_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 8a7c0ec..a77fff3 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
 obj-$(CONFIG_SWIOTLB)			+= pci-swiotlb.o
 obj-$(CONFIG_OF)			+= devicetree.o
 obj-$(CONFIG_UNWIND)			+= dwarf.o
+obj-$(CONFIG_UNWIND)			+= unwind_init_$(BITS).o
 
 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/unwind_init_64.S b/arch/x86/kernel/unwind_init_64.S
new file mode 100644
index 0000000..4c8c9ed
--- /dev/null
+++ b/arch/x86/kernel/unwind_init_64.S
@@ -0,0 +1,31 @@
+
+#include <linux/linkage.h>
+#include <asm/ptrace-abi.h>
+#include <asm/unwind.h>
+
+	.code64
+ENTRY(unw_init)
+	/* Callee saved: RBX, RBP, R12-R15  */
+	movq %r12, R12(%rdi)
+	movq %r13, R13(%rdi)
+	movq %r14, R14(%rdi)
+	movq %r15, R15(%rdi)
+	movq %rbp, RBP(%rdi)
+	movq %rbx, RBX(%rdi)
+
+	movq %r8,  R8(%rdi)
+	movq %r9,  R9(%rdi)
+	movq %rdi, RDI(%rdi)
+	movq %rsi, RSI(%rdi)
+	movq %rdx, RDX(%rdi)
+	movq %rax, RAX(%rdi)
+	movq %rcx, RCX(%rdi)
+
+	leaq 8(%rsp), %rax /* exclude this call.  */
+	movq %rax, UNW_X86_64_CFA_OFF(%rdi)
+	movq 0(%rsp), %rax
+	movq %rax, RIP(%rdi)
+
+	xorq %rax, %rax
+	retq
+END(unw_init)
diff --git a/include/linux/unwind.h b/include/linux/unwind.h
new file mode 100644
index 0000000..d99b028
--- /dev/null
+++ b/include/linux/unwind.h
@@ -0,0 +1,23 @@
+#ifndef UNWIND_H
+#define UNWIND_H
+
+#include <linux/ptrace.h>
+#include <asm/unwind.h>
+#include <linux/dwarf.h>
+
+struct unw_t {
+	struct pt_regs regs;
+	dwarf_word_t cfa;
+
+	/*
+	 * First 2 items are touched by assembly code,
+	 * do not move them.
+	 */
+};
+
+void unw_init(struct unw_t *u);
+void unw_regs(struct unw_t *u, struct pt_regs *regs);
+int  unw_step(struct unw_t *u);
+void unw_backtrace(void);
+
+#endif /* UNWIND_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index 3ddbc72..d472f4e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_UNWIND) += dwarf-read.o
 obj-$(CONFIG_UNWIND) += dwarf-cfi.o
 obj-$(CONFIG_UNWIND) += dwarf-expression.o
 obj-$(CONFIG_UNWIND) += dwarf-fde.o
+obj-$(CONFIG_UNWIND) += unwind.o
 
 $(obj)/configs.o: $(obj)/config_data.h
 
diff --git a/kernel/unwind.c b/kernel/unwind.c
new file mode 100644
index 0000000..f5191d5
--- /dev/null
+++ b/kernel/unwind.c
@@ -0,0 +1,180 @@
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/unwind.h>
+#include <linux/dwarf.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+
+struct table_entry {
+	int32_t start_ip_offset;
+	int32_t fde_offset;
+};
+
+static struct table_entry* 	table_data;
+static dwarf_word_t 		table_count;
+static dwarf_word_t		table_base;
+
+static struct table_entry*
+lookup(dwarf_word_t ip)
+{
+	struct table_entry *fde = NULL;
+	unsigned long lo, hi, mid;
+
+	ip -= table_base;
+
+	/* Do a binary search for right entry. */
+	for (lo = 0, hi = table_count; lo < hi;)
+	{
+		mid = (lo + hi) / 2;
+		fde = table_data + mid;
+
+		if (ip < fde->start_ip_offset)
+			hi = mid;
+		else
+			lo = mid + 1;
+	}
+
+	if (hi <= 0)
+		return NULL;
+
+	fde = table_data + hi - 1;
+	return fde;
+}
+
+#ifdef CONFIG_UNWIND_EH_FRAME
+extern char __eh_frame_hdr_start[];
+extern char __eh_frame_hdr_end[];
+extern char __eh_frame_start[];
+extern char __eh_frame_end[];
+
+struct eh_frame_hdr {
+	unsigned char version;
+	unsigned char eh_frame_ptr_enc;
+	unsigned char fde_count_enc;
+	unsigned char table_enc;
+};
+
+static int __init eh_frame_init(void)
+{
+	struct eh_frame_hdr *hdr;
+	dwarf_word_t addr, eh_frame_start;
+
+	hdr = (struct eh_frame_hdr *) __eh_frame_hdr_start;
+	addr = (dwarf_word_t) (hdr + 1);
+
+	if (dwarf_read_pointer(&addr, hdr->eh_frame_ptr_enc,
+			       &eh_frame_start)) {
+		printk("unwind failed to read eh_frame_start\n");
+		goto failed;
+	}
+
+	if (dwarf_read_pointer(&addr, hdr->fde_count_enc,
+			       &table_count)) {
+		printk("unwind failed to read fde_count\n");
+		goto failed;
+	}
+
+	if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
+		printk("unwind unexpected table_enc\n");
+		goto failed;
+	}
+
+	table_data = (struct table_entry *) addr;
+	table_base = (dwarf_word_t) hdr;
+
+	printk("unwind __eh_frame_hdr_start  %p\n", __eh_frame_hdr_start);
+	printk("unwind __eh_frame_hdr_end    %p\n", __eh_frame_hdr_end);
+	printk("unwind __eh_frame_start      %p\n", __eh_frame_start);
+	printk("unwind __eh_frame_en         %p\n", __eh_frame_end);
+	printk("unwind version               %x\n", hdr->version);
+	printk("unwind eh_frame_ptr_enc      %x\n", hdr->eh_frame_ptr_enc);
+	printk("unwind fde_count_enc         %x\n", hdr->fde_count_enc);
+	printk("unwind table_enc             %x\n", hdr->table_enc);
+	printk("unwind table_data            %p\n", table_data);
+	printk("unwind table_count           %llx\n", table_count);
+	printk("unwind table_base            %llx\n", table_base);
+
+	printk("unwind eh_frame table initialized\n");
+	return 0;
+
+ failed:
+	printk("unwind table initialization failed\n");
+	return -EINVAL;
+}
+#endif /* CONFIG_UNWIND_EH_FRAME */
+
+static int __init unw_init_table(void)
+{
+#ifdef CONFIG_UNWIND_EH_FRAME
+	return eh_frame_init();
+#endif
+	return -EINVAL;
+}
+
+pure_initcall(unw_init_table);
+
+__weak void unw_init(struct unw_t *u)
+{
+}
+
+static void dwarf_regs_get(struct unw_t *u, struct dwarf_regs *regs)
+{
+	regs->cfa = u->cfa;
+	dwarf_regs_pt2dwarf(&u->regs, regs);
+}
+
+static void dwarf_regs_set(struct unw_t *u, struct dwarf_regs *regs)
+{
+	u->cfa = regs->cfa;
+	dwarf_regs_dwarf2pt(regs, &u->regs);
+}
+
+int unw_step(struct unw_t *u)
+{
+	struct table_entry *entry;
+	struct dwarf_fde fde;
+	struct dwarf_regs regs;
+	void *data;
+	int ret;
+
+	entry = lookup(u->regs.ip);
+	if (!entry)
+		return -EINVAL;
+
+	data = (void *) (table_base + entry->fde_offset);
+
+	ret = dwarf_fde_init(&fde, data);
+	if (ret)
+		return ret;
+
+	dwarf_regs_get(u, &regs);
+
+	ret = dwarf_fde_process(&fde, &regs);
+	if (!ret)
+		dwarf_regs_set(u, &regs);
+
+	return ret;
+}
+
+void unw_regs(struct unw_t *u, struct pt_regs *regs)
+{
+	memcpy(regs, &u->regs, sizeof(u->regs));
+}
+
+void unw_backtrace(void)
+{
+	struct unw_t unw;
+	struct pt_regs regs;
+
+	unw_init(&unw);
+
+	printk("unwind backtrace:\n");
+
+	do {
+		unw_regs(&unw, &regs);
+		printk("    [0x%lx] %pS\n", regs.ip, (void *) regs.ip);
+	} while (!unw_step(&unw));
+
+}
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ