#include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ZLIB_H #include #endif #include "elf.h" #include "elf_boot.h" #include "mkelfImage.h" static struct file_type file_type[] = { { "linux-i386", linux_i386_probe, linux_i386_mkelf, linux_i386_usage }, { "bzImage-i386", bzImage_i386_probe, linux_i386_mkelf, linux_i386_usage }, { "vmlinux-i386", vmlinux_i386_probe, linux_i386_mkelf, linux_i386_usage }, { "linux-ia64", linux_ia64_probe, linux_ia64_mkelf, linux_ia64_usage }, }; static const int file_types = sizeof(file_type)/sizeof(file_type[0]); void die(char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(1); } /************************************************************************** IPCHKSUM - Checksum IP Header **************************************************************************/ uint16_t ipchksum(const void *data, unsigned long length) { unsigned long sum; unsigned long i; const uint8_t *ptr; /* In the most straight forward way possible, * compute an ip style checksum. */ sum = 0; ptr = data; for(i = 0; i < length; i++) { unsigned long value; value = ptr[i]; if (i & 1) { value <<= 8; } /* Add the new value */ sum += value; /* Wrap around the carry */ if (sum > 0xFFFF) { sum = (sum + (sum >> 16)) & 0xFFFF; } } return (~cpu_to_le16(sum)) & 0xFFFF; } uint16_t add_ipchksums(unsigned long offset, uint16_t sum, uint16_t new) { unsigned long checksum; sum = ~sum & 0xFFFF; new = ~new & 0xFFFF; if (offset & 1) { /* byte swap the sum if it came from an odd offset * since the computation is endian independant this * works. */ new = bswap_16(new); } checksum = sum + new; if (checksum > 0xFFFF) { checksum -= 0xFFFF; } return (~checksum) & 0xFFFF; } void *xmalloc(size_t size, const char *name) { void *buf; buf = malloc(size); if (!buf) { die("Cannot malloc %ld bytes to hold %s: %s\n", size + 0UL, name, strerror(errno)); } return buf; } void *xrealloc(void *ptr, size_t size, const char *name) { void *buf; buf = realloc(ptr, size); if (!buf) { die("Cannot realloc %ld bytes to hold %s: %s\n", size + 0UL, name, strerror(errno)); } return buf; } char *slurp_file(const char *filename, off_t *r_size) { int fd; char *buf; off_t size, progress; ssize_t result; struct stat stats; if (!filename) { *r_size = 0; return 0; } fd = open(filename, O_RDONLY); if (fd < 0) { die("Cannot open `%s': %s\n", filename, strerror(errno)); } result = fstat(fd, &stats); if (result < 0) { die("Cannot stat: %s: %s\n", filename, strerror(errno)); } size = stats.st_size; *r_size = size; buf = xmalloc(size, filename); progress = 0; while(progress < size) { result = read(fd, buf + progress, size - progress); if (result < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; die("read on %s of %ld bytes failed: %s\n", filename, (size - progress)+ 0UL, strerror(errno)); } progress += result; } result = close(fd); if (result < 0) { die("Close of %s failed: %s\n", filename, strerror(errno)); } return buf; } #if HAVE_ZLIB_H char *slurp_decompress_file(const char *filename, off_t *r_size) { gzFile fp; int errnum; const char *msg; char *buf; off_t size, allocated; ssize_t result; if (!filename) { *r_size = 0; return 0; } fp = gzopen(filename, "rb"); if (fp == 0) { msg = gzerror(fp, &errnum); if (errnum == Z_ERRNO) { msg = strerror(errno); } die("Cannot open `%s': %s\n", filename, msg); } size = 0; allocated = 65536; buf = xmalloc(allocated, filename); do { if (size == allocated) { allocated <<= 1; buf = xrealloc(buf, allocated, filename); } result = gzread(fp, buf + size, allocated - size); if (result < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; msg = gzerror(fp, &errnum); if (errnum == Z_ERRNO) { msg = strerror(errno); } die ("read on %s of %ld bytes failed: %s\n", filename, (allocated - size) + 0UL, msg); } size += result; } while(result > 0); result = gzclose(fp); if (result != Z_OK) { msg = gzerror(fp, &errnum); if (errnum == Z_ERRNO) { msg = strerror(errno); } die ("Close of %s failed: %s\n", filename, msg); } *r_size = size; return buf; } #else char *slurp_decompress_file(const char *filename, off_t *r_size) { return slurp_file(filename, r_size); } #endif struct memelfphdr *add_program_headers(struct memelfheader *ehdr, int count) { struct memelfphdr *phdr; int i; ehdr->e_phnum = count; ehdr->e_phdr = phdr = xmalloc(count *sizeof(*phdr), "Program headers"); /* Set the default values */ for(i = 0; i < count; i++) { phdr[i].p_type = PT_LOAD; phdr[i].p_flags = PF_R | PF_W | PF_X; phdr[i].p_vaddr = 0; phdr[i].p_paddr = 0; phdr[i].p_filesz = 0; phdr[i].p_memsz = 0; phdr[i].p_data = 0; } return phdr; } struct memelfnote *add_notes(struct memelfheader *ehdr, int count) { struct memelfnote *notes; ehdr->e_notenum = count; ehdr->e_notes = notes = xmalloc(count *sizeof(*notes), "Notes"); memset(notes, 0, count *sizeof(*notes)); return notes; } static int sizeof_notes(struct memelfnote *note, int notes) { int size; int i; size = 0; for(i = 0; i < notes; i++) { size += sizeof(Elf_Nhdr); size += roundup(strlen(note[i].n_name)+1, 4); size += roundup(note[i].n_descsz, 4); } return size; } static uint16_t cpu_to_elf16(struct memelfheader *ehdr, uint16_t val) { if (ehdr->ei_data == ELFDATA2LSB) { return cpu_to_le16(val); } else if (ehdr->ei_data == ELFDATA2MSB) { return cpu_to_be16(val); } die("Uknown elf layout in cpu_to_elf16"); return 0; } static uint32_t cpu_to_elf32(struct memelfheader *ehdr, uint32_t val) { if (ehdr->ei_data == ELFDATA2LSB) { return cpu_to_le32(val); } else if (ehdr->ei_data == ELFDATA2MSB) { return cpu_to_be32(val); } die("Uknown elf layout in cpu_to_elf32"); return 0; } static uint64_t cpu_to_elf64(struct memelfheader *ehdr, uint64_t val) { if (ehdr->ei_data == ELFDATA2LSB) { return cpu_to_le64(val); } else if (ehdr->ei_data == ELFDATA2MSB) { return cpu_to_be64(val); } die("Uknown elf layout in cpu_to_elf64"); return 0; } static void serialize_notes(char *buf, struct memelfheader *ehdr) { struct Elf_Nhdr hdr; struct memelfnote *note; int notes; size_t size, offset; int i; /* Clear the buffer */ note = ehdr->e_notes; notes = ehdr->e_notenum; size = sizeof_notes(note, notes); memset(buf, 0, size); /* Write the Elf Notes */ offset = 0; for(i = 0; i < notes; i++) { /* Compute the note header */ size_t n_namesz; n_namesz = strlen(note[i].n_name) +1; hdr.n_namesz = cpu_to_elf32(ehdr, n_namesz); hdr.n_descsz = cpu_to_elf32(ehdr, note[i].n_descsz); hdr.n_type = cpu_to_elf32(ehdr, note[i].n_type); /* Copy the note into the buffer */ memcpy(buf + offset, &hdr, sizeof(hdr)); offset += sizeof(hdr); memcpy(buf + offset, note[i].n_name, n_namesz); offset += roundup(n_namesz, 4); memcpy(buf + offset, note[i].n_desc, note[i].n_descsz); offset += roundup(note[i].n_descsz, 4); } } static void serialize_ehdr(char *buf, struct memelfheader *ehdr) { if (ehdr->ei_class == ELFCLASS32) { Elf32_Ehdr *hdr = (Elf32_Ehdr *)buf; hdr->e_ident[EI_MAG0] = ELFMAG0; hdr->e_ident[EI_MAG1] = ELFMAG1; hdr->e_ident[EI_MAG2] = ELFMAG2; hdr->e_ident[EI_MAG3] = ELFMAG3; hdr->e_ident[EI_CLASS] = ehdr->ei_class; hdr->e_ident[EI_DATA] = ehdr->ei_data; hdr->e_ident[EI_VERSION] = EV_CURRENT; hdr->e_type = cpu_to_elf16(ehdr, ehdr->e_type); hdr->e_machine = cpu_to_elf16(ehdr, ehdr->e_machine); hdr->e_version = cpu_to_elf32(ehdr, EV_CURRENT); hdr->e_entry = cpu_to_elf32(ehdr, ehdr->e_entry); hdr->e_phoff = cpu_to_elf32(ehdr, sizeof(*hdr)); hdr->e_shoff = cpu_to_elf32(ehdr, 0); hdr->e_flags = cpu_to_elf32(ehdr, ehdr->e_flags); hdr->e_ehsize = cpu_to_elf16(ehdr, sizeof(*hdr)); hdr->e_phentsize = cpu_to_elf16(ehdr, sizeof(Elf32_Phdr)); hdr->e_phnum = cpu_to_elf16(ehdr, ehdr->e_phnum); hdr->e_shentsize = cpu_to_elf16(ehdr, 0); hdr->e_shnum = cpu_to_elf16(ehdr, 0); hdr->e_shstrndx = cpu_to_elf16(ehdr, 0); } else if (ehdr->ei_class == ELFCLASS64) { Elf64_Ehdr *hdr = (Elf64_Ehdr *)buf; hdr->e_ident[EI_MAG0] = ELFMAG0; hdr->e_ident[EI_MAG1] = ELFMAG1; hdr->e_ident[EI_MAG2] = ELFMAG2; hdr->e_ident[EI_MAG3] = ELFMAG3; hdr->e_ident[EI_CLASS] = ehdr->ei_class; hdr->e_ident[EI_DATA] = ehdr->ei_data; hdr->e_ident[EI_VERSION] = EV_CURRENT; hdr->e_type = cpu_to_elf16(ehdr, ehdr->e_type); hdr->e_machine = cpu_to_elf16(ehdr, ehdr->e_machine); hdr->e_version = cpu_to_elf32(ehdr, EV_CURRENT); hdr->e_entry = cpu_to_elf64(ehdr, ehdr->e_entry); hdr->e_phoff = cpu_to_elf64(ehdr, sizeof(*hdr)); hdr->e_shoff = cpu_to_elf64(ehdr, 0); hdr->e_flags = cpu_to_elf32(ehdr, ehdr->e_flags); hdr->e_ehsize = cpu_to_elf16(ehdr, sizeof(*hdr)); hdr->e_phentsize = cpu_to_elf16(ehdr, sizeof(Elf64_Phdr)); hdr->e_phnum = cpu_to_elf16(ehdr, ehdr->e_phnum); hdr->e_shentsize = cpu_to_elf16(ehdr, 0); hdr->e_shnum = cpu_to_elf16(ehdr, 0); hdr->e_shstrndx = cpu_to_elf16(ehdr, 0); } else die("Uknown elf class: %x\n", ehdr->ei_class); } static void serialize_phdrs(char *buf, struct memelfheader *ehdr, size_t note_size) { int i; size_t offset, note_offset; if (ehdr->ei_class == ELFCLASS32) { Elf32_Phdr *phdr = (Elf32_Phdr *)buf; note_offset = sizeof(Elf32_Ehdr) + (sizeof(Elf32_Phdr)*ehdr->e_phnum); offset = note_offset + note_size; for(i = 0; i < ehdr->e_phnum; i++) { struct memelfphdr *hdr = ehdr->e_phdr + i; phdr[i].p_type = cpu_to_elf32(ehdr, hdr->p_type); phdr[i].p_offset = cpu_to_elf32(ehdr, offset); phdr[i].p_vaddr = cpu_to_elf32(ehdr, hdr->p_vaddr); phdr[i].p_paddr = cpu_to_elf32(ehdr, hdr->p_paddr); phdr[i].p_filesz = cpu_to_elf32(ehdr, hdr->p_filesz); phdr[i].p_memsz = cpu_to_elf32(ehdr, hdr->p_memsz); phdr[i].p_flags = cpu_to_elf32(ehdr, hdr->p_flags); phdr[i].p_align = cpu_to_elf32(ehdr, 0); if (phdr[i].p_type == PT_NOTE) { phdr[i].p_filesz = cpu_to_elf32(ehdr, note_size); phdr[i].p_memsz = cpu_to_elf32(ehdr, note_size); phdr[i].p_offset = cpu_to_elf32(ehdr, note_offset); } else { offset += hdr->p_filesz; } } } else if (ehdr->ei_class == ELFCLASS64) { Elf64_Phdr *phdr = (Elf64_Phdr *)buf; note_offset = sizeof(Elf64_Ehdr) + (sizeof(Elf64_Phdr)*ehdr->e_phnum); offset = note_offset + note_size; for(i = 0; i < ehdr->e_phnum; i++) { struct memelfphdr *hdr = ehdr->e_phdr + i; phdr[i].p_type = cpu_to_elf32(ehdr, hdr->p_type); phdr[i].p_flags = cpu_to_elf32(ehdr, hdr->p_flags); phdr[i].p_offset = cpu_to_elf64(ehdr, offset); phdr[i].p_vaddr = cpu_to_elf64(ehdr, hdr->p_vaddr); phdr[i].p_paddr = cpu_to_elf64(ehdr, hdr->p_paddr); phdr[i].p_filesz = cpu_to_elf64(ehdr, hdr->p_filesz); phdr[i].p_memsz = cpu_to_elf64(ehdr, hdr->p_memsz); phdr[i].p_align = cpu_to_elf64(ehdr, 0); if (phdr[i].p_type == PT_NOTE) { phdr[i].p_filesz = cpu_to_elf64(ehdr, note_size); phdr[i].p_memsz = cpu_to_elf64(ehdr, note_size); phdr[i].p_offset = cpu_to_elf64(ehdr, note_offset); } else { offset += hdr->p_filesz; } } } else { die("Unknwon elf class: %x\n", ehdr->ei_class); } } static void write_buf(int fd, char *buf, size_t size) { size_t progress = 0; ssize_t result; while(progress < size) { result = write(fd, buf + progress, size - progress); if (result < 0) { if ((errno == EAGAIN) || (errno == EINTR)) { continue; } die ("write of %ld bytes failed: %s\n", size - progress, strerror(errno)); } progress += result; } } static void write_elf(struct memelfheader *ehdr, char *output) { size_t ehdr_size; size_t phdr_size; size_t note_size; size_t size; uint16_t checksum; size_t bytes; char *buf; int result, fd; int i; /* Prep for adding the checksum */ for(i = 0; i < ehdr->e_notenum; i++) { if ((memcmp(ehdr->e_notes[i].n_name, "ELFBoot", 8) == 0) && (ehdr->e_notes[i].n_type == EIN_PROGRAM_CHECKSUM)) { ehdr->e_notes[i].n_desc = &checksum; ehdr->e_notes[i].n_descsz = 2; } } /* Compute the sizes */ ehdr_size = 0; phdr_size = 0; note_size = 0; if (ehdr->e_notenum) { note_size = sizeof_notes(ehdr->e_notes, ehdr->e_notenum); } if (ehdr->ei_class == ELFCLASS32) { ehdr_size = sizeof(Elf32_Ehdr); phdr_size = sizeof(Elf32_Phdr) * ehdr->e_phnum; } else if (ehdr->ei_class == ELFCLASS64) { ehdr_size = sizeof(Elf64_Ehdr); phdr_size = sizeof(Elf64_Phdr) * ehdr->e_phnum; } else { die("Unknown elf class: %x\n", ehdr->ei_class); } /* Allocate a buffer to temporarily hold the serialized forms */ size = ehdr_size + phdr_size + note_size; buf = xmalloc(size, "Elf Headers"); memset(buf, 0, size); serialize_ehdr(buf, ehdr); serialize_phdrs(buf + ehdr_size, ehdr, note_size); /* Compute the checksum... */ checksum = ipchksum(buf, ehdr_size + phdr_size); bytes = ehdr_size + phdr_size; for(i = 0; i < ehdr->e_phnum; i++) { checksum = add_ipchksums(bytes, checksum, ipchksum(ehdr->e_phdr[i].p_data, ehdr->e_phdr[i].p_filesz)); bytes += ehdr->e_phdr[i].p_memsz; } /* Compute the final form of the notes */ serialize_notes(buf + ehdr_size + phdr_size, ehdr); /* Now write the elf image */ fd = open(output, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IRGRP | S_IROTH); if (fd < 0) { die("Cannot open ``%s'':%s\n", output, strerror(errno)); } write_buf(fd, buf, size); for(i = 0; i < ehdr->e_phnum; i++) { write_buf(fd, ehdr->e_phdr[i].p_data, ehdr->e_phdr[i].p_filesz); } result = close(fd); if (result < 0) { die("Close on %s failed: %s\n", output, strerror(errno)); } } static void version(void) { printf("mkelfImage " VERSION " released " RELEASE_DATE "\n"); } void usage(void) { int i; version(); printf( "Usage: mkelfImage [OPTION]... \n" "Build an ELF bootable kernel image from a normal kernel image\n" "\n" " -h, --help Print this help.\n" " -v, --version Print the version of kexec.\n" " --kernel= Set the kernel to \n" " --output= Output to \n" " -t, --type=TYPE Specify the new kernel is of .\n" "\n" "Supported kernel types: \n" ); for(i = 0; i < file_types; i++) { printf("%s\n", file_type[i].name); file_type[i].usage(); } printf("\n"); } void error(char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); usage(); exit(1); } int main(int argc, char **argv) { int opt; int fileind; char *type, *kernel, *output; off_t kernel_size; char *kernel_buf; int result; int i; struct memelfheader hdr; static const struct option options[] = { MKELF_OPTIONS { 0, 0, 0, 0 }, }; static const char short_options[] = MKELF_OPT_STR; memset(&hdr, 0, sizeof(hdr)); kernel = 0; output = 0; /* Get the default type from the program name */ type = strrchr(argv[0], '/'); if (!type) type = argv[0]; if (memcmp(type, "mkelf-", 6) == 0) { type = type + 6; } else { type = 0; } opterr = 0; /* Don't complain about unrecognized options here */ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { switch(opt) { case OPT_HELP: usage(); return 0; case OPT_VERSION: version(); return 0; case OPT_KERNEL: kernel = optarg; break; case OPT_OUTPUT: output = optarg; break; case OPT_TYPE: type = optarg; break; default: break; } } fileind = optind; /* Reset getopt for the next pass */ opterr = 1; optind = 1; if (argc - fileind > 0) { kernel = argv[fileind++]; } if (argc - fileind > 0) { output = argv[fileind++]; } if (!kernel) { error("No kernel specified!\n"); } if (!output) { error("No output file specified!\n"); } if (argc - fileind > 0) { error("%d extra options specified!\n", argc - fileind); } /* slurp in the input kernel */ kernel_buf = slurp_decompress_file(kernel, &kernel_size); /* Find/verify the kernel type */ for(i = 0; i < file_types; i++) { char *reason; if (type && (strcmp(type, file_type[i].name) != 0)) { continue; } reason = file_type[i].probe(kernel_buf, kernel_size); if (reason == 0) { break; } if (type) { die("Not %s: %s\n", type, reason); } } if (i == file_types) { die("Can not determine the file type of %s\n", kernel); } result = file_type[i].mkelf(argc, argv, &hdr, kernel_buf, kernel_size); if (result < 0) { die("Cannot create %s result: %d\n", output, result); } /* open the output file */ write_elf(&hdr, output); return 0; }