diff options
author | Lennart Poettering <lennart@poettering.net> | 2012-04-21 20:02:19 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2012-04-21 20:02:19 +0200 |
commit | b70ad6623f5201744e64a9e4b3b1450184453974 (patch) | |
tree | 9891b93486cf9459760336cac66a3530e6870c4f | |
parent | fdcfacaf4a570edeb39b464827a11e8c8fb62835 (diff) |
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | src/context.c | 89 | ||||
-rw-r--r-- | src/context.h | 4 | ||||
-rw-r--r-- | src/coredump-util.c | 2 | ||||
-rw-r--r-- | src/format.h | 8 | ||||
-rw-r--r-- | src/read-coredump.c | 60 | ||||
-rw-r--r-- | src/read-minidump.c | 219 | ||||
-rw-r--r-- | src/read-minidump.h | 1 | ||||
-rw-r--r-- | src/read-process.c | 8 | ||||
-rw-r--r-- | src/util.h | 4 |
10 files changed, 331 insertions, 68 deletions
@@ -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) { @@ -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 |