[BACK]Return to copy_elf.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / mkuboot

File: [local] / src / usr.sbin / mkuboot / copy_elf.c (download)

Revision 1.7, Tue Apr 28 04:17:42 2020 UTC (4 years, 1 month ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, HEAD
Changes since 1.6: +3 -2 lines

use (long long) and %llx to satisfy both 32-bit and 64-bit systems.

/*      $OpenBSD: copy_elf.c,v 1.7 2020/04/28 04:17:42 deraadt 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 <elf.h>

#if defined(ELFSIZE) && (ELFSIZE == 32)
#define elfoff2h(x) letoh32(x)
#define h2elfoff(x) htole32(x)
#elif defined(ELFSIZE) && (ELFSIZE == 64)
#define elfoff2h(x) letoh64(x)
#define h2elfoff(x) htole64(x)
#else
#error "unknown elf size"
#endif

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)elfoff2h(elf.e_shoff), SEEK_SET) == -1)
		err(1, "%s unable to seek to section header", iname);

	sz = letoh16(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 < letoh16(ehdr.e_phnum); i++) {
		if (lseek(ifd, elfoff2h(ehdr.e_phoff) + i *
		    letoh16(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 (letoh32(phdr.p_type) == PT_LOAD)
			vaddr = elfoff2h(phdr.p_vaddr) +
			    elfoff2h(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 < letoh16(elf.e_shnum); i++) {
		if (esym == 0 && elfoff2h(shp[i].sh_flags) & SHF_WRITE &&
		    elfoff2h(shp[i].sh_flags) & SHF_ALLOC)
			esym = elfoff2h(shp[i].sh_addr);

		if (letoh32(shp[i].sh_type) == SHT_SYMTAB ||
		    letoh32(shp[i].sh_type) == SHT_STRTAB) {
#ifdef DEBUG
		fprintf(stderr, "shdr %d %d/%d off %lx\n", i,
		    letoh32(shp[i].sh_type), roundup(elfoff2h(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 = h2elfoff(off);
			off += roundup(elfoff2h(shp[i].sh_size),
			    sizeof(Elf_Addr));
			vaddr += roundup(elfoff2h(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 < letoh16(ehdr.e_phnum); i++) {
#ifdef DEBUG
		fprintf(stderr, "phdr %d/%d\n", i, letoh16(ehdr.e_phnum));
#endif
		if (lseek(ifd, elfoff2h(ehdr.e_phoff) + i *
		    letoh16(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 type %#x offset %p filesz %p memsz %p\n",
		    elfoff2h(phdr.p_vaddr), letoh32(phdr.p_type),
	            elfoff2h(phdr.p_offset), elfoff2h(phdr.p_filesz),
                    elfoff2h(phdr.p_memsz));
#endif

		switch (letoh32(phdr.p_type)) {
		case PT_LOAD:
			break;
		case PT_NULL:
		case PT_NOTE:
		case PT_OPENBSD_RANDOMIZE:
#ifdef DEBUG
			fprintf(stderr, "skipping segment type %#x\n",
			    letoh32(phdr.p_type));
#endif
			continue;
		default:
			errx(1, "unexpected segment type %#x",
			    letoh32(phdr.p_type));
		}

		if (i == 0) 
			vaddr = elfoff2h(phdr.p_vaddr);
		else if (vaddr != elfoff2h(phdr.p_vaddr)) {
#ifdef DEBUG
			fprintf(stderr, "gap %p->%p\n", vaddr,
			    elfoff2h(phdr.p_vaddr));
#endif
			/* fill the gap between the previous phdr if any */
			crc = fill_zeroes(ofd, oname, crc, ih,
			    elfoff2h(phdr.p_vaddr) - vaddr);
			vaddr = elfoff2h(phdr.p_vaddr);
		}

		if (elfoff2h(phdr.p_filesz) != 0) {
#ifdef DEBUG
			fprintf(stderr, "copying %p from infile %p\n",
			   elfoff2h(phdr.p_filesz), elfoff2h(phdr.p_offset));
#endif
			/* esym will be in the data portion of a region */
			if (esym >= elfoff2h(phdr.p_vaddr) &&
			    esym < elfoff2h(phdr.p_vaddr) +
			    elfoff2h(phdr.p_filesz)) {
				/* load the region up to the esym
				 * (may be empty)
				 */
				Elf_Addr loadlen = esym -
				    elfoff2h(phdr.p_vaddr);

				if (lseek(ifd, elfoff2h(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, elfoff2h(phdr.p_offset) +
				    loadlen + sizeof(esymval), SEEK_SET) ==
				    (off_t)-1)
					err(1, "%s", iname);
				crc = copy_data(ifd, iname, ofd, oname, crc,
				    ih, elfoff2h(phdr.p_filesz) - loadlen -
				    sizeof(esymval));
			} else {

				if (lseek(ifd, elfoff2h(phdr.p_offset),
				    SEEK_SET) == (off_t)-1)
					err(1, "%s", iname);
				crc = copy_data(ifd, iname, ofd, oname, crc,
				    ih, elfoff2h(phdr.p_filesz));
			}
			if (elfoff2h(phdr.p_memsz) - elfoff2h(phdr.p_filesz)
			    != 0) {
#ifdef DEBUG
				fprintf(stderr, "zeroing %p\n",
				    elfoff2h(phdr.p_memsz) -
				    elfoff2h(phdr.p_filesz));
#endif
				crc = fill_zeroes(ofd, oname, crc, ih,
				    elfoff2h(phdr.p_memsz) -
				    elfoff2h(phdr.p_filesz));
			}
			ovaddr = vaddr + elfoff2h(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 += elfoff2h(phdr.p_memsz);
	}

	vaddr = roundup(vaddr, sizeof(Elf_Addr));
	if (vaddr != ovaddr) {
#ifdef DEBUG
		fprintf(stderr, "gap %p->%p\n", vaddr, elfoff2h(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 < letoh16(elf.e_shnum); i++)
		if (letoh32(shp[i].sh_type) == SHT_SYMTAB)
			havesyms = 1;

	if (havesyms == 0)
		return crc;

	elf.e_phoff = 0;
	elf.e_shoff = h2elfoff(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;
	vaddr += sizeof(elf) + sz;

	off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr));
	for (i = 0; i < letoh16(elf.e_shnum); i++) {
		if (letoh32(shp[i].sh_type) == SHT_SYMTAB ||
		    letoh32(shp[i].sh_type) == SHT_STRTAB) {
			Elf_Addr align;
			/* data is at shp[i].sh_offset of len shp[i].sh_size */
			if (lseek(ifd, elfoff2h(shp[i].sh_offset), SEEK_SET)
			    == -1)
				err(1, "%s", iname);

			off += elfoff2h(shp[i].sh_size);
			vaddr += elfoff2h(shp[i].sh_size);
			crc = copy_data(ifd, iname, ofd, oname, crc, ih,
			    elfoff2h(shp[i].sh_size));
			align = roundup(elfoff2h(shp[i].sh_size),
			    sizeof(Elf_Addr)) - elfoff2h(shp[i].sh_size);
			if (align != 0) {
				vaddr += align;
				crc = fill_zeroes(ofd, oname, crc, ih, align);
			}
		}
	}

	if (vaddr != esymval)
		warnx("esymval and vaddr mismatch %llx %llx\n",
		    (long long)esymval, (long long)vaddr);

	return crc;
}