diff options
author | Lennart Poettering <lennart@poettering.net> | 2012-04-18 01:34:53 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2012-04-18 01:34:53 +0200 |
commit | 8979b1a576ab0ec9b20086a55acd3400ad3b3377 (patch) | |
tree | 715fe0b442335b72aff7d2f31741998b57586212 | |
parent | 747697607f9716e6200912e7063e132b416b9193 (diff) |
minidump: implement minicore writer
-rw-r--r-- | coredump.c | 20 | ||||
-rw-r--r-- | minidump.c | 278 | ||||
-rw-r--r-- | mkminidump.c | 2 |
3 files changed, 298 insertions, 2 deletions
@@ -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; } @@ -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; } |