summaryrefslogtreecommitdiff
path: root/kdump
diff options
context:
space:
mode:
Diffstat (limited to 'kdump')
-rw-r--r--kdump/Makefile35
-rw-r--r--kdump/kdump.c311
2 files changed, 346 insertions, 0 deletions
diff --git a/kdump/Makefile b/kdump/Makefile
new file mode 100644
index 0000000..d9a7ab7
--- /dev/null
+++ b/kdump/Makefile
@@ -0,0 +1,35 @@
+#
+# kdump (reading a crashdump from memory)
+#
+
+KDUMP_C_SRCS:= kdump/kdump.c
+
+KDUMP_C_OBJS:= $(patsubst %.c, $(OBJDIR)/%.o, $(KDUMP_C_SRCS))
+KDUMP_C_DEPS:= $(patsubst %.c, $(OBJDIR)/%.d, $(KDUMP_C_SRCS))
+KDUMP_SRCS:= $(KDUMP_C_SRCS)
+KDUMP_OBJS:= $(KDUMP_C_OBJS)
+KDUMP_DEPS:= $(KDUMP_C_DEPS)
+KDUMP:= $(SBINDIR)/kdump
+
+include $(KDUMP_DEPS)
+
+$(KDUMP_C_DEPS): $(OBJDIR)/%.d: %.c
+ mkdir -p $(@D)
+ $(CC) $(CFLAGS) -M $< | sed -e 's|$(patsubst %.d,%.o,$(@F))|$(patsubst %.d,%.o,$(@))|' > $@
+
+$(KDUMP_C_OBJS): $(OBJDIR)/%.o: %.c $(OBJDIR)/%.d
+ mkdir -p $(@D)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(KDUMP): $(KDUMP_OBJS)
+ mkdir -p $(@D)
+ $(CC) $(CFLAGS) -o $@ $(KDUMP_OBJS)
+
+echo::
+ @echo "KDUMP_C_SRCS $(KDUMP_C_SRCS)"
+ @echo "KDUMP_C_DEPS $(KDUMP_C_DEPS)"
+ @echo "KDUMP_C_OBJS $(KDUMP_C_OBJS)"
+ @echo "KDUMP_SRCS $(KDUMP_SRCS)"
+ @echo "KDUMP_DEPS $(KDUMP_DEPS)"
+ @echo "KDUMP_OBJS $(KDUMP_OBJS)"
+
diff --git a/kdump/kdump.c b/kdump/kdump.c
new file mode 100644
index 0000000..6745982
--- /dev/null
+++ b/kdump/kdump.c
@@ -0,0 +1,311 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <endian.h>
+#include <elf.h>
+
+#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) || !defined(__BIG_ENDIAN)
+#error Endian defines missing
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define ELFDATALOCAL ELFDATA2LSB
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define ELFDATALOCAL ELFDATA2MSB
+#else
+# error Unknown byte order
+#endif
+
+#define MAP_WINDOW_SIZE (64*1024*1024)
+#define DEV_MEM "/dev/mem"
+
+static void *map_addr(int fd, unsigned long size, off_t offset)
+{
+ void *result;
+ result = mmap(0, size, PROT_READ, MAP_SHARED, fd, offset);
+ if (result == MAP_FAILED) {
+ fprintf(stderr, "Cannot mmap " DEV_MEM " offset: %llu size: %lu: %s\n",
+ (unsigned long long)offset, size, strerror(errno));
+ exit(5);
+ }
+ return result;
+}
+
+static void unmap_addr(void *addr, unsigned long size)
+{
+ int ret;
+ ret = munmap(addr, size);
+ if (ret < 0) {
+ fprintf(stderr, "munmap failed: %s\n",
+ strerror(errno));
+ exit(6);
+ }
+}
+
+static void *xmalloc(size_t size)
+{
+ void *result;
+ result = malloc(size);
+ if (result == NULL) {
+ fprintf(stderr, "malloc of %u bytes failed: %s\n",
+ size, strerror(errno));
+ exit(7);
+ }
+ return result;
+}
+
+static void *collect_notes(
+ int fd, Elf64_Ehdr *ehdr, Elf64_Phdr *phdr, size_t *note_bytes)
+{
+ int i;
+ size_t bytes, result_bytes;
+ char *notes;
+
+ result_bytes = 0;
+ /* Find the worst case note memory usage */
+ bytes = 0;
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_type == PT_NOTE) {
+ bytes += phdr[i].p_filesz;
+ }
+ }
+
+ /* Allocate the worst case note array */
+ notes = xmalloc(bytes);
+
+ /* Walk through and capture the notes */
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ Elf64_Nhdr *hdr, *lhdr, *nhdr;
+ void *pnotes;
+ if (phdr[i].p_type != PT_NOTE) {
+ continue;
+ }
+ /* First snapshot the notes */
+ pnotes = map_addr(fd, phdr[i].p_filesz, phdr[i].p_offset);
+ memcpy(notes + result_bytes, pnotes, phdr[i].p_filesz);
+ unmap_addr(pnotes, phdr[i].p_filesz);
+
+ /* Walk through the new notes and find the real length */
+ hdr = (Elf64_Nhdr *)(notes + result_bytes);
+ lhdr = (Elf64_Nhdr *)(notes + result_bytes + phdr[i].p_filesz);
+ for(; hdr < lhdr; hdr = nhdr) {
+ size_t hdr_size;
+ /* If there is not a name this is a invalid/reserved note
+ * stop here.
+ */
+ if (hdr->n_namesz == 0) {
+ break;
+ }
+ hdr_size =
+ sizeof(*hdr) +
+ ((hdr->n_namesz + 3) & ~3) +
+ ((hdr->n_descsz + 3) & ~3);
+
+ nhdr = (Elf64_Nhdr *)(((char *)hdr) + hdr_size);
+ /* if the note does not fit in the segment stop here */
+ if (nhdr > lhdr) {
+ break;
+ }
+ /* Update result_bytes for after each good header */
+ result_bytes = ((char *)hdr) - notes;
+ }
+ }
+ *note_bytes = result_bytes;
+ return notes;
+}
+
+static void *generate_new_headers(
+ Elf64_Ehdr *ehdr, Elf64_Phdr *phdr, size_t note_bytes, size_t *header_bytes)
+{
+ unsigned phnum;
+ size_t bytes;
+ char *headers;
+ Elf64_Ehdr *nehdr;
+ Elf64_Phdr *nphdr;
+ unsigned long long offset;
+ int i;
+ /* Count the number of program headers.
+ * When we are done there will be only one note header.
+ */
+ phnum = 1;
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_type == PT_NOTE) {
+ continue;
+ }
+ phnum++;
+ }
+
+ /* Compute how many bytes we will need for headers */
+ bytes = sizeof(*ehdr) + sizeof(*phdr)*phnum;
+
+ /* Allocate memory for the headers */
+ headers = xmalloc(bytes);
+
+ /* Setup pointers to the new headers */
+ nehdr = (Elf64_Ehdr *)headers;
+ nphdr = (Elf64_Phdr *)(headers + sizeof(*nehdr));
+
+ /* Copy and adjust the Elf header */
+ memcpy(nehdr, ehdr, sizeof(*nehdr));
+ nehdr->e_phoff = sizeof(*nehdr);
+ nehdr->e_phnum = phnum;
+ nehdr->e_shoff = 0;
+ nehdr->e_shentsize = 0;
+ nehdr->e_shnum = 0;
+ nehdr->e_shstrndx = 0;
+
+ /* Write the note program header */
+ nphdr->p_type = PT_NOTE;
+ nphdr->p_offset = bytes;
+ nphdr->p_vaddr = 0;
+ nphdr->p_paddr = 0;
+ nphdr->p_filesz = note_bytes;
+ nphdr->p_memsz = note_bytes;
+ nphdr->p_flags = 0;
+ nphdr->p_align = 0;
+ nphdr++;
+
+ /* Write the rest of the program headers */
+ offset = bytes + note_bytes;
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_type == PT_NOTE) {
+ continue;
+ }
+ memcpy(nphdr, &phdr[i], sizeof(*nphdr));
+ nphdr->p_offset = offset;
+ offset += phdr[i].p_filesz;
+ }
+
+ *header_bytes = bytes;
+ return headers;
+}
+
+static void write_all(int fd, const void *buf, size_t count)
+{
+ ssize_t result, written;
+ const char *ptr;
+ size_t left;
+ ptr = buf;
+ left = count;
+ do {
+ result = write(fd, ptr, left);
+ if (result >= 0) {
+ written += result;
+ ptr += result;
+ left -= result;
+ }
+ else if ((errno != EAGAIN) && (errno != EINTR)) {
+ fprintf(stderr, "write failed: %s\n",
+ strerror(errno));
+ exit(8);
+ }
+ } while(written < count);
+}
+
+int main(int argc, char **argv)
+{
+ char *start_addr_str, *end;
+ unsigned long long start_addr;
+ Elf64_Ehdr *ehdr;
+ Elf64_Phdr *phdr;
+ void *notes, *headers;
+ size_t note_bytes, header_bytes;
+ int fd;
+ int i;
+ start_addr_str = 0;
+ if (argc > 2) {
+ fprintf(stderr, "Invalid argument count\n");
+ exit(9);
+ }
+ if (argc == 2) {
+ start_addr_str = argv[1];
+ }
+ if (!start_addr_str) {
+ start_addr_str = getenv("elfcorehdr");
+ }
+ if (!start_addr_str) {
+ fprintf(stderr, "Cannot find the start of the core dump\n");
+ exit(1);
+ }
+ start_addr = strtoull(start_addr_str, &end, 0);
+ if ((start_addr_str == end) || (*end != '\0')) {
+ fprintf(stderr, "Bad core dump start addres: %s\n",
+ start_addr_str);
+ exit(2);
+ }
+
+ fd = open(DEV_MEM, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open " DEV_MEM ": %s\n",
+ strerror(errno));
+ exit(3);
+ }
+
+ /* Get the elf header */
+ ehdr = map_addr(fd, sizeof(*ehdr), start_addr);
+
+ /* Verify the ELF header */
+ if ( (ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
+ (ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
+ (ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
+ (ehdr->e_ident[EI_MAG3] != ELFMAG3) ||
+ (ehdr->e_ident[EI_CLASS] != ELFCLASS64) ||
+ (ehdr->e_ident[EI_DATA] != ELFDATALOCAL) ||
+ (ehdr->e_ident[EI_VERSION] != EV_CURRENT) ||
+ (ehdr->e_type != ET_CORE) ||
+ (ehdr->e_version != EV_CURRENT) ||
+ (ehdr->e_ehsize != sizeof(Elf64_Ehdr)) ||
+ (ehdr->e_phentsize != sizeof(Elf64_Phdr)) ||
+ (ehdr->e_phnum == 0))
+ {
+ fprintf(stderr, "Invalid Elf header\n");
+ exit(4);
+ }
+
+ /* Get the program header */
+ phdr = map_addr(fd, sizeof(*phdr)*(ehdr->e_phnum), ehdr->e_phoff);
+
+ /* Collect up the notes */
+ note_bytes = 0;
+ notes = collect_notes(fd, ehdr, phdr, &note_bytes);
+
+ /* Generate new headers */
+ header_bytes = 0;
+ headers = generate_new_headers(ehdr, phdr, note_bytes, &header_bytes);
+
+ /* Write out everything */
+ write_all(STDOUT_FILENO, headers, header_bytes);
+ write_all(STDOUT_FILENO, notes, note_bytes);
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ unsigned long long offset, size;
+ size_t wsize;
+ if (phdr[i].p_type != PT_NOTE) {
+ continue;
+ }
+ offset = phdr[i].p_offset;
+ size = phdr[i].p_filesz;
+ wsize = MAP_WINDOW_SIZE;
+ if (wsize > size) {
+ wsize = size;
+ }
+ for(;size > 0; size -= wsize, offset += wsize) {
+ void *buf;
+ wsize = MAP_WINDOW_SIZE;
+ if (wsize > size) {
+ wsize = size;
+ }
+ buf = map_addr(fd, wsize, offset);
+ write_all(STDOUT_FILENO, buf, wsize);
+ unmap_addr(buf, wsize);
+ }
+ }
+ free(notes);
+ close(fd);
+ return 0;
+}