diff options
Diffstat (limited to 'src/read-minidump.c')
-rw-r--r-- | src/read-minidump.c | 219 |
1 files changed, 214 insertions, 5 deletions
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; } |