#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);
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);
if (letoh32(phdr.p_type) == PT_LOAD)
vaddr = elfoff2h(phdr.p_vaddr) +
elfoff2h(phdr.p_memsz);
}
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
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
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
if (esym >= elfoff2h(phdr.p_vaddr) &&
esym < elfoff2h(phdr.p_vaddr) +
elfoff2h(phdr.p_filesz)) {
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;
}
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
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;
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;
}