[minicoredumper] [PATCH 2/2] minicoredumper: add new get_stack_pointer function

Marco Felsch Marco.Felsch at preh.de
Wed Aug 2 17:30:01 CEST 2017


Since kernel 4.9 the ESP isn't any more reported to the /proc filesystem.
But the stack pointer is stored in the origin core file too [1] so we can
extract them from the corefile.

The disadvantage of this methode is the architecture dependency. Therefore
I've add a new function to get the stack pointer from the core file and
keep the 'old' methode for the backward compatibility. We still use the
proc-fs to get the stack pointer for hosts with a kernel < 4.9. This keep
the support for a wide range of architectures.

[1] http://lists.linutronix.de/pipermail/minicoredumper/2017-July/thread.html

Signed-off-by: Marco Felsch <Marco.Felsch at preh.de>
---
 configure.ac                      |  14 ++-
 src/minicoredumper/corestripper.c | 197 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 208 insertions(+), 3 deletions(-)

diff --git a/configure.ac b/configure.ac
index cb13fa9..9cef0c7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,6 +41,15 @@ AC_SUBST([ISODATE])
 
 AC_CANONICAL_HOST
 AC_CANONICAL_BUILD
+
+AC_MSG_CHECKING([for supported architecture])
+AS_CASE(["$host_cpu"],
+	[aarch64*], [ARCH=aarch64],
+	[ARCH=""])
+AS_IF([test "x$ARCH" != x],
+	[AC_MSG_RESULT([yes])],
+	[AC_MSG_RESULT([no! stacktrace may not work correctly])])
+
 AC_LANG([C])
 
 AC_PROG_CC
