summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2012-04-18 14:04:41 +0200
committerLennart Poettering <lennart@poettering.net>2012-04-18 14:04:41 +0200
commit08b3ee2e1a4bb8f39349fbc9b1bfb01fea8a778e (patch)
treede2bf2dd96243e3d1495d05e65ee163782f418f9
parentef6086a473d9dba0973cd1c35bbee87e072e5224 (diff)
build-sys: rearrange source files
-rw-r--r--Makefile22
-rw-r--r--TODO17
-rw-r--r--context.h160
-rw-r--r--coredump-util.c (renamed from coredump.c)22
-rw-r--r--coredump-util.h36
-rw-r--r--coredump.h14
-rw-r--r--minidump.c1817
-rw-r--r--minidump.h22
-rw-r--r--mkminidump.c20
-rw-r--r--read-coredump.c277
-rw-r--r--read-coredump.h31
-rw-r--r--read-minidump.c77
-rw-r--r--read-minidump.h33
-rw-r--r--read-process.c466
-rw-r--r--read-process.h36
-rw-r--r--segfault.c20
-rw-r--r--util.c96
-rw-r--r--util.h31
-rw-r--r--write-minicore.c373
-rw-r--r--write-minicore.h30
-rw-r--r--write-minidump.c508
-rw-r--r--write-minidump.h30
22 files changed, 2367 insertions, 1771 deletions
diff --git a/Makefile b/Makefile
index d11153e..a1a50e3 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,25 @@ CFLAGS=-Wextra -Wall -O0 -g -D_GNU_SOURCE -pthread
all: segfault mkminidump core
-mkminidump: mkminidump.o coredump.h coredump.o minidump.o minidump.h format.h
+mkminidump: mkminidump.o \
+ minidump.o \
+ minidump.h \
+ context.h \
+ read-coredump.o \
+ read-coredump.h \
+ read-minidump.o \
+ read-minidump.h \
+ format.h \
+ read-process.o \
+ read-process.h \
+ write-minicore.o \
+ write-minicore.h \
+ write-minidump.o \
+ write-minidump.h \
+ coredump-util.h \
+ coredump-util.o \
+ util.c \
+ util.h
$(CC) $(CFLAGS) $(LIBS) $^ -o $@
segfault: segfault.c
@@ -12,4 +30,4 @@ core: segfault
( ./segfault ||: ) > /dev/null 2>&1
clean:
- rm -f core.* core segfault mkminidump
+ rm -f core.* core segfault mkminidump *.o
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..522d86d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,17 @@
+Now:
+
+- implement ELF coredump generator
+
+- implement minidump parser
+
+- additional CPUID sysinfo stream fields
+
+- parse thread name
+
+- build id logic
+
+Later:
+
+- Proper UTF8-to-UTF16 encoder
+
+- Possibly, full coredump generator, in addition to the existing minicore generator
diff --git a/context.h b/context.h
new file mode 100644
index 0000000..5e53bdf
--- /dev/null
+++ b/context.h
@@ -0,0 +1,160 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foocontexthfoo
+#define foocontexthfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <elf.h>
+#include <sys/user.h>
+#include <sys/procfs.h>
+#include <signal.h>
+#include <link.h>
+
+#include "minidump.h"
+#include "format.h"
+
+#define MINIDUMP_STREAMS_MAX 17
+#define CODE_SAVE_SIZE 256
+#define STACK_SAVE_SIZE (32*1024)
+
+struct buffer {
+ char *data;
+ size_t size;
+};
+
+struct extent {
+ unsigned long address;
+ size_t size;
+};
+
+struct map_info {
+ struct extent extent;
+ char *name;
+ char *build_id;
+
+ /* When writing a minidump, the location of this stream */
+ size_t minidump_offset;
+};
+
+struct thread_info {
+ pid_t tid;
+ unsigned long stack_pointer;
+ unsigned long instruction_pointer;
+ char *name;
+
+ bool have_prstatus:1;
+ bool have_siginfo:1;
+ bool have_user:1;
+
+ struct elf_prstatus prstatus; /* only available on coredumps */
+ siginfo_t siginfo; /* only available on ptrace */
+ struct user user; /* only available on ptrace */
+
+ struct user_regs_struct regs;
+ struct user_fpregs_struct fpregs;
+#ifdef __i386
+ struct user_fpxregs_struct fpxregs;
+#endif
+
+ /* When writing a minidump, the location of this context */
+ size_t minidump_offset;
+};
+
+struct context {
+ pid_t pid;
+ int coredump_fd;
+ int minidump_fd;
+
+ bool have_coredump_header:1;
+ bool have_minidump_header:1;
+ bool have_prpsinfo:1;
+
+ ElfW(Ehdr) coredump_header; /* only available on coredumps */
+ struct elf_prpsinfo prpsinfo; /* only available on coredumps */
+
+ struct minidump_header minidump_header; /* only available on minidumps */
+
+ struct buffer auxv;
+
+ /* The total maps we know off */
+ struct map_info *maps;
+ unsigned n_maps;
+ unsigned allocated_maps;
+
+ /* The subset we care about */
+ struct map_info *write_maps;
+ unsigned n_write_maps;
+
+ struct thread_info *threads;
+ unsigned n_threads;
+ unsigned allocated_threads;
+
+ /* Data from /proc */
+ struct buffer proc_maps;
+ struct buffer proc_status;
+ struct buffer proc_environ;
+ struct buffer proc_cmdline;
+ struct buffer proc_comm;
+ struct buffer proc_attr_current;
+ struct buffer proc_exe;
+
+ /* System data */
+ struct buffer proc_cpuinfo;
+ struct buffer lsb_release;
+ struct buffer os_release;
+
+ void *output;
+ size_t output_size;
+ size_t output_allocated;
+ size_t output_offset;
+
+ /* This is needed while writing a minidump */
+ struct minidump_directory minidump_directory[MINIDUMP_STREAMS_MAX];
+ uint32_t minidump_n_streams;
+
+ /* This is needed while writing a minicore */
+ ElfW(Phdr) *minicore_phs;
+ uint32_t minicore_n_phs;
+};
+
+#define CONTEXT_HAVE_PROCESS(c) ((c)->pid > 0)
+#define CONTEXT_HAVE_COREDUMP(c) ((c)->coredump_fd >= 0)
+#define CONTEXT_HAVE_MINIDUMP(c) ((c)->minidump_fd >= 0)
+
+int context_read_memory(struct context *c, unsigned long source, void *destination, size_t length);
+
+int context_add_thread(struct context *c, struct thread_info *i);
+int context_add_mapping(struct context *c, unsigned long start, unsigned long end, const char *name);
+
+struct map_info *context_find_map_info(struct map_info *m, unsigned n, unsigned long address);
+
+int context_reserve_bytes(struct context *c, size_t bytes, void **ptr, size_t *offset);
+int context_append_bytes(struct context *c, const void *data, size_t bytes, size_t *offset);
+int context_null_bytes(struct context *c, size_t bytes, size_t *offset);
+int context_append_concat_string(struct context *c, size_t *offset, size_t *size, ...);
+
+void context_show(FILE *f, struct context *c);
+int context_load(struct context *c);
+void context_release(struct context *c);
+
+#endif
diff --git a/coredump.c b/coredump-util.c
index 0a08898..83bf829 100644
--- a/coredump.c
+++ b/coredump-util.c
@@ -1,5 +1,25 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#include <errno.h>
#include <stdlib.h>
#include <string.h>
@@ -8,7 +28,7 @@
#include <unistd.h>
#include <sys/param.h>
-#include "coredump.h"
+#include "coredump-util.h"
int coredump_read_header(int fd, ElfW(Ehdr) *header) {
ssize_t l;
diff --git a/coredump-util.h b/coredump-util.h
new file mode 100644
index 0000000..2c9edee
--- /dev/null
+++ b/coredump-util.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foocoredumputilhfoo
+#define foocoredumputilhfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <elf.h>
+#include <link.h>
+
+int coredump_read_header(int fd, ElfW(Ehdr) *header);
+int coredump_read_segment_header(int fd, const ElfW(Ehdr) *header, unsigned long i, ElfW(Phdr) *segment);
+int coredump_read_memory(int fd, const ElfW(Ehdr) *header, unsigned long source, void *destination, size_t length);
+
+int coredump_find_note_segment(int fd, const ElfW(Ehdr) *header, off_t *offset, off_t *length);
+int coredump_next_note(int fd, off_t *offset, off_t *length, ElfW(Nhdr) *n, off_t *name, off_t *descriptor);
+
+#endif
diff --git a/coredump.h b/coredump.h
deleted file mode 100644
index 34cb902..0000000
--- a/coredump.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef foocoredumphfoo
-#define foocoredumphfoo
-
-#include <elf.h>
-#include <link.h>
-
-int coredump_read_header(int fd, ElfW(Ehdr) *header);
-int coredump_read_segment_header(int fd, const ElfW(Ehdr) *header, unsigned long i, ElfW(Phdr) *segment);
-int coredump_read_memory(int fd, const ElfW(Ehdr) *header, unsigned long source, void *destination, size_t length);
-
-int coredump_find_note_segment(int fd, const ElfW(Ehdr) *header, off_t *offset, off_t *length);
-int coredump_next_note(int fd, off_t *offset, off_t *length, ElfW(Nhdr) *n, off_t *name, off_t *descriptor);
-
-#endif
diff --git a/minidump.c b/minidump.c
index 5ca3395..2e1d54f 100644
--- a/minidump.c
+++ b/minidump.c
@@ -1,502 +1,74 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#include <assert.h>
-#include <stdbool.h>
-#include <dirent.h>
+#include <sys/param.h>
#include <stdlib.h>
#include <errno.h>
-#include <stdio.h>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
#include <string.h>
-#include <sys/param.h>
-#include <sys/user.h>
-#include <sys/procfs.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <time.h>
#include <stdarg.h>
-#include <sys/utsname.h>
-#include <stddef.h>
-#include <endian.h>
-#include "coredump.h"
-#include "format.h"
#include "minidump.h"
+#include "format.h"
+#include "coredump-util.h"
+#include "context.h"
+#include "read-minidump.h"
+#include "read-coredump.h"
+#include "read-process.h"
+#include "write-minidump.h"
+#include "write-minicore.h"
-#define MINIDUMP_STREAMS_MAX 17
-#define CODE_SAVE_SIZE 256
-#define STACK_SAVE_SIZE (32*1024)
-
-#define NOTE_SIZE_MAX (1024*1024*10)
-
-struct buffer {
- char *data;
- size_t size;
-};
-
-struct extent {
- unsigned long address;
- size_t size;
-};
-
-struct map_info {
- struct extent extent;
- char *name;
- char *build_id;
-
- size_t minidump_offset;
-};
-
-struct thread_info {
- pid_t tid;
- unsigned long stack_pointer;
- unsigned long instruction_pointer;
- char *name;
-
- bool have_prstatus:1;
- bool have_siginfo:1;
- bool have_user:1;
-
- struct elf_prstatus prstatus; /* only available on coredumps */
- siginfo_t siginfo; /* only available on ptrace */
- struct user user; /* only available on ptrace */
-
- struct user_regs_struct regs;
- struct user_fpregs_struct fpregs;
-#ifdef __i386
- struct user_fpxregs_struct fpxregs;
-#endif
-
- size_t minidump_offset;
-};
-
-struct context {
- pid_t pid;
- int coredump_fd;
- int minidump_fd;
-
- bool have_coredump_header:1;
- bool have_minidump_header:1;
- bool have_prpsinfo:1;
-
- ElfW(Ehdr) coredump_header; /* only available on coredumps */
- struct elf_prpsinfo prpsinfo; /* only available on coredumps */
-
- struct minidump_header minidump_header; /* only available on minidumps */
-
- struct buffer auxv;
-
- /* The total maps we know of */
- struct map_info *maps;
- unsigned n_maps;
- unsigned allocated_maps;
-
- /* The subset we know off */
- struct map_info *write_maps;
- unsigned n_write_maps;
-
- struct thread_info *threads;
- unsigned n_threads;
- unsigned allocated_threads;
-
- /* Data from /proc */
- struct buffer proc_maps;
- struct buffer proc_status;
- struct buffer proc_environ;
- struct buffer proc_cmdline;
- struct buffer proc_comm;
- struct buffer proc_attr_current;
- struct buffer proc_exe;
-
- /* system data */
- struct buffer proc_cpuinfo;
- struct buffer lsb_release;
- struct buffer os_release;
-
- void *output;
- size_t output_size;
- size_t output_allocated;
- size_t output_offset;
-
- /* This is needed while writing a minidump */
- struct minidump_directory minidump_directory[MINIDUMP_STREAMS_MAX];
- uint32_t minidump_n_streams;
-
- /* This is needed while writing a minicore */
- ElfW(Phdr) *minicore_phs;
- uint32_t minicore_n_phs;
-};
-
-#define HAVE_PROCESS(c) ((c)->pid > 0)
-#define HAVE_COREDUMP(c) ((c)->coredump_fd >= 0)
-#define HAVE_MINIDUMP(c) ((c)->minidump_fd >= 0)
-
-static void* memdup(const void *p, size_t l) {
- void *r;
-
- assert(p);
-
- r = malloc(l);
- if (!r)
- return NULL;
-
- memcpy(r, p, l);
- return r;
-}
-
-static int threads_begin(pid_t pid, DIR **_d) {
- char *path;
- DIR *d;
-
- assert(pid > 0);
-
- if (asprintf(&path, "/proc/%lu/task", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- d = opendir(path);
- free(path);
-
- if (!d)
- return -errno;
-
- *_d = d;
- return 0;
-}
-
-static int threads_next(DIR *d, pid_t *pid) {
- struct dirent buf, *de;
- int k;
- long l;
- char *p;
-
- for (;;) {
- k = readdir_r(d, &buf, &de);
- if (k != 0)
- return -k;
-
- if (!de)
- return 0;
-
- if (de->d_name[0] == '.')
- continue;
-
- if (de->d_type != DT_DIR)
- continue;
-
- errno = 0;
- l = strtol(de->d_name, &p, 10);
- if (errno != 0)
- continue;
-
- if (p && *p != 0)
- continue;
-
- if (l <= 0)
- continue;
-
- *pid = (pid_t) l;
- return 1;
- }
-}
-
-static int read_full_file(const char *path, void **_buffer, size_t *_size) {
- void *buffer = NULL;
- size_t size = 0, allocated = 0;
- FILE *f;
- int r;
-
- assert(path);
- assert(_buffer);
- assert(_size);
-
- f = fopen(path, "re");
- if (!f)
- return -errno;
-
- while (!feof(f)) {
- size_t k;
-
- if (size >= allocated) {
- size_t l;
- void *p;
-
- l = MAX(LINE_MAX, size * 2);
- p = realloc(buffer, l);
- if (!p) {
- r = -ENOMEM;
- goto finish;
- }
-
- buffer = p;
- allocated = l;
- }
-
- k = fread((uint8_t*) buffer + size, 1, allocated - size, f);
- if (k <= 0 && ferror(f)) {
- r = -errno;
- goto finish;
- }
-
- size += k;
- }
-
- r = 0;
-
- *_buffer = buffer;
- *_size = size;
-
-finish:
- fclose(f);
-
- return r;
-}
-
-static int attach_threads(struct context *c) {
- DIR* d = NULL;
- int r;
-
- assert(c);
- assert(HAVE_PROCESS(c));
-
- r = threads_begin(c->pid, &d);
- if (r < 0)
- return r;
-
- for (;;) {
- pid_t tid;
-
- r = threads_next(d, &tid);
- if (r < 0)
- goto finish;
- if (r == 0)
- break;
-
- if (ptrace(PTRACE_ATTACH, tid, NULL, NULL) < 0) {
-
- if (errno == ESRCH)
- continue;
-
- r = -errno;
- goto finish;
- }
-
- /* Wait until the thread is actually stopped */
- for (;;) {
- int status;
-
- if (waitpid(tid, &status, __WALL) < 0) {
- if (errno == EINTR)
- continue;
-
- r = -errno;
- goto finish;
- }
-
- if (WIFSTOPPED(status))
- break;
- }
- }
-
- r = 0;
-
-finish:
- if (d)
- closedir(d);
-
- return r;
-}
-
-static int detach_threads(struct context *c) {
- DIR* d = NULL;
- int r;
-
- assert(c);
- assert(HAVE_PROCESS(c));
-
- r = threads_begin(c->pid, &d);
- if (r < 0)
- return r;
-
- for (;;) {
- pid_t tid;
-
- r = threads_next(d, &tid);
- if (r < 0)
- goto finish;
- if (r == 0)
- break;
-
- if (ptrace(PTRACE_DETACH, tid, NULL, NULL) < 0) {
-
- if (errno == ESRCH)
- continue;
-
- r = -errno;
- goto finish;
- }
- }
-
- r = 0;
-
-finish:
- if (d)
- closedir(d);
-
- return r;
-}
-
-static int ptrace_copy(enum __ptrace_request req, pid_t pid, unsigned long source, void *destination, size_t length) {
- long l;
-
- assert(req == PTRACE_PEEKTEXT ||
- req == PTRACE_PEEKDATA ||
- req == PTRACE_PEEKUSER);
-
- assert(pid > 0);
- assert(destination);
- assert(length > 0);
-
- while (length > 0) {
-
- errno = 0;
- l = ptrace(req, pid, (void*) source, NULL);
- if (errno != 0)
- return -errno;
-
- memcpy(destination, &l, MIN(length, sizeof(l)));
-
- if (length <= sizeof(l))
- break;
-
- length -= sizeof(l);
- source += sizeof(l);
- destination = (uint8_t*) destination + sizeof(l);
- }
-
- return 0;
-}
-
-static int minidump_read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
- assert(c);
- assert(destination);
- assert(length > 0);
- assert(c->minidump_fd >= 0);
-
- /* FIXME */
-
- return -ENOTSUP;
-}
-
-static int read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
+int context_read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
int r;
assert(c);
assert(destination);
assert(length > 0);
- if (HAVE_COREDUMP(c)) {
+ if (CONTEXT_HAVE_COREDUMP(c)) {
r = coredump_read_memory(c->coredump_fd, &c->coredump_header, source, destination, length);
if (r != 0)
return r;
}
- if (HAVE_MINIDUMP(c)) {
+ if (CONTEXT_HAVE_MINIDUMP(c)) {
r = minidump_read_memory(c, source, destination, length);
if (r != 0)
return r;
}
- if (HAVE_PROCESS(c))
- return ptrace_copy(PTRACE_PEEKDATA, c->pid, source, destination, length);
-
- return 0;
-}
-
-static int proc_read_buffer(const char *path, struct buffer *b) {
- assert(path);
- assert(b);
-
- if (b->data)
- return 0;
-
- return read_full_file(path, (void**) &b->data, &b->size);
-}
-
-static int proc_read_pid_buffer(pid_t pid, const char *field, struct buffer *b) {
- char *p;
- int r;
-
- assert(pid > 0);
- assert(field);
- assert(b);
-
- if (asprintf(&p, "/proc/%lu/%s", (unsigned long) pid, field) < 0)
- return -ENOMEM;
-
- r = proc_read_buffer(p, b);
- free(p);
-
- return r;
-}
-
-static int proc_readlink_pid_buffer(pid_t pid, const char *field, struct buffer *b) {
- char path[PATH_MAX];
- char *p;
- int r;
-
- assert(pid > 0);
- assert(b);
-
- if (b->data)
- return 0;
-
- if (asprintf(&p, "/proc/%lu/%s", (unsigned long) pid, field) < 0)
- return -ENOMEM;
-
- r = readlink(p, path, sizeof(path));
- free(p);
-
- if (r == 0)
- return 0;
- if (r < 0)
- return -errno;
- if (r == sizeof(path))
- return -E2BIG;
-
- p = memdup(path, r);
- if (!p)
- return -ENOMEM;
-
- b->data = p;
- b->size = r;
-
- return 0;
-}
-
-static int minidump_read_header(struct context *c) {
- ssize_t l;
-
- assert(c);
- assert(HAVE_MINIDUMP(c));
-
- l = pread(c->minidump_fd, &c->minidump_header, sizeof(c->minidump_header), 0);
- if (l < 0)
- return -errno;
- if (l != sizeof(c->minidump_header))
- return -EIO;
+ if (CONTEXT_HAVE_PROCESS(c)) {
+ r = process_read_memory(c, source, destination, length);
- if (c->minidump_header.signature != htole32(0x504d444d))
- return -EINVAL;
-
- c->have_minidump_header = true;
+ if (r != 0)
+ return r;
+ }
return 0;
}
-static int add_thread(struct context *c, struct thread_info *i) {
+int context_add_thread(struct context *c, struct thread_info *i) {
unsigned j;
assert(c);
@@ -532,335 +104,11 @@ static int add_thread(struct context *c, struct thread_info *i) {
(unsigned long) i->tid,
(unsigned long) i->stack_pointer,
(unsigned long) i->instruction_pointer);
- return 0;
-}
-
-static int read_thread_info_ptrace(struct context *c, pid_t tid, struct thread_info *i) {
- int r;
- struct iovec iovec;
-
- assert(c);
- assert(HAVE_PROCESS(c));
- assert(tid > 0);
- assert(i);
-
- memset(i, 0, sizeof(*i));
-
- i->tid = tid;
-
- r = ptrace_copy(PTRACE_PEEKUSER, tid, 0, &i->user, sizeof(i->user));
- if (r < 0)
- return r;
-
- r = ptrace(PTRACE_GETSIGINFO, tid, NULL, &i->siginfo, sizeof(i->siginfo));
- if (r < 0)
- return r;
-
- /* Note: Asking the kernel for NT_PRSTATUS will actually give
- * us only the regs, not the full prstatus. The kernel is a
- * bit surprising sometimes. */
- iovec.iov_base = &i->regs;
- iovec.iov_len = sizeof(i->regs);
- r = ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
- if (r < 0)
- return r;
- if (iovec.iov_len != sizeof(i->regs))
- return -EIO;
-
- iovec.iov_base = &i->fpregs;
- iovec.iov_len = sizeof(i->fpregs);
- r = ptrace(PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
- if (r < 0)
- return r;
- if (iovec.iov_len != sizeof(i->fpregs))
- return -EIO;
-
-#ifdef __i386
- iovec.iov_base = &i->fpxregs;
- iovec.iov_len = sizeof(i->fpxregs);
- r = ptrace(PTRACE_GETREGSET, tid, NT_PRXFPREG, &iovec);
- if (r < 0)
- return r;
- if (iovec.iov_len != sizeof(i->fpxregs))
- return -EIO;
-#endif
-
- i->have_siginfo = i->have_user = true;
-
- return 0;
-}
-
-static int proc_read_threads(struct context *c) {
- DIR *d = NULL;
- int r;
-
- assert(c);
- assert(HAVE_PROCESS(c));
-
- r = threads_begin(c->pid, &d);
- if (r < 0)
- return r;
-
- for (;;) {
- pid_t tid;
- struct thread_info i;
-
- r = threads_next(d, &tid);
- if (r < 0)
- goto finish;
-
- if (r == 0)
- break;
-
- r = read_thread_info_ptrace(c, tid, &i);
- if (r < 0)
- goto finish;
-
- r = add_thread(c, &i);
- if (r < 0)
- goto finish;
-
- /* FIXME: read name */
- }
-
- r = 0;
-
-finish:
- if (d)
- closedir(d);
-
- return r;
-}
-
-static int coredump_read_threads(struct context *c) {
- off_t offset, length;
- int r;
- struct thread_info i;
- unsigned thread_count;
- bool found_prpsinfo = false, found_auxv = false;
- bool found_prstatus = false, found_fpregset = false;
-
- assert(c);
- assert(HAVE_COREDUMP(c));
-
- r = coredump_find_note_segment(c->coredump_fd, &c->coredump_header, &offset, &length);
- if (r < 0)
- return r;
- if (r == 0)
- return -EIO;
-
- thread_count = 0;
-
- while (length > 0) {
- off_t name_offset, descriptor_offset;
- char name[16];
- ssize_t l;
- ElfW(Nhdr) note;
-
- r = coredump_next_note(c->coredump_fd, &offset, &length, &note, &name_offset, &descriptor_offset);
- if (r < 0)
- return r;
-
- if (note.n_namesz >= sizeof(name))
- continue;
-
- if (note.n_descsz >= NOTE_SIZE_MAX)
- return -EBADMSG;
-
- l = pread(c->coredump_fd, name, note.n_namesz, name_offset);
- if (l < 0)
- return -errno;
- if (l != note.n_namesz)
- return -EIO;
-
- name[l] = 0;
-
- fprintf(stderr, "Found note %s, type %u\n", name, note.n_type);
-
- if (strcmp(name, "CORE") == 0 &&
- note.n_type == NT_PRSTATUS) {
-
- if (thread_count > 0) {
- if (!found_prstatus || !found_fpregset)
- return -EIO;
-
- add_thread(c, &i);
- }
-
- memset(&i, 0, sizeof(i));
- thread_count ++;
- found_prstatus = true;
- found_fpregset = false;
-
- if (note.n_descsz != sizeof(i.prstatus))
- return -EIO;
-
- l = pread(c->coredump_fd, &i.prstatus, sizeof(i.prstatus), descriptor_offset);
- if (l < 0)
- return -errno;
- if (l != sizeof(i.prstatus))
- return -EIO;
-
- i.tid = i.prstatus.pr_pid;
- memcpy(&i.regs, i.prstatus.pr_reg, sizeof(i.regs));
-
- } else if (strcmp(name, "CORE") == 0 &&
- note.n_type == NT_PRPSINFO) {
-
- if (found_prpsinfo)
- return -EIO;
-
- found_prpsinfo = true;
-
- if (note.n_descsz != sizeof(c->prpsinfo))
- return -EIO;
-
- l = pread(c->coredump_fd, &c->prpsinfo, sizeof(c->prpsinfo), descriptor_offset);
- if (l < 0)
- return -errno;
- if (l != sizeof(c->prpsinfo))
- return -EIO;
-
- } else if (strcmp(name, "CORE") == 0 &&
- note.n_type == NT_AUXV) {
-
- if (found_auxv)
- return -EIO;
-
- found_auxv = true;
-
- free(c->auxv.data);
- c->auxv.data = malloc(note.n_descsz);
- if (!c->auxv.data)
- return -ENOMEM;
-
- l = pread(c->coredump_fd, c->auxv.data, note.n_descsz, descriptor_offset);
- if (l < 0)
- return -errno;
- if (l != note.n_descsz)
- return -EIO;
-
- c->auxv.size = note.n_descsz;
-
- } else if (strcmp(name, "CORE") == 0 &&
- note.n_type == NT_FPREGSET) {
-
- if (found_fpregset)
- return -EIO;
-
- found_fpregset = true;
-
- if (note.n_descsz != sizeof(i.fpregs))
- return -EIO;
-
- l = pread(c->coredump_fd, &i.fpregs, sizeof(i.fpregs), descriptor_offset);
- if (l < 0)
- return -errno;
- if (l != sizeof(i.fpregs))
- return -EIO;
-#ifdef __i386
- } else if (strcmp(name, "LINUX") == 0 &&
- note.n_type == NT_PRXFPREG) {
-
- if (note.n_descsz != sizeof(i.fpxregs))
- return -EIO;
-
- l = pread(c->fd, &i.fpxregs, sizeof(i.fpxregs), descriptor_offset);
- if (l < 0)
- return -errno;
- if (l != sizeof(i.fpxregs))
- return -EIO;
-#endif
- } else if (strcmp(name, "LENNART") == 0) {
- struct buffer *b;
-
- switch (note.n_type) {
-
- case MINIDUMP_LINUX_MAPS:
- b = &c->proc_maps;
- break;
- case MINIDUMP_LINUX_PROC_STATUS:
- b = &c->proc_status;
- break;
- case MINIDUMP_LINUX_ENVIRON:
- b = &c->proc_environ;
- break;
- case MINIDUMP_LINUX_CMD_LINE:
- b = &c->proc_cmdline;
- break;
- case MINIDUMP_LINUX_COMM:
- b = &c->proc_comm;
- break;
- case MINIDUMP_LINUX_ATTR_CURRENT:
- b = &c->proc_attr_current;
- break;
- case MINIDUMP_LINUX_EXE:
- b = &c->proc_exe;
- break;
- case MINIDUMP_LINUX_CPU_INFO:
- b = &c->proc_cpuinfo;
- break;
- case MINIDUMP_LINUX_LSB_RELEASE:
- b = &c->lsb_release;
- break;
- case MINIDUMP_LINUX_OS_RELEASE:
- b = &c->os_release;
- break;
- default:
- b = NULL;
- break;
- }
-
- if (b) {
- void *p;
-
- p = malloc(note.n_descsz);
- if (!p)
- return -ENOMEM;
-
- l = pread(c->coredump_fd, p, note.n_descsz, descriptor_offset);
- if (l < 0) {
- free(p);
- return -errno;
- }
- if (l != note.n_descsz) {
- free(p);
- return -EIO;
- }
-
- free(b->data);
- b->data = p;
- b->size = note.n_descsz;
- }
- }
- }
-
- if (thread_count > 0) {
- if (!found_prstatus || !found_fpregset)
- return -EIO;
-
- i.have_prstatus = true;
-
- add_thread(c, &i);
- }
-
- if (!found_prpsinfo || !found_auxv)
- return -EIO;
-
- c->have_prpsinfo = true;
return 0;
}
-static int minidump_read_threads(struct context *c) {
- assert(c);
-
- /* FIXME */
-
- return -ENOTSUP;
-}
-
-static int add_mapping(struct context *c, unsigned long start, unsigned long end, const char *name) {
+int context_add_mapping(struct context *c, unsigned long start, unsigned long end, const char *name) {
unsigned j;
assert(c);
@@ -894,61 +142,7 @@ static int add_mapping(struct context *c, unsigned long start, unsigned long end
return 0;
}
-static int proc_read_maps(struct context *c) {
- char *p;
- FILE *f;
- int r;
-
- assert(c);
- assert(HAVE_PROCESS(c));
-
- if (asprintf(&p, "/proc/%lu/maps", (unsigned long) c->pid) < 0)
- return -ENOMEM;
-
- f = fopen(p, "re");
- free(p);
-
- if (!f)
- return -errno;
-
- while (!feof(f)) {
- int k;
- char line[LINE_MAX];
- unsigned long start, end;
- int j;
-
- if (!fgets(line, sizeof(line), f)) {
- if (ferror(f)) {
- r = -errno;
- goto finish;
- }
-
- break;
- }
-
- line[strcspn(line, "\n\r")] = 0;
-
- k = sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*u %n", &start, &end, &j);
- if (k != 2) {
- r = -EIO;
- break;
- }
-
- r = add_mapping(c, start, end, line[j] == 0 ? NULL : line + j);
- if (r < 0)
- goto finish;
- }
-
- r = 0;
-
-finish:
- if (f)
- fclose(f);
-
- return r;
-}
-
-static struct map_info *find_map_info(struct map_info *m, unsigned n, unsigned long address) {
+struct map_info *context_find_map_info(struct map_info *m, unsigned n, unsigned long address) {
unsigned j;
assert(m);
@@ -967,39 +161,6 @@ static struct map_info *find_map_info(struct map_info *m, unsigned n, unsigned l
return NULL;
}
-static int coredump_read_maps(struct context *c) {
- unsigned long i;
- int r;
-
- assert(c);
- assert(HAVE_COREDUMP(c));
-
- for (i = 0; i < c->coredump_header.e_phnum; i++) {
- ElfW(Phdr) segment;
-
- r = coredump_read_segment_header(c->coredump_fd, &c->coredump_header, i, &segment);
- if (r < 0)
- return r;
-
- if (segment.p_type != PT_LOAD)
- continue;
-
- r = add_mapping(c, segment.p_vaddr, segment.p_vaddr+segment.p_filesz, NULL);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int minidump_read_maps(struct context *c) {
- assert(c);
-
- /* FIXME */
-
- return -ENOTSUP;
-}
-
static int pick_maps(struct context *c) {
unsigned i;
@@ -1155,7 +316,7 @@ static int mask_maps(struct context *c) {
return 0;
}
-static int reserve_bytes(struct context *c, size_t bytes, void **ptr, size_t *offset) {
+int context_reserve_bytes(struct context *c, size_t bytes, void **ptr, size_t *offset) {
assert(c);
if (c->output_size + bytes > c->output_allocated) {
@@ -1183,14 +344,14 @@ static int reserve_bytes(struct context *c, size_t bytes, void **ptr, size_t *of
return 0;
}
-static int append_bytes(struct context *c, const void *data, size_t bytes, size_t *offset) {
+int context_append_bytes(struct context *c, const void *data, size_t bytes, size_t *offset) {
void *p;
int r;
assert(c);
assert(data || bytes <= 0);
- r = reserve_bytes(c, bytes, &p, offset);
+ r = context_reserve_bytes(c, bytes, &p, offset);
if (r < 0)
return r;
@@ -1200,13 +361,13 @@ static int append_bytes(struct context *c, const void *data, size_t bytes, size_
return r;
}
-static int null_bytes(struct context *c, size_t bytes, size_t *offset) {
+int context_null_bytes(struct context *c, size_t bytes, size_t *offset) {
void *p;
int r;
assert(c);
- r = reserve_bytes(c, bytes, &p, offset);
+ r = context_reserve_bytes(c, bytes, &p, offset);
if (r < 0)
return r;
@@ -1216,88 +377,7 @@ static int null_bytes(struct context *c, size_t bytes, size_t *offset) {
return r;
}
-static int minidump_write_string(struct context *c, const char *s, size_t *offset) {
- size_t n, l;
- struct minidump_string h;
- unsigned i;
- int r;
- void *p;
-
- assert(c);
- assert(s);
-
- l = strlen(s);
- n = offsetof(struct minidump_string, buffer) + l*2;
-
- r = reserve_bytes(c, n, &p, offset);
- if (r < 0)
- return r;
-
- memset(&h, 0, sizeof(h));
- h.length = htole32(l*2);
- memcpy(p, &h, offsetof(struct minidump_string, buffer));
-
- for (i = 0; i < l; i++) {
- uint16_t le;
-
- /* We just care about ASCII, so the conversion to UTF16 is trivial */
-
- le = htole16(s[i]);
- memcpy((uint8_t*) p + offsetof(struct minidump_string, buffer) + (2 * i), &le, 2);
-
- /* FIXME: We should have proper UTF8 → UTF16 conversion here */
- }
-
- return 0;
-}
-
-static int minidump_append_directory(struct context *c, uint32_t stream_type, size_t offset, size_t size) {
- uint32_t i;
-
- assert(c);
-
- i = c->minidump_n_streams++;
- assert(i < MINIDUMP_STREAMS_MAX);
-
- c->minidump_directory[i].stream_type = htole32(stream_type);
- c->minidump_directory[i].location.data_size = htole32((uint32_t) size);
- c->minidump_directory[i].location.rva = htole32((uint32_t) offset);
-
- fprintf(stderr, "Appending directory entry type=0x%x offset=%lu size=%lu\n", stream_type, (unsigned long) offset, (unsigned long) size);
-
- return 0;
-}
-
-static int minidump_write_blob_stream(struct context *c, uint32_t stream_type, const void *buffer, size_t size) {
- int r;
- size_t offset;
-
- assert(c);
- assert(buffer);
- assert(size > 0);
-
- r = append_bytes(c, buffer, size, &offset);
- if (r < 0)
- return r;
-
- r = minidump_append_directory(c, stream_type, offset, size);
- if (r < 0)
- return r;
-
- return r;
-}
-
-static int minidump_write_buffer_stream(struct context *c, uint32_t stream_type, const struct buffer *buffer) {
- assert(c);
- assert(buffer);
-
- if (!buffer->data)
- return 0;
-
- return minidump_write_blob_stream(c, stream_type, buffer->data, buffer->size);
-}
-
-static int append_concat_string(struct context *c, size_t *offset, size_t *size, ...) {
+int context_append_concat_string(struct context *c, size_t *offset, size_t *size, ...) {
va_list ap;
bool first = true;
size_t sum = 0, o = 0;
@@ -1316,7 +396,7 @@ static int append_concat_string(struct context *c, size_t *offset, size_t *size,
break;
l = strlen(p);
- r = append_bytes(c, p, l, first ? &o : NULL);
+ r = context_append_bytes(c, p, l, first ? &o : NULL);
if (r < 0)
goto finish;
@@ -1324,7 +404,7 @@ static int append_concat_string(struct context *c, size_t *offset, size_t *size,
first = false;
}
- r = append_bytes(c, "", 1, first ? &o : NULL);
+ r = context_append_bytes(c, "", 1, first ? &o : NULL);
if (r < 0)
goto finish;
@@ -1342,745 +422,6 @@ finish:
return r;
}
-static int minidump_write_system_info_stream(struct context *c) {
- struct minidump_system_info i;
- long l;
- struct utsname u;
- int r;
- size_t offset;
-
- assert(c);
-
- memset(&i, 0, sizeof(i));
-
- i.platform_id = htole32(MINIDUMP_PLATFORM_LINUX);
-
-#if defined(__i386)
- i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_INTEL);
-#elif defined(__mips__)
- i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_MIPS);
-#elif defined(__ppc__)
- i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_PPC);
-#elif defined (__arm__)
- i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_ARM);
-#elif defined (__ia64__)
- i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_IA64);
-#elif defined (__x86_64)
- i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_AMD64);
-#elif defined (__sparc__)
- i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_SPARC);
-#else
-#error "I need porting"
-#endif
-
- l = sysconf(_SC_NPROCESSORS_ONLN);
- i.number_of_processors = l <= 0 ? 1 : l;
-
- r = uname(&u);
- if (r < 0)
- return -errno;
-
- r = append_concat_string(c,
- &offset, NULL,
- u.sysname, " ",
- u.release, " ",
- u.version, " ",
- u.machine, " ",
- NULL);
- if (r < 0)
- return r;
-
- i.csd_version_rva = htole32((uint32_t) offset);
-
- /* FIXME: Breakpad fills these one in too, and we should as well, based on CPUID */
- /* FIXME: i.processor_level = "cpu family"; */
- /* FIXME: i.processor_revision = "model" << 8 | "stepping"; */
- /* FIXME: i.cpu.x86_cpu_info.vendor_id = "vendor_id"; */
-
- /* FIXME: On top of that we probably should fill in these as well: */
- /* FIXME: i.major_version = 3 */
- /* FIXME: i.minor_version = 3*/
- /* FIXME: i.build_number = 1 */
- /* FIXME: i.cpu.x86_cpu_info = CPUID... */
-
- return minidump_write_blob_stream(c, MINIDUMP_SYSTEM_INFO_STREAM, &i, sizeof(i));
-}
-
-#ifdef __x86_64
-#define minidump_context minidump_context_amd64
-
-static void minidump_fill_context(struct minidump_context_amd64 *context, struct thread_info *t) {
- assert(context);
- assert(t);
-
- context->context_flags = MINIDUMP_CONTEXT_AMD64_FULL|MINIDUMP_CONTEXT_AMD64_SEGMENTS;
-
- context->cs = t->regs.cs;
- context->ds = t->regs.ds;
- context->es = t->regs.es;
- context->fs = t->regs.fs;
- context->gs = t->regs.gs;
- context->ss = t->regs.ss;
- context->eflags = t->regs.eflags;
- if (t->have_user) {
- context->dr0 = t->user.u_debugreg[0];
- context->dr1 = t->user.u_debugreg[1];
- context->dr2 = t->user.u_debugreg[2];
- context->dr3 = t->user.u_debugreg[3];
- context->dr6 = t->user.u_debugreg[6];
- context->dr7 = t->user.u_debugreg[7];
- }
- context->rax = t->regs.rax;
- context->rcx = t->regs.rcx;
- context->rdx = t->regs.rdx;
- context->rbx = t->regs.rbx;
- context->rsp = t->regs.rsp;
- context->rbp = t->regs.rbp;
- context->rsi = t->regs.rsi;
- context->rdi = t->regs.rdi;
- context->r8 = t->regs.r8;
- context->r9 = t->regs.r9;
- context->r10 = t->regs.r10;
- context->r11 = t->regs.r11;
- context->r12 = t->regs.r12;
- context->r13 = t->regs.r13;
- context->r14 = t->regs.r14;
- context->r15 = t->regs.r15;
- context->rip = t->regs.rip;
-
- context->flt_save.control_word = t->fpregs.cwd;
- context->flt_save.status_word = t->fpregs.swd;
- context->flt_save.tag_word = t->fpregs.ftw;
- context->flt_save.error_opcode = t->fpregs.fop;
- context->flt_save.error_offset = t->fpregs.rip;
- context->flt_save.data_offset = t->fpregs.rdp;
- context->flt_save.mx_csr = t->fpregs.mxcsr;
- context->flt_save.mx_csr_mask = t->fpregs.mxcr_mask;
- memcpy(&context->flt_save.float_registers, &t->fpregs.st_space, 8 * 16);
- memcpy(&context->flt_save.xmm_registers, &t->fpregs.xmm_space, 16 * 16);
-}
-#else
-#error "I need porting"
-#endif
-
-static int minidump_write_thread_list_stream(struct context *c) {
- struct minidump_thread_list *h;
- unsigned i;
- size_t l;
- int r;
-
- l = offsetof(struct minidump_thread_list, threads) +
- sizeof(struct minidump_thread) * c->n_threads;
- h = alloca(l);
- memset(h, 0, l);
- h->number_of_threads = htole32(c->n_threads);
-
- for (i = 0; i < c->n_threads; i++) {
- struct thread_info *a;
- struct minidump_thread *b;
- size_t offset;
- struct minidump_context context;
- struct map_info *m;
-
- a = c->threads + i;
- b = h->threads + i;
-
- memset(&context, 0, sizeof(context));
- minidump_fill_context(&context, a);
- r = append_bytes(c, &context, sizeof(context), &offset);
- if (r < 0)
- return r;
-
- memset(b, 0, sizeof(*b));
- b->thread_id = htole32(a->tid);
- b->thread_context.rva = htole32(offset);
- b->thread_context.data_size = htole32(sizeof(context));
-
- m = find_map_info(c->write_maps, c->n_write_maps, a->stack_pointer);
- if (m) {
- b->stack.start_of_memory_range = htole64(m->extent.address);
- b->stack.memory.data_size = htole32(m->extent.size);
- b->stack.memory.rva = htole32(m->minidump_offset);
- }
-
- a->minidump_offset = offset;
- }
-
- return minidump_write_blob_stream(c, MINIDUMP_THREAD_LIST_STREAM, h, l);
-}
-
-static int minidump_write_module_list_stream(struct context *c) {
- struct minidump_module_list *h;
- unsigned i;
- size_t l;
- int r;
-
- assert(c);
-
- l = offsetof(struct minidump_module_list, modules) +
- sizeof(struct minidump_module) * c->n_maps;
- h = alloca(l);
- memset(h, 0, l);
- h->number_of_modules = htole32(c->n_maps);
-
- for (i = 0; i < c->n_maps; i++) {
- struct map_info *a;
- struct minidump_module *b;
- size_t offset;
-
- a = c->maps + i;
- b = h->modules + i;
-
- memset(b, 0, sizeof(*b));
- b->base_of_image = htole64(a->extent.address);
- b->size_of_image = htole32(a->extent.size);
-
- if (a->name) {
- r = minidump_write_string(c, a->name, &offset);
- if (r < 0)
- return r;
-
- b->module_name_rva = htole32(offset);
- }
-
- /* FIXME: we should fill in a lot more here */
- }
-
- return minidump_write_blob_stream(c, MINIDUMP_MODULE_LIST_STREAM, h, l);
-}
-
-static int minidump_write_memory_list_stream(struct context *c) {
- struct minidump_memory_list *h;
- unsigned i;
- size_t l;
- int r;
-
- assert(c);
-
- l = offsetof(struct minidump_memory_list, memory_ranges) +
- sizeof(struct minidump_memory_descriptor) * c->n_write_maps;
- h = alloca(l);
- memset(h, 0, l);
- h->number_of_memory_ranges = htole32(c->n_write_maps);
-
- for (i = 0; i < c->n_write_maps; i++) {
- struct map_info *a;
- struct minidump_memory_descriptor *b;
- size_t offset;
- void *p;
-
- a = c->write_maps + i;
- b = h->memory_ranges + i;
-
- r = reserve_bytes(c, a->extent.size, &p, &offset);
- if (r < 0)
- return r;
-
- r = read_memory(c, a->extent.address, p, a->extent.size);
- if (r < 0)
- return r;
-
- memset(b, 0, sizeof(*b));
- b->start_of_memory_range = htole64(a->extent.address);
- b->memory.rva = htole32(offset);
- b->memory.data_size = htole32(a->extent.size);
-
- a->minidump_offset = offset;
- }
-
- return minidump_write_blob_stream(c, MINIDUMP_MEMORY_LIST_STREAM, h, l);
-}
-
-static int minidump_write_exception_stream(struct context *c) {
- struct minidump_exception_stream h;
- struct thread_info *t;
-
- assert(c);
- assert(c->n_threads > 0);
-
- t = c->threads+0;
-
- memset(&h, 0, sizeof(h));
- h.thread_id = htole32(t->tid);
-
- if (t->have_prstatus)
- h.exception_record.exception_code = htole32(t->prstatus.pr_info.si_signo);
- else if (t->have_siginfo) {
- h.exception_record.exception_code = htole32(t->siginfo.si_signo);
- h.exception_record.exception_address = htole64((uint64_t) t->siginfo.si_addr);
- }
-
- h.thread_context.data_size = htole32(sizeof(struct minidump_context));
- h.thread_context.rva = htole32(t->minidump_offset);
-
- return minidump_write_blob_stream(c, MINIDUMP_EXCEPTION_STREAM, &h, sizeof(h));
-}
-
-static int minidump_write_directory(struct context *c) {
- size_t offset;
- struct minidump_header *h;
- int r;
-
- assert(c);
-
- r = append_bytes(c, c->minidump_directory, sizeof(struct minidump_directory) * c->minidump_n_streams, &offset);
- if (r < 0)
- return r;
-
- /* The beginning of the minidump is definitely aligned, so we
- * access it directly and patch in the directory data. */
- h = c->output;
- h->number_of_streams = htole32(c->minidump_n_streams);
- h->stream_directory_rva = htole32((uint32_t) offset);
-
- return 0;
-}
-
-static int write_minidump(struct context *c) {
- struct minidump_header h;
- int r;
-
- assert(c);
-
- memset(&h, 0, sizeof(h));
- h.signature = htole32(0x504d444d);
- h.version = htole32(0x0000a793);
- h.time_date_stamp = htole32(time(NULL));
-
- r = append_bytes(c, &h, sizeof(h), NULL);
- if (r < 0)
- return r;
-
- r = minidump_write_memory_list_stream(c);
- if (r < 0)
- return r;
-
- r = minidump_write_thread_list_stream(c);
- if (r < 0)
- return r;
-
- r = minidump_write_module_list_stream(c);
- if (r < 0)
- return r;
-
- r = minidump_write_exception_stream(c);
- if (r < 0)
- return r;
-
- r = minidump_write_system_info_stream(c);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_MAPS, &c->proc_maps);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_PROC_STATUS, &c->proc_status);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_ENVIRON, &c->proc_environ);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_CMD_LINE, &c->proc_cmdline);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_COMM, &c->proc_comm);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_ATTR_CURRENT, &c->proc_attr_current);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_EXE, &c->proc_exe);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_CPU_INFO, &c->proc_cpuinfo);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_LSB_RELEASE, &c->lsb_release);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_OS_RELEASE, &c->os_release);
- if (r < 0)
- return r;
-
- r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_AUXV, &c->auxv);
- if (r < 0)
- return r;
-
- if (c->have_prpsinfo) {
- r = minidump_write_blob_stream(c, MINIDUMP_LINUX_PRPSINFO, &c->prpsinfo, sizeof(c->prpsinfo));
- if (r < 0)
- return r;
- }
-
- if (c->have_coredump_header) {
- r = minidump_write_blob_stream(c, MINIDUMP_LINUX_CORE_EHDR, &c->coredump_header, sizeof(c->coredump_header));
- if (r < 0)
- return r;
- }
-
- /* We probably should find __abort_msg and __glib_assert_msg
- * and include it here */
-
- r = minidump_write_directory(c);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int minicore_append_ph(struct context *c, const ElfW(Phdr) *ph) {
- uint32_t i;
-
- assert(c);
-
- i = c->minicore_n_phs++;
- assert(i < c->n_write_maps + 1);
-
- memcpy(c->minicore_phs+i, ph, sizeof(*ph));
-
- fprintf(stderr, "Appending segment type=0x%x size=%lu\n",
- (unsigned) ph->p_type,
- (unsigned long) ph->p_filesz);
-
- return 0;
-}
-
-static int minicore_write_maps(struct context *c) {
- unsigned i;
- int r;
-
- assert(c);
-
- for (i = 0; i < c->n_write_maps; i++) {
- struct map_info *a;
- ElfW(Phdr) ph;
- void *p;
- size_t offset;
-
- a = c->write_maps + i;
-
- r = reserve_bytes(c, a->extent.size, &p, &offset);
- if (r < 0)
- return r;
-
- r = read_memory(c, a->extent.address, p, a->extent.size);
- if (r < 0)
- return r;
-
- memset(&ph, 0, sizeof(ph));
- ph.p_type = PT_LOAD;
- ph.p_offset = offset;
- ph.p_filesz = a->extent.size;
- ph.p_memsz = a->extent.size;
- ph.p_vaddr = a->extent.address;
- ph.p_flags = PF_W|PF_R|PF_X;
-
- r = minicore_append_ph(c, &ph);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int minicore_write_one_note(struct context *c, const char *name, ElfW(Word) type, const void *data, size_t length) {
- int r;
- ElfW(Nhdr) nh;
-
- assert(c);
- assert(name);
- assert(data || length <= 0);
-
- if (length <= 0)
- return 0;
-
- memset(&nh, 0, sizeof(nh));
- nh.n_namesz = strlen(name);
- nh.n_descsz = length;
- nh.n_type = type;
-
- r = append_bytes(c, &nh, sizeof(nh), NULL);
- if (r < 0)
- return r;
-
- r = append_bytes(c, name, nh.n_namesz, NULL);
- if (r < 0)
- return r;
-
- r = null_bytes(c, roundup(nh.n_namesz, sizeof(int)) - nh.n_namesz, NULL);
- if (r < 0)
- return r;
-
- r = append_bytes(c, data, nh.n_descsz, NULL);
- if (r < 0)
- return r;
-
- r = null_bytes(c, roundup(nh.n_descsz, sizeof(int)) - nh.n_descsz, NULL);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int minicore_write_note_prstatus(struct context *c, struct thread_info *i) {
- struct elf_prstatus synthetic;
- assert(c);
- assert(i);
-
- if (i->have_prstatus)
- return minicore_write_one_note(c, "CORE", NT_PRSTATUS, &i->prstatus, sizeof(i->prstatus));
-
- memset(&synthetic, 0, sizeof(synthetic));
- synthetic.pr_pid = i->tid;
- memcpy(&synthetic.pr_reg, &i->regs, sizeof(i->regs));
-
- return minicore_write_one_note(c, "CORE", NT_PRSTATUS, &synthetic, sizeof(synthetic));
-}
-
-static int minicore_write_note_prpsinfo(struct context *c) {
- struct elf_prpsinfo synthetic;
-
- assert(c);
-
- if (c->have_prpsinfo)
- return minicore_write_one_note(c, "CORE", NT_PRPSINFO, &c->prpsinfo, sizeof(c->prpsinfo));
-
- memset(&synthetic, 0, sizeof(synthetic));
- synthetic.pr_pid = c->pid;
- if (c->proc_comm.data)
- memcpy(synthetic.pr_fname, c->proc_comm.data, MIN(sizeof(synthetic.pr_fname), sizeof(c->proc_comm.size)));
-
- return minicore_write_one_note(c, "CORE", NT_PRPSINFO, &synthetic, sizeof(synthetic));
-}
-
-static int minicore_write_note_auxv(struct context *c) {
- assert(c);
-
- if (c->auxv.data)
- return minicore_write_one_note(c, "CORE", NT_AUXV, c->auxv.data, c->auxv.size);
-
- return 0;
-}
-
-static int minicore_write_note_fpregset(struct context *c, struct thread_info *i) {
- assert(c);
-
- return minicore_write_one_note(c, "CORE", NT_FPREGSET, &i->fpregs, sizeof(i->fpregs));
-}
-
-#ifdef __i386
-static int minicore_write_note_fpregset(struct context *c, struct thread_info *i) {
- assert(c);
-
- return minicore_write_one_note(c, "LINUX", NT_PRXFPREG, &i->fpxregs, sizeof(i->fpxregs));
-}
-#endif
-
-static int minicore_write_notes_for_thread(struct context *c, unsigned i) {
- int r;
-
- r = minicore_write_note_prstatus(c, c->threads+i);
- if (r < 0)
- return r;
-
- if (i == 0) {
- /* The data for the process is written in the middle
- * of the data of thread #1 */
- r = minicore_write_note_prpsinfo(c);
- if (r < 0)
- return r;
-
- r = minicore_write_note_auxv(c);
- if (r < 0)
- return r;
- }
- r = minicore_write_note_fpregset(c, c->threads+i);
- if (r < 0)
- return r;
-
-#ifdef __i386
- r = minicore_write_note_prxfpreg(c, c->threads+i);
- if (r < 0)
- return r;
-#endif
-
- return 0;
-}
-
-static int minicore_write_meta_notes(struct context *c) {
- int r;
-
- assert(c);
-
- /* We use the same type identifiers as the minidump logic */
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_MAPS, c->proc_maps.data, c->proc_maps.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_PROC_STATUS, c->proc_status.data, c->proc_status.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_ENVIRON, c->proc_environ.data, c->proc_environ.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_CMD_LINE, c->proc_cmdline.data, c->proc_cmdline.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_COMM, c->proc_comm.data, c->proc_comm.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_ATTR_CURRENT, c->proc_attr_current.data, c->proc_attr_current.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_EXE, c->proc_exe.data, c->proc_exe.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_CPU_INFO, c->proc_cpuinfo.data, c->proc_cpuinfo.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_LSB_RELEASE, c->lsb_release.data, c->lsb_release.size);
- if (r < 0)
- return r;
-
- r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_OS_RELEASE, c->os_release.data, c->os_release.size);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int minicore_write_notes(struct context *c) {
- ElfW(Phdr) ph;
- unsigned i;
- size_t offset;
- int r;
-
- assert(c);
- assert(c->n_threads > 0);
-
- offset = c->output_size;
-
- for (i = 0; i < c->n_threads; i++) {
- r = minicore_write_notes_for_thread(c, i);
- if (r < 0)
- return r;
- }
-
- r = minicore_write_meta_notes(c);
- if (r < 0)
- return r;
-
- memset(&ph, 0, sizeof(ph));
- ph.p_type = PT_NOTE;
- ph.p_offset = offset;
- ph.p_filesz = c->output_size - offset;
-
- r = minicore_append_ph(c, &ph);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int minicore_write_phs(struct context *c) {
- size_t offset;
- ElfW(Ehdr) *h;
- int r;
-
- assert(c);
-
- r = append_bytes(c, c->minicore_phs, sizeof(ElfW(Phdr)) * c->minicore_n_phs, &offset);
- if (r < 0)
- return r;
-
- h = c->output;
- h->e_phnum = c->minicore_n_phs;
- h->e_phoff = offset;
-
- return 0;
-}
-
-static int write_minicore(struct context *c) {
- ElfW(Ehdr) h;
- int r;
-
- assert(c);
-
- memset(&h, 0, sizeof(h));
- memcpy(h.e_ident, ELFMAG, SELFMAG);
- h.e_type = ET_CORE;
- h.e_ehsize = sizeof(ElfW(Ehdr));
- h.e_phentsize = sizeof(ElfW(Phdr));
- h.e_shentsize = sizeof(ElfW(Shdr));
-
-#if __WORDSIZE == 32
- h.e_ident[EI_CLASS] = ELFCLASS32;
-#elif __WORDSIZE == 64
- h.e_ident[EI_CLASS] = ELFCLASS64;
-#else
-#error "Unknown word size."
-#endif
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
- h.e_ident[EI_DATA] = ELFDATA2LSB;
-#elif __BYTE_ORDER == __BIG_ENDIAN
- h.e_ident[EI_DATA] = ELFDATA2MSB;
-#else
-#error "Unknown endianess."
-#endif
- h.e_ident[EI_VERSION] = EV_CURRENT;
- h.e_ident[EI_OSABI] = ELFOSABI_NONE;
-
-#if __i386
- h.e_machine = EM_386;
-#elif __x86_64
- h.e_machine = EM_X86_64;
-#else
-#error "Unknown machine."
-#endif
- h.e_version = EV_CURRENT;
-
- r = append_bytes(c, &h, sizeof(h), NULL);
- if (r < 0)
- return r;
-
- /* Allocate an array for one segment per map plus one NOTE segment */
- c->minicore_phs = malloc(sizeof(ElfW(Phdr)) * (1 + c->n_write_maps));
- if (!c->minicore_phs)
- return -ENOMEM;
-
- r = minicore_write_notes(c);
- if (r < 0)
- return r;
-
- r = minicore_write_maps(c);
- if (r < 0)
- return r;
-
- r = minicore_write_phs(c);
- if (r < 0)
- return r;
-
- return 0;
-}
-
static int show_buffer(FILE *f, const char *title, struct buffer *b) {
char *p;
@@ -2149,7 +490,7 @@ static void thread_show(FILE *f, unsigned i, struct thread_info *t) {
fputc('\n', f);
}
-static void context_show(FILE *f, struct context *c) {
+void context_show(FILE *f, struct context *c) {
unsigned i;
unsigned long sum;
@@ -2187,38 +528,6 @@ static void context_show(FILE *f, struct context *c) {
show_buffer(f, "/etc/os-release", &c->os_release);
}
-static int proc_load_fields(struct context *c) {
- int r;
-
- assert(c);
- assert(c->pid >= 0);
-
- /* These ones matter */
- r = proc_read_pid_buffer(c->pid, "maps", &c->proc_maps);
- if (r < 0)
- return r;
-
- r = proc_read_pid_buffer(c->pid, "auxv", &c->auxv);
- if (r < 0)
- return r;
-
- /* The following ones don't really matter, so don't check return values */
- proc_read_pid_buffer(c->pid, "status", &c->proc_status);
- proc_read_pid_buffer(c->pid, "cmdline", &c->proc_cmdline);
- proc_read_pid_buffer(c->pid, "environ", &c->proc_environ);
- proc_read_pid_buffer(c->pid, "comm", &c->proc_comm);
- proc_read_pid_buffer(c->pid, "attr/current", &c->proc_attr_current);
- proc_readlink_pid_buffer(c->pid, "exe", &c->proc_exe);
-
- proc_read_buffer("/proc/cpuinfo", &c->proc_cpuinfo);
- /* This is an Ubuntuism, but Google is doing this, hence let's stay compatible here */
- proc_read_buffer("/etc/lsb-release", &c->lsb_release);
- /* It's much nicer to write /etc/os-release instead, which is more widely supported */
- proc_read_buffer("/etc/os-release", &c->os_release);
-
- return 0;
-}
-
static int map_compare(const void *_a, const void *_b) {
const struct map_info *a = _a, *b = _b;
@@ -2231,12 +540,12 @@ static int map_compare(const void *_a, const void *_b) {
return 0;
}
-static int context_load(struct context *c) {
+int context_load(struct context *c) {
int r;
assert(c);
- if (HAVE_MINIDUMP(c)) {
+ if (CONTEXT_HAVE_MINIDUMP(c)) {
r = minidump_read_header(c);
if (r < 0)
return r;
@@ -2250,7 +559,7 @@ static int context_load(struct context *c) {
return r;
}
- if (HAVE_COREDUMP(c)) {
+ if (CONTEXT_HAVE_COREDUMP(c)) {
r = coredump_read_header(c->coredump_fd, &c->coredump_header);
if (r < 0)
return r;
@@ -2266,23 +575,23 @@ static int context_load(struct context *c) {
return r;
}
- if (HAVE_PROCESS(c)) {
+ if (CONTEXT_HAVE_PROCESS(c)) {
if (kill(c->pid, 0) < 0)
return -errno;
- r = attach_threads(c);
+ r = process_attach(c);
if (r < 0)
return r;
- r = proc_read_maps(c);
+ r = process_read_maps(c);
if (r < 0)
return r;
- r = proc_read_threads(c);
+ r = process_read_threads(c);
if (r < 0)
return r;
- r = proc_load_fields(c);
+ r = process_read_fields(c);
if (r < 0)
return r;
}
@@ -2305,13 +614,13 @@ static int context_load(struct context *c) {
return r;
}
-static void context_release(struct context *c) {
+void context_release(struct context *c) {
unsigned j;
assert(c);
- if (HAVE_PROCESS(c))
- detach_threads(c);
+ if (CONTEXT_HAVE_PROCESS(c))
+ process_detach(c);
free(c->auxv.data);
for (j = 0; j < c->n_maps; j++) {
@@ -2377,11 +686,11 @@ finish:
}
int minidump_make(pid_t pid, int fd, void **minidump, size_t *size) {
- return make(pid, fd, minidump, size, write_minidump);
+ return make(pid, fd, minidump, size, minidump_write);
}
int minicore_make(pid_t pid, int fd, void **minicore, size_t *size) {
- return make(pid, fd, minicore, size, write_minicore);
+ return make(pid, fd, minicore, size, minicore_write);
}
static int show(FILE *f, pid_t pid, int coredump_fd, int minidump_fd) {
@@ -2440,7 +749,7 @@ int minidump_to_minicore(int minidump_fd, void **output, size_t *output_size) {
if (r < 0)
goto finish;
- r = write_minicore(&c);
+ r = minicore_write(&c);
if (r < 0)
goto finish;
diff --git a/minidump.h b/minidump.h
index 5f04c9d..99bfe76 100644
--- a/minidump.h
+++ b/minidump.h
@@ -1,6 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
#ifndef foominidumphfoo
#define foominidumphfoo
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#include <sys/types.h>
#include <stdio.h>
diff --git a/mkminidump.c b/mkminidump.c
index f509ea0..f2cbfd3 100644
--- a/mkminidump.c
+++ b/mkminidump.c
@@ -1,5 +1,25 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
diff --git a/read-coredump.c b/read-coredump.c
new file mode 100644
index 0000000..7dd9091
--- /dev/null
+++ b/read-coredump.c
@@ -0,0 +1,277 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "coredump-util.h"
+#include "read-coredump.h"
+
+#define NOTE_SIZE_MAX (1024*1024*10)
+
+int coredump_read_threads(struct context *c) {
+ off_t offset, length;
+ int r;
+ struct thread_info i;
+ unsigned thread_count;
+ bool found_prpsinfo = false, found_auxv = false;
+ bool found_prstatus = false, found_fpregset = false;
+
+ assert(c);
+ assert(CONTEXT_HAVE_COREDUMP(c));
+
+ r = coredump_find_note_segment(c->coredump_fd, &c->coredump_header, &offset, &length);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EIO;
+
+ thread_count = 0;
+
+ while (length > 0) {
+ off_t name_offset, descriptor_offset;
+ char name[16];
+ ssize_t l;
+ ElfW(Nhdr) note;
+
+ r = coredump_next_note(c->coredump_fd, &offset, &length, &note, &name_offset, &descriptor_offset);
+ if (r < 0)
+ return r;
+
+ if (note.n_namesz >= sizeof(name))
+ continue;
+
+ if (note.n_descsz >= NOTE_SIZE_MAX)
+ return -EBADMSG;
+
+ l = pread(c->coredump_fd, name, note.n_namesz, name_offset);
+ if (l < 0)
+ return -errno;
+ if (l != note.n_namesz)
+ return -EIO;
+
+ name[l] = 0;
+
+ fprintf(stderr, "Found note %s, type %u\n", name, note.n_type);
+
+ if (strcmp(name, "CORE") == 0 &&
+ note.n_type == NT_PRSTATUS) {
+
+ if (thread_count > 0) {
+ if (!found_prstatus || !found_fpregset)
+ return -EIO;
+
+ context_add_thread(c, &i);
+ }
+
+ memset(&i, 0, sizeof(i));
+ thread_count ++;
+ found_prstatus = true;
+ found_fpregset = false;
+
+ if (note.n_descsz != sizeof(i.prstatus))
+ return -EIO;
+
+ l = pread(c->coredump_fd, &i.prstatus, sizeof(i.prstatus), descriptor_offset);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(i.prstatus))
+ return -EIO;
+
+ i.tid = i.prstatus.pr_pid;
+ memcpy(&i.regs, i.prstatus.pr_reg, sizeof(i.regs));
+
+ } else if (strcmp(name, "CORE") == 0 &&
+ note.n_type == NT_PRPSINFO) {
+
+ if (found_prpsinfo)
+ return -EIO;
+
+ found_prpsinfo = true;
+
+ if (note.n_descsz != sizeof(c->prpsinfo))
+ return -EIO;
+
+ l = pread(c->coredump_fd, &c->prpsinfo, sizeof(c->prpsinfo), descriptor_offset);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(c->prpsinfo))
+ return -EIO;
+
+ } else if (strcmp(name, "CORE") == 0 &&
+ note.n_type == NT_AUXV) {
+
+ if (found_auxv)
+ return -EIO;
+
+ found_auxv = true;
+
+ free(c->auxv.data);
+ c->auxv.data = malloc(note.n_descsz);
+ if (!c->auxv.data)
+ return -ENOMEM;
+
+ l = pread(c->coredump_fd, c->auxv.data, note.n_descsz, descriptor_offset);
+ if (l < 0)
+ return -errno;
+ if (l != note.n_descsz)
+ return -EIO;
+
+ c->auxv.size = note.n_descsz;
+
+ } else if (strcmp(name, "CORE") == 0 &&
+ note.n_type == NT_FPREGSET) {
+
+ if (found_fpregset)
+ return -EIO;
+
+ found_fpregset = true;
+
+ if (note.n_descsz != sizeof(i.fpregs))
+ return -EIO;
+
+ l = pread(c->coredump_fd, &i.fpregs, sizeof(i.fpregs), descriptor_offset);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(i.fpregs))
+ return -EIO;
+#ifdef __i386
+ } else if (strcmp(name, "LINUX") == 0 &&
+ note.n_type == NT_PRXFPREG) {
+
+ if (note.n_descsz != sizeof(i.fpxregs))
+ return -EIO;
+
+ l = pread(c->fd, &i.fpxregs, sizeof(i.fpxregs), descriptor_offset);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(i.fpxregs))
+ return -EIO;
+#endif
+ } else if (strcmp(name, "LENNART") == 0) {
+ struct buffer *b;
+
+ switch (note.n_type) {
+
+ case MINIDUMP_LINUX_MAPS:
+ b = &c->proc_maps;
+ break;
+ case MINIDUMP_LINUX_PROC_STATUS:
+ b = &c->proc_status;
+ break;
+ case MINIDUMP_LINUX_ENVIRON:
+ b = &c->proc_environ;
+ break;
+ case MINIDUMP_LINUX_CMD_LINE:
+ b = &c->proc_cmdline;
+ break;
+ case MINIDUMP_LINUX_COMM:
+ b = &c->proc_comm;
+ break;
+ case MINIDUMP_LINUX_ATTR_CURRENT:
+ b = &c->proc_attr_current;
+ break;
+ case MINIDUMP_LINUX_EXE:
+ b = &c->proc_exe;
+ break;
+ case MINIDUMP_LINUX_CPU_INFO:
+ b = &c->proc_cpuinfo;
+ break;
+ case MINIDUMP_LINUX_LSB_RELEASE:
+ b = &c->lsb_release;
+ break;
+ case MINIDUMP_LINUX_OS_RELEASE:
+ b = &c->os_release;
+ break;
+ default:
+ b = NULL;
+ break;
+ }
+
+ if (b) {
+ void *p;
+
+ p = malloc(note.n_descsz);
+ if (!p)
+ return -ENOMEM;
+
+ l = pread(c->coredump_fd, p, note.n_descsz, descriptor_offset);
+ if (l < 0) {
+ free(p);
+ return -errno;
+ }
+ if (l != note.n_descsz) {
+ free(p);
+ return -EIO;
+ }
+
+ free(b->data);
+ b->data = p;
+ b->size = note.n_descsz;
+ }
+ }
+ }
+
+ if (thread_count > 0) {
+ if (!found_prstatus || !found_fpregset)
+ return -EIO;
+
+ i.have_prstatus = true;
+
+ context_add_thread(c, &i);
+ }
+
+ if (!found_prpsinfo || !found_auxv)
+ return -EIO;
+
+ c->have_prpsinfo = true;
+
+ return 0;
+}
+
+int coredump_read_maps(struct context *c) {
+ unsigned long i;
+ int r;
+
+ assert(c);
+ assert(CONTEXT_HAVE_COREDUMP(c));
+
+ for (i = 0; i < c->coredump_header.e_phnum; i++) {
+ ElfW(Phdr) segment;
+
+ r = coredump_read_segment_header(c->coredump_fd, &c->coredump_header, i, &segment);
+ if (r < 0)
+ return r;
+
+ if (segment.p_type != PT_LOAD)
+ continue;
+
+ r = context_add_mapping(c, segment.p_vaddr, segment.p_vaddr+segment.p_filesz, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/read-coredump.h b/read-coredump.h
new file mode 100644
index 0000000..fdd6c2d
--- /dev/null
+++ b/read-coredump.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooreadcoredumphfoo
+#define fooreadcoredumphfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include "context.h"
+
+int coredump_read_threads(struct context *c);
+int coredump_read_maps(struct context *c);
+
+#endif
diff --git a/read-minidump.c b/read-minidump.c
new file mode 100644
index 0000000..f9a96ac
--- /dev/null
+++ b/read-minidump.c
@@ -0,0 +1,77 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "context.h"
+#include "read-minidump.h"
+
+int minidump_read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
+ assert(c);
+ assert(destination);
+ assert(length > 0);
+ assert(CONTEXT_HAVE_MINIDUMP(c));
+
+ /* FIXME */
+
+ return -ENOTSUP;
+}
+
+int minidump_read_header(struct context *c) {
+ ssize_t l;
+
+ assert(c);
+ assert(CONTEXT_HAVE_MINIDUMP(c));
+
+ l = pread(c->minidump_fd, &c->minidump_header, sizeof(c->minidump_header), 0);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(c->minidump_header))
+ return -EIO;
+
+ if (c->minidump_header.signature != htole32(0x504d444d))
+ return -EINVAL;
+
+ c->have_minidump_header = true;
+
+ return 0;
+}
+
+int minidump_read_threads(struct context *c) {
+ assert(c);
+ assert(CONTEXT_HAVE_MINIDUMP(c));
+
+ /* FIXME */
+
+ return -ENOTSUP;
+}
+
+int minidump_read_maps(struct context *c) {
+ assert(c);
+ assert(CONTEXT_HAVE_MINIDUMP(c));
+
+ /* FIXME */
+
+ return -ENOTSUP;
+}
diff --git a/read-minidump.h b/read-minidump.h
new file mode 100644
index 0000000..dbec445
--- /dev/null
+++ b/read-minidump.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooreadminidumphfoo
+#define fooreadminidumphfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include "context.h"
+
+int minidump_read_header(struct context *c);
+int minidump_read_threads(struct context *c);
+int minidump_read_maps(struct context *c);
+int minidump_read_memory(struct context *c, unsigned long source, void *destination, size_t length);
+
+#endif
diff --git a/read-process.c b/read-process.c
new file mode 100644
index 0000000..cc36fa8
--- /dev/null
+++ b/read-process.c
@@ -0,0 +1,466 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "read-process.h"
+#include "util.h"
+
+static int threads_begin(pid_t pid, DIR **_d) {
+ char *path;
+ DIR *d;
+
+ assert(pid > 0);
+
+ if (asprintf(&path, "/proc/%lu/task", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ d = opendir(path);
+ free(path);
+
+ if (!d)
+ return -errno;
+
+ *_d = d;
+ return 0;
+}
+
+static int threads_next(DIR *d, pid_t *pid) {
+ struct dirent buf, *de;
+ int k;
+ long l;
+ char *p;
+
+ for (;;) {
+ k = readdir_r(d, &buf, &de);
+ if (k != 0)
+ return -k;
+
+ if (!de)
+ return 0;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ if (de->d_type != DT_DIR)
+ continue;
+
+ errno = 0;
+ l = strtol(de->d_name, &p, 10);
+ if (errno != 0)
+ continue;
+
+ if (p && *p != 0)
+ continue;
+
+ if (l <= 0)
+ continue;
+
+ *pid = (pid_t) l;
+ return 1;
+ }
+}
+
+int process_attach(struct context *c) {
+ DIR* d = NULL;
+ int r;
+
+ assert(c);
+ assert(CONTEXT_HAVE_PROCESS(c));
+
+ r = threads_begin(c->pid, &d);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ pid_t tid;
+
+ r = threads_next(d, &tid);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ break;
+
+ if (ptrace(PTRACE_ATTACH, tid, NULL, NULL) < 0) {
+
+ if (errno == ESRCH)
+ continue;
+
+ r = -errno;
+ goto finish;
+ }
+
+ /* Wait until the thread is actually stopped */
+ for (;;) {
+ int status;
+
+ if (waitpid(tid, &status, __WALL) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ r = -errno;
+ goto finish;
+ }
+
+ if (WIFSTOPPED(status))
+ break;
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (d)
+ closedir(d);
+
+ return r;
+}
+
+int process_detach(struct context *c) {
+ DIR* d = NULL;
+ int r;
+
+ assert(c);
+ assert(CONTEXT_HAVE_PROCESS(c));
+
+ r = threads_begin(c->pid, &d);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ pid_t tid;
+
+ r = threads_next(d, &tid);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ break;
+
+ if (ptrace(PTRACE_DETACH, tid, NULL, NULL) < 0) {
+
+ if (errno == ESRCH)
+ continue;
+
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (d)
+ closedir(d);
+
+ return r;
+}
+
+static int ptrace_copy(enum __ptrace_request req, pid_t pid, unsigned long source, void *destination, size_t length) {
+ long l;
+
+ assert(req == PTRACE_PEEKTEXT ||
+ req == PTRACE_PEEKDATA ||
+ req == PTRACE_PEEKUSER);
+
+ assert(pid > 0);
+ assert(destination);
+ assert(length > 0);
+
+ while (length > 0) {
+
+ errno = 0;
+ l = ptrace(req, pid, (void*) source, NULL);
+ if (errno != 0)
+ return -errno;
+
+ memcpy(destination, &l, MIN(length, sizeof(l)));
+
+ if (length <= sizeof(l))
+ break;
+
+ length -= sizeof(l);
+ source += sizeof(l);
+ destination = (uint8_t*) destination + sizeof(l);
+ }
+
+ return 0;
+}
+
+int process_read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
+ assert(c);
+ assert(CONTEXT_HAVE_PROCESS(c));
+
+ return ptrace_copy(PTRACE_PEEKDATA, c->pid, source, destination, length);
+}
+
+static int proc_read_buffer(const char *path, struct buffer *b) {
+ assert(path);
+ assert(b);
+
+ if (b->data)
+ return 0;
+
+ return read_full_file(path, (void**) &b->data, &b->size);
+}
+
+static int proc_read_pid_buffer(pid_t pid, const char *field, struct buffer *b) {
+ char *p;
+ int r;
+
+ assert(pid > 0);
+ assert(field);
+ assert(b);
+
+ if (asprintf(&p, "/proc/%lu/%s", (unsigned long) pid, field) < 0)
+ return -ENOMEM;
+
+ r = proc_read_buffer(p, b);
+ free(p);
+
+ return r;
+}
+
+static int proc_readlink_pid_buffer(pid_t pid, const char *field, struct buffer *b) {
+ char path[PATH_MAX];
+ char *p;
+ int r;
+
+ assert(pid > 0);
+ assert(b);
+
+ if (b->data)
+ return 0;
+
+ if (asprintf(&p, "/proc/%lu/%s", (unsigned long) pid, field) < 0)
+ return -ENOMEM;
+
+ r = readlink(p, path, sizeof(path));
+ free(p);
+
+ if (r == 0)
+ return 0;
+ if (r < 0)
+ return -errno;
+ if (r == sizeof(path))
+ return -E2BIG;
+
+ p = memdup(path, r);
+ if (!p)
+ return -ENOMEM;
+
+ b->data = p;
+ b->size = r;
+
+ return 0;
+}
+
+static int read_thread_info_ptrace(struct context *c, pid_t tid, struct thread_info *i) {
+ int r;
+ struct iovec iovec;
+
+ assert(c);
+ assert(CONTEXT_HAVE_PROCESS(c));
+ assert(tid > 0);
+ assert(i);
+
+ memset(i, 0, sizeof(*i));
+
+ i->tid = tid;
+
+ r = ptrace_copy(PTRACE_PEEKUSER, tid, 0, &i->user, sizeof(i->user));
+ if (r < 0)
+ return r;
+
+ r = ptrace(PTRACE_GETSIGINFO, tid, NULL, &i->siginfo, sizeof(i->siginfo));
+ if (r < 0)
+ return r;
+
+ /* Note: Asking the kernel for NT_PRSTATUS will actually give
+ * us only the regs, not the full prstatus. The kernel is a
+ * bit surprising sometimes. */
+ iovec.iov_base = &i->regs;
+ iovec.iov_len = sizeof(i->regs);
+ r = ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
+ if (r < 0)
+ return r;
+ if (iovec.iov_len != sizeof(i->regs))
+ return -EIO;
+
+ iovec.iov_base = &i->fpregs;
+ iovec.iov_len = sizeof(i->fpregs);
+ r = ptrace(PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+ if (r < 0)
+ return r;
+ if (iovec.iov_len != sizeof(i->fpregs))
+ return -EIO;
+
+#ifdef __i386
+ iovec.iov_base = &i->fpxregs;
+ iovec.iov_len = sizeof(i->fpxregs);
+ r = ptrace(PTRACE_GETREGSET, tid, NT_PRXFPREG, &iovec);
+ if (r < 0)
+ return r;
+ if (iovec.iov_len != sizeof(i->fpxregs))
+ return -EIO;
+#endif
+
+ i->have_siginfo = i->have_user = true;
+
+ return 0;
+}
+
+int process_read_threads(struct context *c) {
+ DIR *d = NULL;
+ int r;
+
+ assert(c);
+ assert(CONTEXT_HAVE_PROCESS(c));
+
+ r = threads_begin(c->pid, &d);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ pid_t tid;
+ struct thread_info i;
+
+ r = threads_next(d, &tid);
+ if (r < 0)
+ goto finish;
+
+ if (r == 0)
+ break;
+
+ r = read_thread_info_ptrace(c, tid, &i);
+ if (r < 0)
+ goto finish;
+
+ r = context_add_thread(c, &i);
+ if (r < 0)
+ goto finish;
+
+ /* FIXME: read name */
+ }
+
+ r = 0;
+
+finish:
+ if (d)
+ closedir(d);
+
+ return r;
+}
+
+int process_read_maps(struct context *c) {
+ char *p;
+ FILE *f;
+ int r;
+
+ assert(c);
+ assert(CONTEXT_HAVE_PROCESS(c));
+
+ if (asprintf(&p, "/proc/%lu/maps", (unsigned long) c->pid) < 0)
+ return -ENOMEM;
+
+ f = fopen(p, "re");
+ free(p);
+
+ if (!f)
+ return -errno;
+
+ while (!feof(f)) {
+ int k;
+ char line[LINE_MAX];
+ unsigned long start, end;
+ int j;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (ferror(f)) {
+ r = -errno;
+ goto finish;
+ }
+
+ break;
+ }
+
+ line[strcspn(line, "\n\r")] = 0;
+
+ k = sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*u %n", &start, &end, &j);
+ if (k != 2) {
+ r = -EIO;
+ break;
+ }
+
+ r = context_add_mapping(c, start, end, line[j] == 0 ? NULL : line + j);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
+int process_read_fields(struct context *c) {
+ int r;
+
+ assert(c);
+ assert(c->pid >= 0);
+
+ /* These ones matter */
+ r = proc_read_pid_buffer(c->pid, "maps", &c->proc_maps);
+ if (r < 0)
+ return r;
+
+ r = proc_read_pid_buffer(c->pid, "auxv", &c->auxv);
+ if (r < 0)
+ return r;
+
+ /* The following ones don't really matter, so don't check return values */
+ proc_read_pid_buffer(c->pid, "status", &c->proc_status);
+ proc_read_pid_buffer(c->pid, "cmdline", &c->proc_cmdline);
+ proc_read_pid_buffer(c->pid, "environ", &c->proc_environ);
+ proc_read_pid_buffer(c->pid, "comm", &c->proc_comm);
+ proc_read_pid_buffer(c->pid, "attr/current", &c->proc_attr_current);
+ proc_readlink_pid_buffer(c->pid, "exe", &c->proc_exe);
+
+ proc_read_buffer("/proc/cpuinfo", &c->proc_cpuinfo);
+ /* This is an Ubuntuism, but Google is doing this, hence let's stay compatible here */
+ proc_read_buffer("/etc/lsb-release", &c->lsb_release);
+ /* It's much nicer to write /etc/os-release instead, which is more widely supported */
+ proc_read_buffer("/etc/os-release", &c->os_release);
+
+ return 0;
+}
diff --git a/read-process.h b/read-process.h
new file mode 100644
index 0000000..aafcc9f
--- /dev/null
+++ b/read-process.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooreadprocesshfoo
+#define fooreadprocesshfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include "context.h"
+
+int process_attach(struct context *c);
+int process_detach(struct context *c);
+
+int process_read_fields(struct context *c);
+int process_read_threads(struct context *c);
+int process_read_maps(struct context *c);
+int process_read_memory(struct context *c, unsigned long source, void *destination, size_t length);
+
+#endif
diff --git a/segfault.c b/segfault.c
index b69906e..17e87a5 100644
--- a/segfault.c
+++ b/segfault.c
@@ -1,5 +1,25 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..7f5fa06
--- /dev/null
+++ b/util.c
@@ -0,0 +1,96 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <inttypes.h>
+
+#include "util.h"
+
+void* memdup(const void *p, size_t l) {
+ void *r;
+
+ assert(p);
+
+ r = malloc(l);
+ if (!r)
+ return NULL;
+
+ memcpy(r, p, l);
+ return r;
+}
+
+int read_full_file(const char *path, void **_buffer, size_t *_size) {
+ void *buffer = NULL;
+ size_t size = 0, allocated = 0;
+ FILE *f;
+ int r;
+
+ assert(path);
+ assert(_buffer);
+ assert(_size);
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ while (!feof(f)) {
+ size_t k;
+
+ if (size >= allocated) {
+ size_t l;
+ void *p;
+
+ l = MAX(LINE_MAX, size * 2);
+ p = realloc(buffer, l);
+ if (!p) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ buffer = p;
+ allocated = l;
+ }
+
+ k = fread((uint8_t*) buffer + size, 1, allocated - size, f);
+ if (k <= 0 && ferror(f)) {
+ r = -errno;
+ goto finish;
+ }
+
+ size += k;
+ }
+
+ r = 0;
+
+ *_buffer = buffer;
+ *_size = size;
+
+finish:
+ fclose(f);
+
+ return r;
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..4fe428a
--- /dev/null
+++ b/util.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+void* memdup(const void *p, size_t l);
+int read_full_file(const char *path, void **_buffer, size_t *_size);
+
+#endif
diff --git a/write-minicore.c b/write-minicore.c
new file mode 100644
index 0000000..73b1d0e
--- /dev/null
+++ b/write-minicore.c
@@ -0,0 +1,373 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "write-minicore.h"
+
+static int minicore_append_ph(struct context *c, const ElfW(Phdr) *ph) {
+ uint32_t i;
+
+ assert(c);
+
+ i = c->minicore_n_phs++;
+ assert(i < c->n_write_maps + 1);
+
+ memcpy(c->minicore_phs+i, ph, sizeof(*ph));
+
+ fprintf(stderr, "Appending segment type=0x%x size=%lu\n",
+ (unsigned) ph->p_type,
+ (unsigned long) ph->p_filesz);
+
+ return 0;
+}
+
+static int minicore_write_maps(struct context *c) {
+ unsigned i;
+ int r;
+
+ assert(c);
+
+ for (i = 0; i < c->n_write_maps; i++) {
+ struct map_info *a;
+ ElfW(Phdr) ph;
+ void *p;
+ size_t offset;
+
+ a = c->write_maps + i;
+
+ r = context_reserve_bytes(c, a->extent.size, &p, &offset);
+ if (r < 0)
+ return r;
+
+ r = context_read_memory(c, a->extent.address, p, a->extent.size);
+ if (r < 0)
+ return r;
+
+ memset(&ph, 0, sizeof(ph));
+ ph.p_type = PT_LOAD;
+ ph.p_offset = offset;
+ ph.p_filesz = a->extent.size;
+ ph.p_memsz = a->extent.size;
+ ph.p_vaddr = a->extent.address;
+ ph.p_flags = PF_W|PF_R|PF_X;
+
+ r = minicore_append_ph(c, &ph);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int minicore_write_one_note(struct context *c, const char *name, ElfW(Word) type, const void *data, size_t length) {
+ int r;
+ ElfW(Nhdr) nh;
+
+ assert(c);
+ assert(name);
+ assert(data || length <= 0);
+
+ if (length <= 0)
+ return 0;
+
+ memset(&nh, 0, sizeof(nh));
+ nh.n_namesz = strlen(name);
+ nh.n_descsz = length;
+ nh.n_type = type;
+
+ r = context_append_bytes(c, &nh, sizeof(nh), NULL);
+ if (r < 0)
+ return r;
+
+ r = context_append_bytes(c, name, nh.n_namesz, NULL);
+ if (r < 0)
+ return r;
+
+ r = context_null_bytes(c, roundup(nh.n_namesz, sizeof(int)) - nh.n_namesz, NULL);
+ if (r < 0)
+ return r;
+
+ r = context_append_bytes(c, data, nh.n_descsz, NULL);
+ if (r < 0)
+ return r;
+
+ r = context_null_bytes(c, roundup(nh.n_descsz, sizeof(int)) - nh.n_descsz, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int minicore_write_note_prstatus(struct context *c, struct thread_info *i) {
+ struct elf_prstatus synthetic;
+ assert(c);
+ assert(i);
+
+ if (i->have_prstatus)
+ return minicore_write_one_note(c, "CORE", NT_PRSTATUS, &i->prstatus, sizeof(i->prstatus));
+
+ memset(&synthetic, 0, sizeof(synthetic));
+ synthetic.pr_pid = i->tid;
+ memcpy(&synthetic.pr_reg, &i->regs, sizeof(i->regs));
+
+ return minicore_write_one_note(c, "CORE", NT_PRSTATUS, &synthetic, sizeof(synthetic));
+}
+
+static int minicore_write_note_prpsinfo(struct context *c) {
+ struct elf_prpsinfo synthetic;
+
+ assert(c);
+
+ if (c->have_prpsinfo)
+ return minicore_write_one_note(c, "CORE", NT_PRPSINFO, &c->prpsinfo, sizeof(c->prpsinfo));
+
+ memset(&synthetic, 0, sizeof(synthetic));
+ synthetic.pr_pid = c->pid;
+ if (c->proc_comm.data)
+ memcpy(synthetic.pr_fname, c->proc_comm.data, MIN(sizeof(synthetic.pr_fname), sizeof(c->proc_comm.size)));
+
+ return minicore_write_one_note(c, "CORE", NT_PRPSINFO, &synthetic, sizeof(synthetic));
+}
+
+static int minicore_write_note_auxv(struct context *c) {
+ assert(c);
+
+ if (c->auxv.data)
+ return minicore_write_one_note(c, "CORE", NT_AUXV, c->auxv.data, c->auxv.size);
+
+ return 0;
+}
+
+static int minicore_write_note_fpregset(struct context *c, struct thread_info *i) {
+ assert(c);
+
+ return minicore_write_one_note(c, "CORE", NT_FPREGSET, &i->fpregs, sizeof(i->fpregs));
+}
+
+#ifdef __i386
+static int minicore_write_note_fpregset(struct context *c, struct thread_info *i) {
+ assert(c);
+
+ return minicore_write_one_note(c, "LINUX", NT_PRXFPREG, &i->fpxregs, sizeof(i->fpxregs));
+}
+#endif
+
+static int minicore_write_notes_for_thread(struct context *c, unsigned i) {
+ int r;
+
+ r = minicore_write_note_prstatus(c, c->threads+i);
+ if (r < 0)
+ return r;
+
+ if (i == 0) {
+ /* The data for the process is written in the middle
+ * of the data of thread #1 */
+ r = minicore_write_note_prpsinfo(c);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_note_auxv(c);
+ if (r < 0)
+ return r;
+ }
+ r = minicore_write_note_fpregset(c, c->threads+i);
+ if (r < 0)
+ return r;
+
+#ifdef __i386
+ r = minicore_write_note_prxfpreg(c, c->threads+i);
+ if (r < 0)
+ return r;
+#endif
+
+ return 0;
+}
+
+static int minicore_write_meta_notes(struct context *c) {
+ int r;
+
+ assert(c);
+
+ /* We use the same type identifiers as the minidump logic */
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_MAPS, c->proc_maps.data, c->proc_maps.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_PROC_STATUS, c->proc_status.data, c->proc_status.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_ENVIRON, c->proc_environ.data, c->proc_environ.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_CMD_LINE, c->proc_cmdline.data, c->proc_cmdline.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_COMM, c->proc_comm.data, c->proc_comm.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_ATTR_CURRENT, c->proc_attr_current.data, c->proc_attr_current.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_EXE, c->proc_exe.data, c->proc_exe.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_CPU_INFO, c->proc_cpuinfo.data, c->proc_cpuinfo.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_LSB_RELEASE, c->lsb_release.data, c->lsb_release.size);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_one_note(c, "LENNART", MINIDUMP_LINUX_OS_RELEASE, c->os_release.data, c->os_release.size);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int minicore_write_notes(struct context *c) {
+ ElfW(Phdr) ph;
+ unsigned i;
+ size_t offset;
+ int r;
+
+ assert(c);
+ assert(c->n_threads > 0);
+
+ offset = c->output_size;
+
+ for (i = 0; i < c->n_threads; i++) {
+ r = minicore_write_notes_for_thread(c, i);
+ if (r < 0)
+ return r;
+ }
+
+ r = minicore_write_meta_notes(c);
+ if (r < 0)
+ return r;
+
+ memset(&ph, 0, sizeof(ph));
+ ph.p_type = PT_NOTE;
+ ph.p_offset = offset;
+ ph.p_filesz = c->output_size - offset;
+
+ r = minicore_append_ph(c, &ph);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int minicore_write_phs(struct context *c) {
+ size_t offset;
+ ElfW(Ehdr) *h;
+ int r;
+
+ assert(c);
+
+ r = context_append_bytes(c, c->minicore_phs, sizeof(ElfW(Phdr)) * c->minicore_n_phs, &offset);
+ if (r < 0)
+ return r;
+
+ h = c->output;
+ h->e_phnum = c->minicore_n_phs;
+ h->e_phoff = offset;
+
+ return 0;
+}
+
+int minicore_write(struct context *c) {
+ ElfW(Ehdr) h;
+ int r;
+
+ assert(c);
+
+ memset(&h, 0, sizeof(h));
+ memcpy(h.e_ident, ELFMAG, SELFMAG);
+ h.e_type = ET_CORE;
+ h.e_ehsize = sizeof(ElfW(Ehdr));
+ h.e_phentsize = sizeof(ElfW(Phdr));
+ h.e_shentsize = sizeof(ElfW(Shdr));
+
+#if __WORDSIZE == 32
+ h.e_ident[EI_CLASS] = ELFCLASS32;
+#elif __WORDSIZE == 64
+ h.e_ident[EI_CLASS] = ELFCLASS64;
+#else
+#error "Unknown word size."
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ h.e_ident[EI_DATA] = ELFDATA2LSB;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ h.e_ident[EI_DATA] = ELFDATA2MSB;
+#else
+#error "Unknown endianess."
+#endif
+ h.e_ident[EI_VERSION] = EV_CURRENT;
+ h.e_ident[EI_OSABI] = ELFOSABI_NONE;
+
+#if __i386
+ h.e_machine = EM_386;
+#elif __x86_64
+ h.e_machine = EM_X86_64;
+#else
+#error "Unknown machine."
+#endif
+ h.e_version = EV_CURRENT;
+
+ r = context_append_bytes(c, &h, sizeof(h), NULL);
+ if (r < 0)
+ return r;
+
+ /* Allocate an array for one segment per map plus one NOTE segment */
+ c->minicore_phs = malloc(sizeof(ElfW(Phdr)) * (1 + c->n_write_maps));
+ if (!c->minicore_phs)
+ return -ENOMEM;
+
+ r = minicore_write_notes(c);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_maps(c);
+ if (r < 0)
+ return r;
+
+ r = minicore_write_phs(c);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
diff --git a/write-minicore.h b/write-minicore.h
new file mode 100644
index 0000000..b091d7e
--- /dev/null
+++ b/write-minicore.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foowriteminicorehfoo
+#define foowriteminicorehfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include "context.h"
+
+int minicore_write(struct context *c);
+
+#endif
diff --git a/write-minidump.c b/write-minidump.c
new file mode 100644
index 0000000..ad0f75e
--- /dev/null
+++ b/write-minidump.c
@@ -0,0 +1,508 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <errno.h>
+#include <alloca.h>
+#include <time.h>
+
+#include "context.h"
+
+static int minidump_write_string(struct context *c, const char *s, size_t *offset) {
+ size_t n, l;
+ struct minidump_string h;
+ unsigned i;
+ int r;
+ void *p;
+
+ assert(c);
+ assert(s);
+
+ l = strlen(s);
+ n = offsetof(struct minidump_string, buffer) + l*2;
+
+ r = context_reserve_bytes(c, n, &p, offset);
+ if (r < 0)
+ return r;
+
+ memset(&h, 0, sizeof(h));
+ h.length = htole32(l*2);
+ memcpy(p, &h, offsetof(struct minidump_string, buffer));
+
+ for (i = 0; i < l; i++) {
+ uint16_t le;
+
+ /* We just care about ASCII, so the conversion to UTF16 is trivial */
+
+ le = htole16(s[i]);
+ memcpy((uint8_t*) p + offsetof(struct minidump_string, buffer) + (2 * i), &le, 2);
+
+ /* FIXME: We should have proper UTF8 → UTF16 conversion here */
+ }
+
+ return 0;
+}
+
+static int minidump_append_directory(struct context *c, uint32_t stream_type, size_t offset, size_t size) {
+ uint32_t i;
+
+ assert(c);
+
+ i = c->minidump_n_streams++;
+ assert(i < MINIDUMP_STREAMS_MAX);
+
+ c->minidump_directory[i].stream_type = htole32(stream_type);
+ c->minidump_directory[i].location.data_size = htole32((uint32_t) size);
+ c->minidump_directory[i].location.rva = htole32((uint32_t) offset);
+
+ fprintf(stderr, "Appending directory entry type=0x%x offset=%lu size=%lu\n", stream_type, (unsigned long) offset, (unsigned long) size);
+
+ return 0;
+}
+
+static int minidump_write_blob_stream(struct context *c, uint32_t stream_type, const void *buffer, size_t size) {
+ int r;
+ size_t offset;
+
+ assert(c);
+ assert(buffer);
+ assert(size > 0);
+
+ r = context_append_bytes(c, buffer, size, &offset);
+ if (r < 0)
+ return r;
+
+ r = minidump_append_directory(c, stream_type, offset, size);
+ if (r < 0)
+ return r;
+
+ return r;
+}
+
+static int minidump_write_buffer_stream(struct context *c, uint32_t stream_type, const struct buffer *buffer) {
+ assert(c);
+ assert(buffer);
+
+ if (!buffer->data)
+ return 0;
+
+ return minidump_write_blob_stream(c, stream_type, buffer->data, buffer->size);
+}
+
+static int minidump_write_system_info_stream(struct context *c) {
+ struct minidump_system_info i;
+ long l;
+ struct utsname u;
+ int r;
+ size_t offset;
+
+ assert(c);
+
+ memset(&i, 0, sizeof(i));
+
+ i.platform_id = htole32(MINIDUMP_PLATFORM_LINUX);
+
+#if defined(__i386)
+ i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_INTEL);
+#elif defined(__mips__)
+ i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_MIPS);
+#elif defined(__ppc__)
+ i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_PPC);
+#elif defined (__arm__)
+ i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_ARM);
+#elif defined (__ia64__)
+ i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_IA64);
+#elif defined (__x86_64)
+ i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_AMD64);
+#elif defined (__sparc__)
+ i.processor_architecture = htole16(MINIDUMP_PROCESSOR_ARCHITECTURE_SPARC);
+#else
+#error "I need porting"
+#endif
+
+ l = sysconf(_SC_NPROCESSORS_ONLN);
+ i.number_of_processors = l <= 0 ? 1 : l;
+
+ r = uname(&u);
+ if (r < 0)
+ return -errno;
+
+ r = context_append_concat_string(c,
+ &offset, NULL,
+ u.sysname, " ",
+ u.release, " ",
+ u.version, " ",
+ u.machine, " ",
+ NULL);
+ if (r < 0)
+ return r;
+
+ i.csd_version_rva = htole32((uint32_t) offset);
+
+ /* FIXME: Breakpad fills these one in too, and we should as well, based on CPUID */
+ /* FIXME: i.processor_level = "cpu family"; */
+ /* FIXME: i.processor_revision = "model" << 8 | "stepping"; */
+ /* FIXME: i.cpu.x86_cpu_info.vendor_id = "vendor_id"; */
+
+ /* FIXME: On top of that we probably should fill in these as well: */
+ /* FIXME: i.major_version = 3 */
+ /* FIXME: i.minor_version = 3*/
+ /* FIXME: i.build_number = 1 */
+ /* FIXME: i.cpu.x86_cpu_info = CPUID... */
+
+ return minidump_write_blob_stream(c, MINIDUMP_SYSTEM_INFO_STREAM, &i, sizeof(i));
+}
+
+#ifdef __x86_64
+#define minidump_context minidump_context_amd64
+
+static void minidump_fill_context(struct minidump_context_amd64 *context, struct thread_info *t) {
+ assert(context);
+ assert(t);
+
+ context->context_flags = MINIDUMP_CONTEXT_AMD64_FULL|MINIDUMP_CONTEXT_AMD64_SEGMENTS;
+
+ context->cs = t->regs.cs;
+ context->ds = t->regs.ds;
+ context->es = t->regs.es;
+ context->fs = t->regs.fs;
+ context->gs = t->regs.gs;
+ context->ss = t->regs.ss;
+ context->eflags = t->regs.eflags;
+ if (t->have_user) {
+ context->dr0 = t->user.u_debugreg[0];
+ context->dr1 = t->user.u_debugreg[1];
+ context->dr2 = t->user.u_debugreg[2];
+ context->dr3 = t->user.u_debugreg[3];
+ context->dr6 = t->user.u_debugreg[6];
+ context->dr7 = t->user.u_debugreg[7];
+ }
+ context->rax = t->regs.rax;
+ context->rcx = t->regs.rcx;
+ context->rdx = t->regs.rdx;
+ context->rbx = t->regs.rbx;
+ context->rsp = t->regs.rsp;
+ context->rbp = t->regs.rbp;
+ context->rsi = t->regs.rsi;
+ context->rdi = t->regs.rdi;
+ context->r8 = t->regs.r8;
+ context->r9 = t->regs.r9;
+ context->r10 = t->regs.r10;
+ context->r11 = t->regs.r11;
+ context->r12 = t->regs.r12;
+ context->r13 = t->regs.r13;
+ context->r14 = t->regs.r14;
+ context->r15 = t->regs.r15;
+ context->rip = t->regs.rip;
+
+ context->flt_save.control_word = t->fpregs.cwd;
+ context->flt_save.status_word = t->fpregs.swd;
+ context->flt_save.tag_word = t->fpregs.ftw;
+ context->flt_save.error_opcode = t->fpregs.fop;
+ context->flt_save.error_offset = t->fpregs.rip;
+ context->flt_save.data_offset = t->fpregs.rdp;
+ context->flt_save.mx_csr = t->fpregs.mxcsr;
+ context->flt_save.mx_csr_mask = t->fpregs.mxcr_mask;
+ memcpy(&context->flt_save.float_registers, &t->fpregs.st_space, 8 * 16);
+ memcpy(&context->flt_save.xmm_registers, &t->fpregs.xmm_space, 16 * 16);
+}
+#else
+#error "I need porting"
+#endif
+
+static int minidump_write_thread_list_stream(struct context *c) {
+ struct minidump_thread_list *h;
+ unsigned i;
+ size_t l;
+ int r;
+
+ l = offsetof(struct minidump_thread_list, threads) +
+ sizeof(struct minidump_thread) * c->n_threads;
+ h = alloca(l);
+ memset(h, 0, l);
+ h->number_of_threads = htole32(c->n_threads);
+
+ for (i = 0; i < c->n_threads; i++) {
+ struct thread_info *a;
+ struct minidump_thread *b;
+ size_t offset;
+ struct minidump_context context;
+ struct map_info *m;
+
+ a = c->threads + i;
+ b = h->threads + i;
+
+ memset(&context, 0, sizeof(context));
+ minidump_fill_context(&context, a);
+ r = context_append_bytes(c, &context, sizeof(context), &offset);
+ if (r < 0)
+ return r;
+
+ memset(b, 0, sizeof(*b));
+ b->thread_id = htole32(a->tid);
+ b->thread_context.rva = htole32(offset);
+ b->thread_context.data_size = htole32(sizeof(context));
+
+ m = context_find_map_info(c->write_maps, c->n_write_maps, a->stack_pointer);
+ if (m) {
+ b->stack.start_of_memory_range = htole64(m->extent.address);
+ b->stack.memory.data_size = htole32(m->extent.size);
+ b->stack.memory.rva = htole32(m->minidump_offset);
+ }
+
+ a->minidump_offset = offset;
+ }
+
+ return minidump_write_blob_stream(c, MINIDUMP_THREAD_LIST_STREAM, h, l);
+}
+
+static int minidump_write_module_list_stream(struct context *c) {
+ struct minidump_module_list *h;
+ unsigned i;
+ size_t l;
+ int r;
+
+ assert(c);
+
+ l = offsetof(struct minidump_module_list, modules) +
+ sizeof(struct minidump_module) * c->n_maps;
+ h = alloca(l);
+ memset(h, 0, l);
+ h->number_of_modules = htole32(c->n_maps);
+
+ for (i = 0; i < c->n_maps; i++) {
+ struct map_info *a;
+ struct minidump_module *b;
+ size_t offset;
+
+ a = c->maps + i;
+ b = h->modules + i;
+
+ memset(b, 0, sizeof(*b));
+ b->base_of_image = htole64(a->extent.address);
+ b->size_of_image = htole32(a->extent.size);
+
+ if (a->name) {
+ r = minidump_write_string(c, a->name, &offset);
+ if (r < 0)
+ return r;
+
+ b->module_name_rva = htole32(offset);
+ }
+
+ /* FIXME: we should fill in a lot more here */
+ }
+
+ return minidump_write_blob_stream(c, MINIDUMP_MODULE_LIST_STREAM, h, l);
+}
+
+static int minidump_write_memory_list_stream(struct context *c) {
+ struct minidump_memory_list *h;
+ unsigned i;
+ size_t l;
+ int r;
+
+ assert(c);
+
+ l = offsetof(struct minidump_memory_list, memory_ranges) +
+ sizeof(struct minidump_memory_descriptor) * c->n_write_maps;
+ h = alloca(l);
+ memset(h, 0, l);
+ h->number_of_memory_ranges = htole32(c->n_write_maps);
+
+ for (i = 0; i < c->n_write_maps; i++) {
+ struct map_info *a;
+ struct minidump_memory_descriptor *b;
+ size_t offset;
+ void *p;
+
+ a = c->write_maps + i;
+ b = h->memory_ranges + i;
+
+ r = context_reserve_bytes(c, a->extent.size, &p, &offset);
+ if (r < 0)
+ return r;
+
+ r = context_read_memory(c, a->extent.address, p, a->extent.size);
+ if (r < 0)
+ return r;
+
+ memset(b, 0, sizeof(*b));
+ b->start_of_memory_range = htole64(a->extent.address);
+ b->memory.rva = htole32(offset);
+ b->memory.data_size = htole32(a->extent.size);
+
+ a->minidump_offset = offset;
+ }
+
+ return minidump_write_blob_stream(c, MINIDUMP_MEMORY_LIST_STREAM, h, l);
+}
+
+static int minidump_write_exception_stream(struct context *c) {
+ struct minidump_exception_stream h;
+ struct thread_info *t;
+
+ assert(c);
+ assert(c->n_threads > 0);
+
+ t = c->threads+0;
+
+ memset(&h, 0, sizeof(h));
+ h.thread_id = htole32(t->tid);
+
+ if (t->have_prstatus)
+ h.exception_record.exception_code = htole32(t->prstatus.pr_info.si_signo);
+ else if (t->have_siginfo) {
+ h.exception_record.exception_code = htole32(t->siginfo.si_signo);
+ h.exception_record.exception_address = htole64((uint64_t) t->siginfo.si_addr);
+ }
+
+ h.thread_context.data_size = htole32(sizeof(struct minidump_context));
+ h.thread_context.rva = htole32(t->minidump_offset);
+
+ return minidump_write_blob_stream(c, MINIDUMP_EXCEPTION_STREAM, &h, sizeof(h));
+}
+
+static int minidump_write_directory(struct context *c) {
+ size_t offset;
+ struct minidump_header *h;
+ int r;
+
+ assert(c);
+
+ r = context_append_bytes(c, c->minidump_directory, sizeof(struct minidump_directory) * c->minidump_n_streams, &offset);
+ if (r < 0)
+ return r;
+
+ /* The beginning of the minidump is definitely aligned, so we
+ * access it directly and patch in the directory data. */
+ h = c->output;
+ h->number_of_streams = htole32(c->minidump_n_streams);
+ h->stream_directory_rva = htole32((uint32_t) offset);
+
+ return 0;
+}
+
+int minidump_write(struct context *c) {
+ struct minidump_header h;
+ int r;
+
+ assert(c);
+
+ memset(&h, 0, sizeof(h));
+ h.signature = htole32(0x504d444d);
+ h.version = htole32(0x0000a793);
+ h.time_date_stamp = htole32(time(NULL));
+
+ r = context_append_bytes(c, &h, sizeof(h), NULL);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_memory_list_stream(c);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_thread_list_stream(c);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_module_list_stream(c);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_exception_stream(c);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_system_info_stream(c);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_MAPS, &c->proc_maps);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_PROC_STATUS, &c->proc_status);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_ENVIRON, &c->proc_environ);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_CMD_LINE, &c->proc_cmdline);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_COMM, &c->proc_comm);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_ATTR_CURRENT, &c->proc_attr_current);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_EXE, &c->proc_exe);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_CPU_INFO, &c->proc_cpuinfo);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_LSB_RELEASE, &c->lsb_release);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_OS_RELEASE, &c->os_release);
+ if (r < 0)
+ return r;
+
+ r = minidump_write_buffer_stream(c, MINIDUMP_LINUX_AUXV, &c->auxv);
+ if (r < 0)
+ return r;
+
+ if (c->have_prpsinfo) {
+ r = minidump_write_blob_stream(c, MINIDUMP_LINUX_PRPSINFO, &c->prpsinfo, sizeof(c->prpsinfo));
+ if (r < 0)
+ return r;
+ }
+
+ if (c->have_coredump_header) {
+ r = minidump_write_blob_stream(c, MINIDUMP_LINUX_CORE_EHDR, &c->coredump_header, sizeof(c->coredump_header));
+ if (r < 0)
+ return r;
+ }
+
+ /* We probably should find __abort_msg and __glib_assert_msg
+ * and include it here */
+
+ r = minidump_write_directory(c);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
diff --git a/write-minidump.h b/write-minidump.h
new file mode 100644
index 0000000..f2c7c8a
--- /dev/null
+++ b/write-minidump.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foowriteminidumphfoo
+#define foowriteminidumphfoo
+
+/***
+ This file is part of libminidump.
+
+ Copyright 2012 Lennart Poettering
+
+ libminidump is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libminidump 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libminidump; If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include "context.h"
+
+int minidump_write(struct context *c);
+
+#endif