diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2006-07-27 02:36:23 -0600 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2006-07-27 02:36:23 -0600 |
commit | 283261998a9846019d898bc454b363e4aaf3d181 (patch) | |
tree | a4af6da4c5a2c6f7669d918c1f07dc68d6aa0ab2 /kdump |
kexec-tools-1.101
- Initial import into git
- initial nbi image formage support
- ppc32 initial register setting fixes.
- gzipped multiboot file support
Diffstat (limited to 'kdump')
-rw-r--r-- | kdump/Makefile | 35 | ||||
-rw-r--r-- | kdump/kdump.c | 311 |
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, ¬e_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; +} |