[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