diff options
Diffstat (limited to 'usr.sbin/mkuboot')
-rw-r--r-- | usr.sbin/mkuboot/Makefile | 3 | ||||
-rw-r--r-- | usr.sbin/mkuboot/copy_elf.c | 236 | ||||
-rw-r--r-- | usr.sbin/mkuboot/copy_elf32.c | 3 | ||||
-rw-r--r-- | usr.sbin/mkuboot/copy_elf64.c | 3 | ||||
-rw-r--r-- | usr.sbin/mkuboot/mkuboot.c | 125 |
5 files changed, 289 insertions, 81 deletions
diff --git a/usr.sbin/mkuboot/Makefile b/usr.sbin/mkuboot/Makefile index 50182167520..8533def100c 100644 --- a/usr.sbin/mkuboot/Makefile +++ b/usr.sbin/mkuboot/Makefile @@ -1,9 +1,10 @@ -# $OpenBSD: Makefile,v 1.2 2013/09/04 14:54:13 patrick Exp $ +# $OpenBSD: Makefile,v 1.3 2013/10/28 09:00:06 patrick Exp $ .if ${MACHINE} == "armv7" BINDIR= /usr/sbin PROG= mkuboot +SRCS= mkuboot.c copy_elf32.c copy_elf64.c DPADD= ${LIBZ} LDADD= -lz CFLAGS= -DMACHINE_ARCH=\"${MACHINE_ARCH}\" diff --git a/usr.sbin/mkuboot/copy_elf.c b/usr.sbin/mkuboot/copy_elf.c new file mode 100644 index 00000000000..1b7c603d49e --- /dev/null +++ b/usr.sbin/mkuboot/copy_elf.c @@ -0,0 +1,236 @@ +/* $OpenBSD: copy_elf.c,v 1.1 2013/10/28 09:00:06 patrick Exp $ */ + +/* + * Copyright (c) 2013 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <err.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/exec_elf.h> + +struct image_header; + +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) + +extern u_long copy_data(int, const char *, int, const char *, u_long, + struct image_header *, Elf_Word); +u_long copy_mem(void *, int, const char *, u_long, struct image_header *, + Elf_Word); +extern u_long fill_zeroes(int, const char *, u_long, struct image_header *, Elf_Word); + +u_long +ELFNAME(copy_elf)(int ifd, const char *iname, int ofd, const char *oname, u_long crc, + struct image_header *ih) +{ + ssize_t nbytes; + Elf_Ehdr ehdr, elf; + Elf_Phdr phdr; + Elf_Addr vaddr, ovaddr, svaddr, off, ssym; + Elf_Shdr *shp, *wshp; + Elf_Addr esym = 0, esymval; + int i, sz, havesyms; + + nbytes = read(ifd, &ehdr, sizeof ehdr); + if (nbytes == -1) + err(1, "%s", iname); + if (nbytes != sizeof ehdr) + return 0; + + elf = ehdr; + + if (lseek(ifd, (off_t)elf.e_shoff, SEEK_SET) == -1) { + err(1, "%s unable to seek to section header", iname); + } + + sz = elf.e_shnum * sizeof(Elf_Shdr); + shp = calloc(sz, 1); + if (read(ifd, shp, sz) != sz) { + err(1, "%s: read section headers", iname); + } + wshp = calloc(sz, 1); + memcpy(wshp, shp, sz); + + + /* first walk the load sections to find the kernel addresses */ + /* next we walk the sections to find the + * location of esym (first address of data space + */ + for (i = 0; i < ehdr.e_phnum; i++) { + if (lseek(ifd, ehdr.e_phoff + i * ehdr.e_phentsize, SEEK_SET) == + (off_t)-1) + err(1, "%s", iname); + if (read(ifd, &phdr, sizeof phdr) != sizeof(phdr)) + err(1, "%s", iname); + /* assumes it loads in incrementing address order */ + if (phdr.p_type == PT_LOAD) + vaddr = phdr.p_vaddr + phdr.p_memsz; + } + + /* ok, we need to write the elf header and section header + * which contains info about the not yet written section data + * however due to crc the data all has to be written in order + * which means walking the structures twice once to precompute + * the data, once to write the data. + */ + ssym = vaddr; + vaddr += roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr)); + off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr)); + for (i = 0; i < elf.e_shnum; i++) { + if (esym == 0 && shp[i].sh_flags & SHF_WRITE) { + esym = shp[i].sh_addr; + } + if (shp[i].sh_type == SHT_SYMTAB || + shp[i].sh_type == SHT_STRTAB) { +#ifdef DEBUG + fprintf(stderr, "shdr %d %d/%d off %lx\n", i, shp[i].sh_type, + roundup(shp[i].sh_size, sizeof(Elf_Addr)), off); +#endif + /* data is at shp[i].sh_offset of len shp[i].sh_size */ + wshp[i].sh_offset = off; + off += roundup(shp[i].sh_size, sizeof(Elf_Addr)); + vaddr += roundup(shp[i].sh_size, sizeof(Elf_Addr)); + } + } + esymval = vaddr; +#ifdef DEBUG + fprintf(stderr, "esymval %lx size %ld\n", esymval, esymval - ssym); +#endif + + for (i = 0; i < ehdr.e_phnum; i++) { +#ifdef DEBUG + fprintf(stderr, "phdr %d/%d\n", i, ehdr.e_phnum); +#endif + if (lseek(ifd, ehdr.e_phoff + i * ehdr.e_phentsize, SEEK_SET) == + (off_t)-1) + err(1, "%s", iname); + if (read(ifd, &phdr, sizeof phdr) != sizeof(phdr)) + err(1, "%s", iname); + +#ifdef DEBUG + fprintf(stderr, "vaddr %p offset %p filesz %p memsz %p\n", + phdr.p_vaddr, phdr.p_offset, phdr.p_filesz, phdr.p_memsz); +#endif + if (i == 0) + vaddr = phdr.p_vaddr; + else if (vaddr != phdr.p_vaddr) { +#ifdef DEBUG + fprintf(stderr, "gap %p->%p\n", vaddr, phdr.p_vaddr); +#endif + /* fill the gap between the previous phdr if any */ + crc = fill_zeroes(ofd, oname, crc, ih, + phdr.p_vaddr - vaddr); + vaddr = phdr.p_vaddr; + } + + if (phdr.p_filesz != 0) { +#ifdef DEBUG + fprintf(stderr, "copying %p from infile %p\n", + phdr.p_filesz, phdr.p_offset); +#endif + /* esym will be in the data portion of a region */ + if (esym >= phdr.p_vaddr && + esym < phdr.p_vaddr + phdr.p_filesz) { + /* load the region up to the esym + * (may be empty) + */ + Elf_Addr loadlen = esym - phdr.p_vaddr; + + if (lseek(ifd, phdr.p_offset, SEEK_SET) == + (off_t)-1) + err(1, "%s", iname); + crc = copy_data(ifd, iname, ofd, oname, crc, + ih, loadlen); + + crc = copy_mem(&esymval, ofd, oname, crc, ih, + sizeof(esymval)); + + if (lseek(ifd, phdr.p_offset + loadlen + + sizeof(esymval), SEEK_SET) == (off_t)-1) + err(1, "%s", iname); + crc = copy_data(ifd, iname, ofd, oname, crc, + ih, phdr.p_filesz - loadlen - + sizeof(esymval)); + } else { + + if (lseek(ifd, phdr.p_offset, SEEK_SET) == + (off_t)-1) + err(1, "%s", iname); + crc = copy_data(ifd, iname, ofd, oname, crc, + ih, phdr.p_filesz); + } + if (phdr.p_memsz - phdr.p_filesz != 0) { +#ifdef DEBUG + fprintf(stderr, "zeroing %p\n", + phdr.p_memsz - phdr.p_filesz); +#endif + crc = fill_zeroes(ofd, oname, crc, ih, + phdr.p_memsz - phdr.p_filesz); + } + ovaddr = vaddr + phdr.p_memsz; + } else { + ovaddr = vaddr; + } + /* + * If p_filesz == 0, this is likely .bss, which we do not + * need to provide. If it's not the last phdr, the gap + * filling code will output the necessary zeroes anyway. + */ + vaddr += phdr.p_memsz; + } + + vaddr = roundup(vaddr, sizeof(Elf_Addr)); + if (vaddr != ovaddr) { +#ifdef DEBUG + fprintf(stderr, "gap %p->%p\n", vaddr, phdr.p_vaddr); +#endif + /* fill the gap between the previous phdr if not aligned */ + crc = fill_zeroes(ofd, oname, crc, ih, vaddr - ovaddr); + } + + for (havesyms = i = 0; i < elf.e_shnum; i++) + if (shp[i].sh_type == SHT_SYMTAB) + havesyms = 1; + + if (havesyms == 0) + return crc; + + elf.e_phoff = 0; + elf.e_shoff = sizeof(Elf_Ehdr); + elf.e_phentsize = 0; + elf.e_phnum = 0; + crc = copy_mem(&elf, ofd, oname, crc, ih, sizeof(elf)); + crc = copy_mem(wshp, ofd, oname, crc, ih, sz); + off = sizeof(elf) + sz; + + off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr)); + for (i = 0; i < elf.e_shnum; i++) { + if (shp[i].sh_type == SHT_SYMTAB || + shp[i].sh_type == SHT_STRTAB) { + /* data is at shp[i].sh_offset of len shp[i].sh_size */ + if (lseek(ifd, shp[i].sh_offset, SEEK_SET) == -1) { + err(1, "%s", iname); + } + off += shp[i].sh_size; + crc = copy_data(ifd, iname, ofd, oname, crc, ih, + shp[i].sh_size); + } + } + + return crc; +} diff --git a/usr.sbin/mkuboot/copy_elf32.c b/usr.sbin/mkuboot/copy_elf32.c new file mode 100644 index 00000000000..3d1aaf598e8 --- /dev/null +++ b/usr.sbin/mkuboot/copy_elf32.c @@ -0,0 +1,3 @@ +/* Public domain - not significant enough to copyright */ +#define ELFSIZE 32 +#include "copy_elf.c" diff --git a/usr.sbin/mkuboot/copy_elf64.c b/usr.sbin/mkuboot/copy_elf64.c new file mode 100644 index 00000000000..6e0f294c817 --- /dev/null +++ b/usr.sbin/mkuboot/copy_elf64.c @@ -0,0 +1,3 @@ +/* Public domain - not significant enough to copyright */ +#define ELFSIZE 64 +#include "copy_elf.c" diff --git a/usr.sbin/mkuboot/mkuboot.c b/usr.sbin/mkuboot/mkuboot.c index 94308cec4f0..a3c95b9da31 100644 --- a/usr.sbin/mkuboot/mkuboot.c +++ b/usr.sbin/mkuboot/mkuboot.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mkuboot.c,v 1.2 2013/07/28 18:07:16 miod Exp $ */ +/* $OpenBSD: mkuboot.c,v 1.3 2013/10/28 09:00:06 patrick Exp $ */ /* * Copyright (c) 2008 Mark Kettenis @@ -70,10 +70,18 @@ struct image_header { extern char *__progname; +extern u_long elf32_copy_elf(int, const char *, int, const char *, u_long, + struct image_header *); +extern u_long elf64_copy_elf(int, const char *, int, const char *, u_long, + struct image_header *); + u_long copy_data(int, const char *, int, const char *, u_long, struct image_header *, Elf_Word); -u_long copy_elf(int, const char *, int, const char *, u_long, +u_long (*copy_elf)(int, const char *, int, const char *, u_long, struct image_header *); + +u_long copy_mem(void *, int, const char *, u_long, struct image_header *, + Elf_Word); u_long copy_raw(int, const char *, int, const char *, u_long, struct image_header *); u_long fill_zeroes(int, const char *, u_long, struct image_header *, Elf_Word); @@ -210,7 +218,7 @@ main(int argc, char *argv[]) ih.ih_arch = mapptr->id; ih.ih_type = typemapptr->id; ih.ih_comp = IH_COMP_NONE; - strlcpy(ih.ih_name, imgname, sizeof ih.ih_name); + strlcpy((char *)ih.ih_name, imgname, sizeof ih.ih_name); ifd = open(iname, O_RDONLY); if (ifd < 0) @@ -234,11 +242,11 @@ main(int argc, char *argv[]) if (ih.ih_type == IH_TYPE_SCRIPT) { /* scripts have two extra words of size/pad */ fsize = htobe32(sb.st_size); - crc = crc32(crc, (void *)&fsize, sizeof(fsize)); + crc = crc32(crc, (Bytef *)&fsize, sizeof(fsize)); if (write(ofd, &fsize, sizeof fsize) != sizeof fsize) err(1, "%s", oname); fsize = 0; - crc = crc32(crc, (void *)&fsize, sizeof(fsize)); + crc = crc32(crc, (Bytef *)&fsize, sizeof(fsize)); if (write(ofd, &fsize, sizeof fsize) != sizeof fsize) err(1, "%s", oname); } @@ -256,7 +264,7 @@ main(int argc, char *argv[]) ih.ih_size = htobe32(ih.ih_size); /* Calculate header CRC. */ - crc = crc32(0, (void *)&ih, sizeof ih); + crc = crc32(0, (Bytef *)&ih, sizeof ih); ih.ih_hcrc = htobe32(crc); /* Write finalized header. */ @@ -282,79 +290,16 @@ is_elf(int ifd, const char *iname) if (lseek(ifd, 0, SEEK_SET) != 0) err(1, "%s", iname); - - return IS_ELF(ehdr); -} - -u_long -copy_elf(int ifd, const char *iname, int ofd, const char *oname, u_long crc, - struct image_header *ih) -{ - ssize_t nbytes; - Elf_Ehdr ehdr; - Elf_Phdr phdr; - Elf_Addr vaddr; - int i; - - nbytes = read(ifd, &ehdr, sizeof ehdr); - if (nbytes == -1) - err(1, "%s", iname); - if (nbytes != sizeof ehdr) + if (!IS_ELF(ehdr)) return 0; - for (i = 0; i < ehdr.e_phnum; i++) { -#ifdef DEBUG - fprintf(stderr, "phdr %d/%d\n", i, ehdr.e_phnum); -#endif - if (lseek(ifd, ehdr.e_phoff + i * ehdr.e_phentsize, SEEK_SET) == - (off_t)-1) - err(1, "%s", iname); - if (read(ifd, &phdr, sizeof phdr) != sizeof(phdr)) - err(1, "%s", iname); - -#ifdef DEBUG - fprintf(stderr, "vaddr %p offset %p filesz %p memsz %p\n", - phdr.p_vaddr, phdr.p_offset, phdr.p_filesz, phdr.p_memsz); -#endif - if (i == 0) - vaddr = phdr.p_vaddr; - else if (vaddr != phdr.p_vaddr) { -#ifdef DEBUG - fprintf(stderr, "gap %p->%p\n", vaddr, phdr.p_vaddr); -#endif - /* fill the gap between the previous phdr if any */ - crc = fill_zeroes(ofd, oname, crc, ih, - phdr.p_vaddr - vaddr); - vaddr = phdr.p_vaddr; - } - - if (phdr.p_filesz != 0) { -#ifdef DEBUG - fprintf(stderr, "copying %p from infile %p\n", - phdr.p_filesz, phdr.p_offset); -#endif - if (lseek(ifd, phdr.p_offset, SEEK_SET) == (off_t)-1) - err(1, "%s", iname); - crc = copy_data(ifd, iname, ofd, oname, crc, ih, - phdr.p_filesz); - if (phdr.p_memsz - phdr.p_filesz != 0) { -#ifdef DEBUG - fprintf(stderr, "zeroing %p\n", - phdr.p_memsz - phdr.p_filesz); -#endif - crc = fill_zeroes(ofd, oname, crc, ih, - phdr.p_memsz - phdr.p_filesz); - } - } - /* - * If p_filesz == 0, this is likely .bss, which we do not - * need to provide. If it's not the last phdr, the gap - * filling code will output the necessary zeroes anyway. - */ - vaddr += phdr.p_memsz; - } - - return crc; + if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) + copy_elf = elf32_copy_elf; + else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) + copy_elf = elf64_copy_elf; + else + err(1, "%s: invalid elf, not 32 or 64 bit", iname); + return 1; } u_long @@ -371,7 +316,27 @@ copy_data(int ifd, const char *iname, int ofd, const char *oname, u_long crc, err(1, "%s", iname); if (write(ofd, buf, nbytes) != nbytes) err(1, "%s", oname); - crc = crc32(crc, buf, nbytes); + crc = crc32(crc, (Bytef *)buf, nbytes); + ih->ih_size += nbytes; + size -= nbytes; + } + + return crc; +} + +u_long +copy_mem(void *mem, int ofd, const char *oname, u_long crc, + struct image_header *ih, Elf_Word size) +{ + ssize_t nbytes; + char *memp = (char *)mem; + + while (size != 0) { + nbytes = size > BUFSIZ ? BUFSIZ : size; + if (write(ofd, memp, nbytes) != nbytes) + err(1, "%s", oname); + crc = crc32(crc, (Bytef *)memp, nbytes); + memp += nbytes; ih->ih_size += nbytes; size -= nbytes; } @@ -392,7 +357,7 @@ fill_zeroes(int ofd, const char *oname, u_long crc, struct image_header *ih, nbytes = write(ofd, buf, chunk); if (nbytes != chunk) err(1, "%s", oname); - crc = crc32(crc, buf, nbytes); + crc = crc32(crc, (Bytef *)buf, nbytes); ih->ih_size += nbytes; size -= nbytes; } @@ -412,7 +377,7 @@ copy_raw(int ifd, const char *iname, int ofd, const char *oname, u_long crc, err(1, "%s", iname); if (write(ofd, buf, nbytes) != nbytes) err(1, "%s", oname); - crc = crc32(crc, buf, nbytes); + crc = crc32(crc, (Bytef *)buf, nbytes); ih->ih_size += nbytes; } |