[minicoredumper] [PATCH 03/14] minicoredumper: read stack pointer from registers
Mateusz Moscicki
m.moscicki2 at partner.samsung.com
Tue May 21 14:52:39 CEST 2019
From: Łukasz Stelmach <l.stelmach at samsung.com>
After stopping the reporting esp/eip values in Linux kernel
(0a1eb2d474ed) it is no longer possible to obtain the values of an SP
register for non-crashing threads of a crashing process. This and
following commits use architecture specific code to read the values from
NT_PRSTATUS notes in a core dump.
Change-Id: I6d5a7ef9275fc301ec11a795fb4332a7e5586c01
---
src/minicoredumper/corestripper.c | 172 +++++++++++++-------------------------
src/minicoredumper/corestripper.h | 3 +-
2 files changed, 60 insertions(+), 115 deletions(-)
diff --git a/src/minicoredumper/corestripper.c b/src/minicoredumper/corestripper.c
index 350927f..8ebcb4b 100644
--- a/src/minicoredumper/corestripper.c
+++ b/src/minicoredumper/corestripper.c
@@ -28,6 +28,7 @@
#include <sys/procfs.h>
#include <sys/syscall.h>
#include <sys/ptrace.h>
+#include <sys/user.h>
#include <linux/futex.h>
#include <elfutils/version.h>
@@ -371,14 +372,11 @@ static int copy_file(const char *dest, const char *src)
static int get_task_list(struct dump_info *di)
{
- pid_t *pidlist = NULL;
struct dirent *de;
int count = 0;
char buf[64];
int err = 0;
- int pid;
DIR *d;
- int i;
di->tsks = NULL;
di->ntsks = 0;
@@ -405,51 +403,16 @@ static int get_task_list(struct dump_info *di)
if (count == 0)
goto out;
- pidlist = calloc(count, sizeof(pid_t));
- if (!pidlist) {
+ di->tsks = calloc(count, sizeof(struct elf_prstatus));
+ if (!di->tsks) {
err = 1;
goto out;
}
- rewinddir(d);
-
- /* read the actual tasks */
- for (i = 0; i < count; ) {
- de = readdir(d);
- if (!de) {
- err = 1;
- goto out;
- }
-
- /* ignore hidden files */
- if (de->d_name[0] == '.')
- continue;
-
- if (sscanf(de->d_name, "%d", &pid) != 1) {
- err = 1;
- goto out;
- }
-
- pidlist[i] = pid;
-
- i++;
- }
-
- /* make sure we really have exactly "count" tasks */
- if (readdir(d) != NULL) {
- err = 1;
- goto out;
- }
-
- di->tsks = pidlist;
- pidlist = NULL;
di->ntsks = count;
out:
closedir(d);
- if (pidlist)
- free(pidlist);
-
return err;
}
@@ -1870,52 +1833,27 @@ static void cleanup_di(struct dump_info *di)
}
}
-static int get_stack_pointer(pid_t pid, unsigned long *addr)
+// Since the commit 0a1eb2d474ed kernel has stopped reporting eip/esp in
+// /proc/<pid>/stat, so we must read these values from the task memory
+static void get_stack_pointer(struct dump_info *di, int i, unsigned long *addr)
{
-#define STAT_LINE_MAXSIZE 4096
- FILE *f = NULL;
- int err = -1;
- char *buf;
- char *p;
- int i;
-
- /* create a buffer large enough for stat line */
- buf = malloc(STAT_LINE_MAXSIZE);
- if (!buf)
- goto out_err;
-
- /* open stat file */
- snprintf(buf, STAT_LINE_MAXSIZE, "/proc/%d/stat", pid);
- f = fopen(buf, "r");
- if (!f)
- goto out_err;
-
- /* read line */
- if (fgets(buf, STAT_LINE_MAXSIZE, f) == NULL)
- goto out_err;
-
- /* find 29th item: man proc(5) */
- p = buf;
- for (i = 0; i < 28; i++) {
- p = strchr(p, ' ');
- if (!p)
- goto out_err;
- p++;
- }
-
- /* read stack pointer */
- if (sscanf(p, "%lu ", addr) != 1)
- goto out_err;
-
- err = 0;
-out_err:
- if (f)
- fclose(f);
- if (buf)
- free(buf);
+#if defined(__arm__)
+ struct user_regs *u_reg = (struct user_regs *)&di->tsks[i].pr_reg;
+#else
+ struct user_regs_struct *u_reg = (struct user_regs_struct *)&di->tsks[i].pr_reg;
+#endif
- return err;
-#undef STAT_LINE_MAXSIZE
+#if defined(__x86_64__)
+ *addr = u_reg->rsp;
+#elif defined(__i386__)
+ *addr = u_reg->esp;
+#elif defined(__arm__)
+ *addr = u_reg->uregs[13];
+#elif defined(__aarch64__)
+ *addr = u_reg->sp;
+#else
+#error Unsupported architecture
+#endif
}
static struct core_vma *get_next_vma_range(struct dump_info *di,
@@ -1964,6 +1902,8 @@ static int dump_vma(struct dump_info *di, unsigned long start, size_t len,
end = start + len;
+ info("dumping %p-%p", start, end);
+
tmp = get_next_vma_range(di, start, end, di->vma);
if (!tmp) {
info("vma not found start=0x%lx! bad recept or internal bug!",
@@ -2002,6 +1942,12 @@ static int dump_vma(struct dump_info *di, unsigned long start, size_t len,
info("dump: %s: %zu bytes @ 0x%lx", desc ? desc : "",
len, dump_start);
+ info ("add_core_data %ld %ld %ld %p",
+ tmp->file_off + dump_start - tmp->start,
+ len,
+ di->mem_fd,
+ dump_start);
+
err = add_core_data(di, tmp->file_off + dump_start -
tmp->start, len, di->mem_fd,
dump_start);
@@ -2022,6 +1968,7 @@ static int note_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
{
size_t offset = 0;
Elf_Data *data;
+ int i;
data = elf_getdata_rawchunk(elf, phdr->p_offset, phdr->p_filesz,
ELF_T_NHDR);
@@ -2031,7 +1978,9 @@ static int note_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
return -1;
}
- while (offset < data->d_size) {
+ di->first_pid = 0;
+ i = 0;
+ while (i < di->ntsks && offset < data->d_size) {
const struct elf_prstatus *status;
size_t name_offset;
size_t desc_offset;
@@ -2053,14 +2002,14 @@ static int note_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
status = (const struct elf_prstatus *)desc;
- di->first_pid = status->pr_pid;
+ if (!di->first_pid)
+ di->first_pid = status->pr_pid;
- /* success, we can stop */
- return 1;
+ memcpy(&di->tsks[i], status, sizeof(struct elf_prstatus));
+ i++;
}
- /* we found nothing, keep looking */
- return 0;
+ return (di->first_pid != 0);
}
/*
@@ -2074,14 +2023,12 @@ static int dump_stacks(struct dump_info *di)
size_t len;
int i;
- if (di->cfg->prog_config.stack.first_thread_only) {
- GElf_Phdr type;
+ GElf_Phdr type;
- /* find and set the first task */
- memset(&type, 0, sizeof(type));
- type.p_type = PT_NOTE;
- do_elf_ph_parse(di, &type, note_cb);
- }
+ /* find and set task statuses*/
+ memset(&type, 0, sizeof(type));
+ type.p_type = PT_NOTE;
+ do_elf_ph_parse(di, &type, note_cb);
if (di->first_pid)
info("first thread: %i", di->first_pid);
@@ -2089,21 +2036,18 @@ static int dump_stacks(struct dump_info *di)
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->tsks[i]))
+ if (di->cfg->prog_config.stack.first_thread_only &&
+ di->first_pid && (di->first_pid != di->tsks[i].pr_pid))
continue;
/* grab the stack pointer */
- if (get_stack_pointer(di->tsks[i], &stack_addr) != 0) {
- info("unable to find thread #%d's (%d) stack pointer",
- i + 1, di->tsks[i]);
- continue;
- }
+ get_stack_pointer(di, i, &stack_addr);
/* 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->tsks[i]);
+ di->tsks[i].pr_pid);
continue;
}
@@ -2114,13 +2058,13 @@ static int dump_stacks(struct dump_info *di)
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->tsks[i], len, max_len);
+ "to %zu bytes", di->tsks[i].pr_pid, len, max_len);
len = max_len;
}
/* dump the bottom part of stack in use */
dump_vma(di, stack_addr, len, 0, "stack[%d]",
- di->tsks[i]);
+ di->tsks[i].pr_pid);
}
return 0;
@@ -2844,13 +2788,13 @@ static void copy_proc_files(struct dump_info *di, int tasks, const char *name,
for (i = 0 ; i < di->ntsks; i++) {
snprintf(path, size, "%s/proc/%d/task/%d", di->dst_dir,
- di->cmd_params->pid, di->tsks[i]);
+ di->cmd_params->pid, di->tsks[i].pr_pid);
mkdir(path, 0700);
/* handle the normal task case */
if (!do_fds) {
snprintf(path, size, "%s/proc/%d/task/%d/%s",
- di->dst_dir, di->cmd_params->pid, di->tsks[i], name);
+ di->dst_dir, di->cmd_params->pid, di->tsks[i].pr_pid, name);
if (link)
copy_link(path, path + base_len);
@@ -2861,7 +2805,7 @@ static void copy_proc_files(struct dump_info *di, int tasks, const char *name,
/* special case: copy the symlinks in the fd directory */
snprintf(path, size, "%s/proc/%d/task/%d/fd", di->dst_dir,
- di->cmd_params->pid, di->tsks[i]);
+ di->cmd_params->pid, di->tsks[i].pr_pid);
mkdir(path, 0700);
d = opendir(path + base_len);
@@ -2878,7 +2822,7 @@ static void copy_proc_files(struct dump_info *di, int tasks, const char *name,
continue;
snprintf(path, size, "%s/proc/%d/task/%d/fd/%s",
- di->dst_dir, di->cmd_params->pid, di->tsks[i],
+ di->dst_dir, di->cmd_params->pid, di->tsks[i].pr_pid,
de->d_name);
copy_link(path, path + base_len);
@@ -3411,10 +3355,6 @@ static void do_dump(struct dump_info *di)
dump_maps(di, 1);
}
- /* copy intersting /proc data (if configured) */
- if (di->cfg->prog_config.write_proc_info)
- write_proc_info(di);
-
/* Get shared object list. This is necessary for sym_address() to work.
* This function will also dump the auxv data (if configured). */
get_so_list(di);
@@ -3423,6 +3363,10 @@ static void do_dump(struct dump_info *di)
if (di->cfg->prog_config.stack.dump_stacks)
dump_stacks(di);
+ /* copy intersting /proc data (if configured) */
+ if (di->cfg->prog_config.write_proc_info)
+ write_proc_info(di);
+
/* dump the pthread list (if configured) */
if (di->cfg->prog_config.dump_pthread_list)
get_pthread_list(di);
diff --git a/src/minicoredumper/corestripper.h b/src/minicoredumper/corestripper.h
index 22da645..1e6c918 100644
--- a/src/minicoredumper/corestripper.h
+++ b/src/minicoredumper/corestripper.h
@@ -10,6 +10,7 @@
#include <stdio.h>
#include <libelf.h>
#include <gelf.h>
+#include <sys/procfs.h>
struct core_data;
@@ -80,7 +81,7 @@ struct dump_info {
/* from core_pattern */
struct cmd_parameters *cmd_params;
- pid_t *tsks;
+ struct elf_prstatus *tsks;
int ntsks;
unsigned long vma_start;
--
2.7.4
More information about the minicoredumper
mailing list