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;
}