diff options
Diffstat (limited to 'util/mkelfImage/main/mkelfImage.c')
-rw-r--r-- | util/mkelfImage/main/mkelfImage.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/util/mkelfImage/main/mkelfImage.c b/util/mkelfImage/main/mkelfImage.c new file mode 100644 index 0000000000..bb318b3f37 --- /dev/null +++ b/util/mkelfImage/main/mkelfImage.c @@ -0,0 +1,663 @@ +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <getopt.h> +#ifdef HAVE_ZLIB_H +#include <zlib.h> +#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]... <kernel> <elf_kernel>\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=<filename> Set the kernel to <filename>\n" + " --output=<filename> Output to <filename>\n" + " -t, --type=TYPE Specify the new kernel is of <type>.\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; +} |