@@ -170,7 +179,10 @@ AC_ARG_VAR([MCD_REGD_USER_GROUP],
 AS_IF([test "x$MCD_REGD_USER_GROUP" = x],
       [MCD_REGD_USER_GROUP="root:root"], [])
 
-MCD_CPPFLAGS="-Wall -D_GNU_SOURCE -D_LARGEFILE64_SOURCE $MCD_WERROR"
+AS_IF([test "x$ARCH" = x],
+	[MCD_CPPFLAGS="-Wall -D_GNU_SOURCE -D_LARGEFILE64_SOURCE $MCD_WERROR"],
+	[MCD_CPPFLAGS="-Wall -D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D$ARCH $MCD_WERROR"])
+
 AC_SUBST([MCD_CPPFLAGS])
 
 MCD_CFLAGS="-std=c99"
diff --git a/src/minicoredumper/corestripper.c b/src/minicoredumper/corestripper.c
index 26501e2..0a392c9 100644
--- a/src/minicoredumper/corestripper.c
+++ b/src/minicoredumper/corestripper.c
@@ -44,6 +44,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
+#include <sys/utsname.h>
 #include <sys/mman.h>
 #include <sys/procfs.h>
 #include <sys/syscall.h>
@@ -51,6 +52,12 @@
 #include <linux/futex.h>
 #include <elfutils/version.h>
 
+#if defined(aarch64)
+# include <asm/ptrace.h>
+#else
+# pragma message ("Your architecture isn't supported yet. Stacktrace for kernel >= 4.9 will not work correctly!")
+#endif
+
 #include "prog_config.h"
 #include "dump_data_private.h"
 #include "minicoredumper.h"
@@ -72,9 +79,19 @@
 #define PTRACE_INTERRUPT 0x4207
 #endif
 
+#define LINUX_490 264448	/* 0x040900 -> 4.9.0 */
+#define KERNEL_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#define USE_PROC  0
+#define USE_ELF_H 1
+
 static struct dump_info *global_di;
 static long PAGESZ;
 
+#if defined(aarch64)
+typedef struct user_pt_regs regs_t;
+static regs_t *regs;
+#endif
+
 struct remote_data_callbacks {
 	void *(*setup_data)(struct dump_data_elem *, void *);
 	void (*cleanup_data)(void *);
@@ -2116,10 +2133,137 @@ static int note_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
 	return 0;
 }
 
+static int get_arch_stack_pointer(struct dump_info *di, const struct elf_prstatus *status)
+{
+	int ret = -1;
+	unsigned int i;
+
+	/* find the correct entry */
+	for (i = 0; i < di->ntsks; i++) {
+		if (di->tsk_list[i].task == status->pr_pid) {
+#if defined(aarch64)
+			regs = (regs_t *)status->pr_reg;
+			di->tsk_list[i].sp = regs->sp;
+#else
+			info("Your architecture is currently not supported.");
+#endif
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int stack_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
+{
+	size_t offset = 0;
+	Elf_Data *data;
+
+	data = elf_getdata_rawchunk(elf, phdr->p_offset, phdr->p_filesz,
+				    ELF_T_NHDR);
+	if (!data) {
+		info("elf_getdata_rawchunk failed: %s",
+			elf_errmsg(elf_errno()));
+		return -1;
+	}
+
+	while (offset < data->d_size) {
+		const struct elf_prstatus *status;
+		size_t name_offset;
+		size_t desc_offset;
+		const char *desc;
+		GElf_Nhdr nhdr;
+
+		offset = gelf_getnote(data, offset, &nhdr, &name_offset,
+				      &desc_offset);
+		if (offset == 0) {
+			info("gelf_getnote failed: %s",
+				elf_errmsg(elf_errno()));
+			return -1;
+		}
+
+		desc = data->d_buf + desc_offset;
+
+		if (nhdr.n_type != NT_PRSTATUS)
+			continue;
+
+		status = (const struct elf_prstatus *)desc;
+
+		/* get sp from architecture specific prstatus note section */
+		if (get_arch_stack_pointer(di, status) < 0)
+			return -1;
+	}
+
+	/* success */
+	return 1;
+}
+
 /*
- * Dumps the current stack of all threads.
+ * Dumps the current stack of all threads using the core-file note section.
  */
-static int dump_stacks(struct dump_info *di)
+static int dump_stacks_elf(struct dump_info *di)
+{
+	unsigned long stack_addr;
+	struct core_vma *tmp;
+	size_t max_len;
+	size_t len;
+	int i;
+	GElf_Phdr type;
+
+	memset(&type, 0, sizeof(type));
+	type.p_type = PT_NOTE;
+
+	if (di->cfg->prog_config.stack.first_thread_only) {
+		/* find and set the first task */
+		do_elf_ph_parse(di, &type, note_cb);
+	}
+
+	if (di->first_pid)
+		info("first thread: %i", di->first_pid);
+
+	/* only run once for all stack pointers */
+	do_elf_ph_parse(di, &type, stack_cb);
+
+	for (i = 0; i < di->ntsks; i++) {
+		/* skip this task if we should only dump the
+		 * first task and we know the first task */
+		if (di->first_pid && (di->first_pid != di->tsk_list[i].task))
+			continue;
+
+		stack_addr = di->tsk_list[i].sp;
+
+		/* find the vma containing the stack */
+		tmp = get_vma_pos(di, stack_addr);
+		if (!tmp) {
+			info("unable to find thread #%d's (%d) stack", i + 1,
+			     di->tsk_list[i].task);
+			continue;
+		}
+
+		/* determine how much of the stack is actually used */
+		len = tmp->file_end - stack_addr;
+
+		/* truncate stack if above max threshold */
+		max_len = di->cfg->prog_config.stack.max_stack_size;
+		if (max_len && len > max_len) {
+			info("stack[%d] is too large (%zu bytes), truncating "
+			     "to %zu bytes", di->tsk_list[i].task, len, max_len);
+			len = max_len;
+		}
+
+		/* dump the bottom part of stack in use */
+		dump_vma(di, stack_addr, len, 0, "stack[%d]",
+			 di->tsk_list[i].task);
+	}
+
+	return 0;
+}
+
+/*
+ * Dumps the current stack of all threads using the proc-fs
+ */
+static int dump_stacks_proc(struct dump_info *di)
 {
 	unsigned long stack_addr;
 	struct core_vma *tmp;
@@ -2179,6 +2323,55 @@ static int dump_stacks(struct dump_info *di)
 	return 0;
 }
 
+/*
+ * Helper to check linux version and to determine the used dumping process.
+ */
+
+static int check_linux_compatibility(void)
+{
+	int ret = -1;
+	unsigned int ver, plevel, sub;
+	struct utsname sys_info;
+
+	ret = uname(&sys_info);
+	if (ret)
+		goto out;
+
+	if(sscanf(sys_info.release, "%u.%u.%u-", &ver, &plevel, &sub) != 3) {
+		ret = -1;
+		goto out;
+	}
+
+	info("kernel: %u.%u.%u - (%u)", ver, plevel, sub, KERNEL_VER(ver, plevel, sub));
+
+	if(KERNEL_VER(ver, plevel, sub) >= LINUX_490)
+		ret = USE_ELF_H;
+	else
+		ret = USE_PROC;
+out:
+	return ret;
+}
+
+static int dump_stacks(struct dump_info *di)
+{
+	int ret = -1;
+
+	switch(check_linux_compatibility()) {
+	case USE_PROC:
+		info("dump: stack: using proc-fs");
+		ret = dump_stacks_proc(di);
+		break;
+	case USE_ELF_H:
+		info("dump: stack: using core file header");
+		ret = dump_stacks_elf(di);
+		break;
+	default:
+		fatal("during stack dumping");
+	}
+
+	return ret;
+}
+
 static off64_t get_core_pos(struct dump_info *di, unsigned long addr)
 {
 	struct core_vma *vma;
-- 
2.11.0




More information about the minicoredumper mailing list