#include #include #include #include #include #include #include #include #include #include #include #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" #define ALIGN_MASK(x,y) (((x) + (y)) & ~(y)) #define ALIGN(x,y) ALIGN_MASK(x, (y) - 1) static void *map_addr(int fd, unsigned long size, off_t offset) { unsigned long page_size = getpagesize(); unsigned long map_offset = offset & (page_size - 1); size_t len = ALIGN(size + map_offset, page_size); void *result; result = mmap(0, len, PROT_READ, MAP_SHARED, fd, offset - map_offset); if (result == MAP_FAILED) { fprintf(stderr, "Cannot mmap " DEV_MEM " offset: %#llx size: %lu: %s\n", (unsigned long long)offset, size, strerror(errno)); exit(5); } return result + map_offset; } static void unmap_addr(void *addr, unsigned long size) { unsigned long page_size = getpagesize(); unsigned long map_offset = (uintptr_t)addr & (page_size - 1); size_t len = ALIGN(size + map_offset, page_size); int ret; addr -= map_offset; ret = munmap(addr, len); 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", (unsigned int)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; nphdr++; 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; size_t written = 0; 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), start_addr + 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; }