summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2012-04-18 01:34:53 +0200
committerLennart Poettering <lennart@poettering.net>2012-04-18 01:34:53 +0200
commit8979b1a576ab0ec9b20086a55acd3400ad3b3377 (patch)
tree715fe0b442335b72aff7d2f31741998b57586212
parent747697607f9716e6200912e7063e132b416b9193 (diff)
minidump: implement minicore writer
-rw-r--r--coredump.c20
-rw-r--r--minidump.c278
-rw-r--r--mkminidump.c2
3 files changed, 298 insertions, 2 deletions
diff --git a/coredump.c b/coredump.c
index 7d70f74..e8a304d 100644
--- a/coredump.c
+++ b/coredump.c
@@ -44,6 +44,26 @@ int coredump_read_header(int fd, ElfW(Ehdr) *header) {
#error "Unknown word size."
#endif
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (header->e_ident[EI_DATA] != ELFDATA2LSB)
+ return -EINVAL;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (header->e_ident[EI_DATA] != ELFDATA2MSB)
+ return -EINVAL;
+#else
+#error "Unknown endianess."
+#endif
+
+#if __i386
+ if (header->e_machine != EM_386)
+ return -EINVAL;
+#elif __x86_64
+ if (header->e_machine != EM_X86_64)
+ return -EINVAL;
+#else
+#error "Unknown machine."
+#endif
+
return 0;
}
diff --git a/minidump.c b/minidump.c
index 70bec96..582a2f1 100644
--- a/minidump.c
+++ b/minidump.c
@@ -18,6 +18,7 @@
#include <stdarg.h>
#include <sys/utsname.h>
#include <stddef.h>
+#include <endian.h>
#include "coredump.h"
#include "format.h"
@@ -120,6 +121,9 @@ struct context {
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)
@@ -1646,10 +1650,282 @@ static int write_minidump(struct context *c) {
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);
+ assert(length > 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 = append_bytes(c, data, length, 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_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;
+ }
+
+ 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);
- /* FIXME */
+ 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;
}
diff --git a/mkminidump.c b/mkminidump.c
index 41fde31..826ef2c 100644
--- a/mkminidump.c
+++ b/mkminidump.c
@@ -207,7 +207,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = output_and_free(arg_minidump, &buffer, &buffer_size);
+ r = output_and_free(arg_minicore, &buffer, &buffer_size);
if (r < 0)
goto finish;
}