summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO4
-rw-r--r--src/context.c89
-rw-r--r--src/context.h4
-rw-r--r--src/coredump-util.c2
-rw-r--r--src/format.h8
-rw-r--r--src/read-coredump.c60
-rw-r--r--src/read-minidump.c219
-rw-r--r--src/read-minidump.h1
-rw-r--r--src/read-process.c8
-rw-r--r--src/util.h4
10 files changed, 331 insertions, 68 deletions
diff --git a/TODO b/TODO
index db5cf27..9000b6f 100644
--- a/TODO
+++ b/TODO
@@ -8,8 +8,12 @@ Now:
- build id logic
+- include cgroup membership
+
Later:
- Proper UTF8-to-UTF16 encoder
- Possibly, full coredump generator, in addition to the existing minicore generator
+
+- some code to find __abort_msg
diff --git a/src/context.c b/src/context.c
index 943fca0..1974702 100644
--- a/src/context.c
+++ b/src/context.c
@@ -26,6 +26,7 @@
#include <errno.h>
#include <string.h>
#include <stdarg.h>
+#include <unistd.h>
#include "minidump.h"
#include "format.h"
@@ -36,6 +37,7 @@
#include "read-process.h"
#include "write-minidump.h"
#include "write-minicore.h"
+#include "util.h"
int context_read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
int r;
@@ -422,6 +424,48 @@ finish:
return r;
}
+struct buffer *context_find_buffer(struct context *c, unsigned type) {
+ assert(c);
+
+ switch (type) {
+
+ case MINIDUMP_LINUX_MAPS:
+ return &c->proc_maps;
+
+ case MINIDUMP_LINUX_PROC_STATUS:
+ return &c->proc_status;
+
+ case MINIDUMP_LINUX_ENVIRON:
+ return &c->proc_environ;
+
+ case MINIDUMP_LINUX_CMD_LINE:
+ return &c->proc_cmdline;
+
+ case MINIDUMP_LINUX_COMM:
+ return &c->proc_comm;
+
+ case MINIDUMP_LINUX_ATTR_CURRENT:
+ return &c->proc_attr_current;
+
+ case MINIDUMP_LINUX_EXE:
+ return &c->proc_exe;
+
+ case MINIDUMP_LINUX_CPU_INFO:
+ return &c->proc_cpuinfo;
+
+ case MINIDUMP_LINUX_LSB_RELEASE:
+ return &c->lsb_release;
+
+ case MINIDUMP_LINUX_OS_RELEASE:
+ return &c->os_release;
+
+ case MINIDUMP_LINUX_AUXV:
+ return &c->auxv;
+ }
+
+ return NULL;
+}
+
static int show_buffer(FILE *f, const char *title, struct buffer *b) {
char *p;
@@ -497,7 +541,16 @@ void context_show(FILE *f, struct context *c) {
assert(f);
assert(c);
- fputs("-- Available Maps\n", f);
+ fprintf(f,
+ "-- Source\n"
+ "Have Process: %s\n"
+ "Have Coredump: %s\n"
+ "Have Minidump: %s\n"
+ "-- Available Maps\n",
+ yes_no(CONTEXT_HAVE_PROCESS(c)),
+ yes_no(CONTEXT_HAVE_COREDUMP(c)),
+ yes_no(CONTEXT_HAVE_MINIDUMP(c)));
+
for (i = 0, sum = 0; i < c->n_maps; i++) {
map_show(f, i, c->maps + i);
sum += c->maps[i].extent.size;
@@ -557,6 +610,10 @@ int context_load(struct context *c) {
r = minidump_read_threads(c);
if (r < 0)
return r;
+
+ r = minidump_read_streams(c);
+ if (r < 0)
+ return r;
}
if (CONTEXT_HAVE_COREDUMP(c)) {
@@ -766,3 +823,33 @@ finish:
context_release(&c);
return r;
}
+
+int context_pread_buffer(int fd, struct buffer *b, size_t length, off_t offset) {
+ void *p;
+ ssize_t l;
+
+ assert(fd >= 0);
+ assert(b);
+ assert(length > 0);
+
+ p = malloc(length);
+ if (!p)
+ return -ENOMEM;
+
+ l = pread(fd, p, length, offset);
+ if (l < 0) {
+ free(p);
+ return -errno;
+ }
+
+ if ((size_t) l != length) {
+ free(p);
+ return -EIO;
+ }
+
+ free(b->data);
+ b->data = p;
+ b->size = length;
+
+ return 0;
+}
diff --git a/src/context.h b/src/context.h
index 5e53bdf..645d009 100644
--- a/src/context.h
+++ b/src/context.h
@@ -153,8 +153,12 @@ int context_append_bytes(struct context *c, const void *data, size_t bytes, size
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, ...);
+struct buffer *context_find_buffer(struct context *c, unsigned type);
+
void context_show(FILE *f, struct context *c);
int context_load(struct context *c);
void context_release(struct context *c);
+int context_pread_buffer(int fd, struct buffer *b, size_t length, off_t offset);
+
#endif
diff --git a/src/coredump-util.c b/src/coredump-util.c
index bbfd77a..582f493 100644
--- a/src/coredump-util.c
+++ b/src/coredump-util.c
@@ -172,7 +172,7 @@ int coredump_read_memory(int fd, const ElfW(Ehdr) *header, unsigned long source,
return -EIO;
l = pread(fd,
- (uint8_t*) destination, length,
+ destination, length,
segment.p_offset + (source - segment.p_vaddr));
if (l < 0)
return -errno;
diff --git a/src/format.h b/src/format.h
index 394746d..fecefa0 100644
--- a/src/format.h
+++ b/src/format.h
@@ -52,10 +52,10 @@ enum {
MINIDUMP_UNUSED_STREAM = 0,
MINIDUMP_RESERVED_STREAM_0 = 1,
MINIDUMP_RESERVED_STREAM_1 = 2,
- MINIDUMP_THREAD_LIST_STREAM = 3, /* TODO XXXX */
- MINIDUMP_MODULE_LIST_STREAM = 4, /* TODO XXXX */
- MINIDUMP_MEMORY_LIST_STREAM = 5, /* TODO XXXX */
- MINIDUMP_EXCEPTION_STREAM = 6, /* TODO XXXX */
+ MINIDUMP_THREAD_LIST_STREAM = 3, /* done */
+ MINIDUMP_MODULE_LIST_STREAM = 4, /* done */
+ MINIDUMP_MEMORY_LIST_STREAM = 5, /* done */
+ MINIDUMP_EXCEPTION_STREAM = 6, /* done */
MINIDUMP_SYSTEM_INFO_STREAM = 7, /* done */
MINIDUMP_THREAD_EX_LIST_STREAM = 8,
MINIDUMP_MEMORY_64_LIST_STREAM = 9,
diff --git a/src/read-coredump.c b/src/read-coredump.c
index 7dd9091..90b89f9 100644
--- a/src/read-coredump.c
+++ b/src/read-coredump.c
@@ -173,63 +173,11 @@ int coredump_read_threads(struct context *c) {
} 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;
- }
-
+ b = context_find_buffer(c, note.n_type);
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;
+ r = context_pread_buffer(c->coredump_fd, b, note.n_descsz, descriptor_offset);
+ if (r < 0)
+ return r;
}
}
}
diff --git a/src/read-minidump.c b/src/read-minidump.c
index 87c7fe2..4f464b6 100644
--- a/src/read-minidump.c
+++ b/src/read-minidump.c
@@ -23,10 +23,14 @@
#include <assert.h>
#include <errno.h>
#include <unistd.h>
+#include <stddef.h>
#include "context.h"
#include "read-minidump.h"
+#define MAX_MEMORY_STREAMS 2048
+#define MAX_THREADS 2048
+
int minidump_read_header(struct context *c) {
ssize_t l;
@@ -47,31 +51,236 @@ int minidump_read_header(struct context *c) {
return 0;
}
+static int find_stream(struct context *c, uint32_t type, off_t *offset, off_t *length) {
+ unsigned i;
+
+ assert(c);
+ assert(CONTEXT_HAVE_MINIDUMP(c));
+ assert(offset);
+ assert(length);
+
+ for (i = 0; i < c->minidump_header.number_of_streams; i++) {
+ off_t o;
+ struct minidump_directory d;
+ ssize_t l;
+
+ o = c->minidump_header.stream_directory_rva + (sizeof(struct minidump_directory) * i);
+
+ l = pread(c->minidump_fd, &d, sizeof(d), o);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(d))
+ return -EBADMSG;
+
+ if (d.stream_type == type) {
+ *offset = d.location.rva;
+ *length = d.location.data_size;
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int find_memory_list_stream(struct context *c, unsigned *n_descriptors, off_t *offset) {
+ struct minidump_memory_list h;
+ int r;
+ ssize_t l;
+ off_t size, off;
+
+ assert(c);
+ assert(CONTEXT_HAVE_MINIDUMP(c));
+ assert(n_descriptors);
+ assert(offset);
+
+ r = find_stream(c, MINIDUMP_MEMORY_LIST_STREAM, &off, &size);
+ if (r <= 0)
+ return r;
+
+ if (size < (off_t) offsetof(struct minidump_memory_list, memory_ranges))
+ return -EBADMSG;
+
+ l = pread(c->minidump_fd, &h, offsetof(struct minidump_memory_list, memory_ranges), off);
+ if (l < 0)
+ return -errno;
+ if (l != offsetof(struct minidump_memory_list, memory_ranges))
+ return -EBADMSG;
+
+ if ((off_t) offsetof(struct minidump_memory_list, memory_ranges) +
+ ((off_t) h.number_of_memory_ranges * (off_t) sizeof(struct minidump_memory_descriptor)) != size)
+ return -EBADMSG;
+
+ if (h.number_of_memory_ranges > MAX_MEMORY_STREAMS)
+ return -E2BIG;
+
+ *n_descriptors = h.number_of_memory_ranges;
+ *offset = off + offsetof(struct minidump_memory_list, memory_ranges);
+
+ return 1;
+}
+
int minidump_read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
+ off_t offset;
+ unsigned i, n;
+ int r;
+ ssize_t l;
+
assert(c);
assert(destination);
assert(length > 0);
assert(CONTEXT_HAVE_MINIDUMP(c));
- /* FIXME */
+ r = find_memory_list_stream(c, &n, &offset);
+ if (r <= 0)
+ return r;
- return -ENOTSUP;
+ for (i = 0; i < n; i++) {
+ struct minidump_memory_descriptor d;
+ off_t o;
+
+ o = offset + (sizeof(struct minidump_memory_descriptor) * i);
+
+ l = pread(c->minidump_fd, &d, sizeof(d), o);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(d))
+ return -EBADMSG;
+
+ if (source >= d.start_of_memory_range + d.memory.data_size)
+ continue;
+
+ if (source + length < d.start_of_memory_range)
+ continue;
+
+ if (source < d.start_of_memory_range ||
+ source + length > d.start_of_memory_range + d.memory.data_size)
+ return -EIO;
+
+ l = pread(c->minidump_fd,
+ destination, length,
+ d.memory.rva + (source - d.start_of_memory_range));
+ if (l < 0)
+ return -errno;
+ if ((size_t) l != length)
+ return -EIO;
+
+ return 1;
+ }
+
+ return 0;
}
int minidump_read_threads(struct context *c) {
+ struct minidump_thread_list h;
+ int r;
+ off_t off, size;
+ ssize_t l;
+ unsigned i;
+
assert(c);
assert(CONTEXT_HAVE_MINIDUMP(c));
- /* FIXME */
+ r = find_stream(c, MINIDUMP_THREAD_LIST_STREAM, &off, &size);
+ if (r <= 0)
+ return r;
+
+ if (size < (off_t) offsetof(struct minidump_thread_list, threads))
+ return -EBADMSG;
+
+ l = pread(c->minidump_fd, &h, offsetof(struct minidump_thread_list, threads), off);
+ if (l < 0)
+ return -errno;
+ if (l != offsetof(struct minidump_thread_list, threads))
+ return -EBADMSG;
+
+ if ((off_t) offsetof(struct minidump_thread_list, threads) +
+ ((off_t) h.number_of_threads * (off_t) sizeof(struct minidump_thread)) != size)
+ return -EBADMSG;
+
+ if (h.number_of_threads > MAX_THREADS)
+ return -E2BIG;
return -ENOTSUP;
}
int minidump_read_maps(struct context *c) {
+ off_t offset;
+ unsigned i, n;
+ int r;
+ ssize_t l;
+
assert(c);
assert(CONTEXT_HAVE_MINIDUMP(c));
- /* FIXME */
+ r = find_memory_list_stream(c, &n, &offset);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
- return -ENOTSUP;
+ for (i = 0; i < n; i++) {
+ off_t o;
+ struct minidump_memory_descriptor d;
+
+ o = offset + (sizeof(struct minidump_memory_descriptor) * i);
+
+ l = pread(c->minidump_fd, &d, sizeof(d), o);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(d))
+ return -EBADMSG;
+
+ r = context_add_mapping(c, d.start_of_memory_range, d.start_of_memory_range + d.memory.data_size, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int minidump_read_streams(struct context *c) {
+ unsigned i;
+ int r;
+
+ assert(c);
+ assert(CONTEXT_HAVE_MINIDUMP(c));
+
+ for (i = 0; i < c->minidump_header.number_of_streams; i++) {
+ off_t o;
+ struct minidump_directory d;
+ ssize_t l;
+ struct buffer *b = NULL;
+
+ o = c->minidump_header.stream_directory_rva + (sizeof(struct minidump_directory) * i);
+
+ l = pread(c->minidump_fd, &d, sizeof(d), o);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(d))
+ return -EBADMSG;
+
+ if (d.stream_type == MINIDUMP_LINUX_PRPSINFO) {
+ if (d.location.data_size != sizeof(struct elf_prpsinfo))
+ return -EBADMSG;
+
+ l = pread(c->minidump_fd, &c->prpsinfo, sizeof(c->prpsinfo), d.location.rva);
+ if (l < 0)
+ return -errno;
+ if (l != sizeof(c->prpsinfo))
+ return -EBADMSG;
+
+ c->have_prpsinfo = true;
+ continue;
+ }
+
+ b = context_find_buffer(c, d.stream_type);
+ if (b) {
+ r = context_pread_buffer(c->minidump_fd, b, d.location.data_size, d.location.rva);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
}
diff --git a/src/read-minidump.h b/src/read-minidump.h
index dbec445..eddedcc 100644
--- a/src/read-minidump.h
+++ b/src/read-minidump.h
@@ -29,5 +29,6 @@ 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);
+int minidump_read_streams(struct context *c);
#endif
diff --git a/src/read-process.c b/src/read-process.c
index cc36fa8..f5f941c 100644
--- a/src/read-process.c
+++ b/src/read-process.c
@@ -215,10 +215,16 @@ static int ptrace_copy(enum __ptrace_request req, pid_t pid, unsigned long sourc
}
int process_read_memory(struct context *c, unsigned long source, void *destination, size_t length) {
+ int r;
+
assert(c);
assert(CONTEXT_HAVE_PROCESS(c));
- return ptrace_copy(PTRACE_PEEKDATA, c->pid, source, destination, length);
+ r = ptrace_copy(PTRACE_PEEKDATA, c->pid, source, destination, length);
+ if (r < 0)
+ return r;
+
+ return 1;
}
static int proc_read_buffer(const char *path, struct buffer *b) {
diff --git a/src/util.h b/src/util.h
index 4fe428a..cccb1b8 100644
--- a/src/util.h
+++ b/src/util.h
@@ -24,8 +24,12 @@
***/
#include <sys/types.h>
+#include <stdbool.h>
void* memdup(const void *p, size_t l);
+
int read_full_file(const char *path, void **_buffer, size_t *_size);
+#define yes_no(b) ((b) ? "yes" : "no")
+
#endif