1 /* $OpenBSD: copy_elf.c,v 1.7 2020/04/28 04:17:42 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <stdio.h> 20 #include <err.h> 21 #include <unistd.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include <elf.h> 26 27 #if defined(ELFSIZE) && (ELFSIZE == 32) 28 #define elfoff2h(x) letoh32(x) 29 #define h2elfoff(x) htole32(x) 30 #elif defined(ELFSIZE) && (ELFSIZE == 64) 31 #define elfoff2h(x) letoh64(x) 32 #define h2elfoff(x) htole64(x) 33 #else 34 #error "unknown elf size" 35 #endif 36 37 struct image_header; 38 39 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 40 41 extern u_long copy_data(int, const char *, int, const char *, u_long, 42 struct image_header *, Elf_Word); 43 u_long copy_mem(void *, int, const char *, u_long, struct image_header *, 44 Elf_Word); 45 extern u_long fill_zeroes(int, const char *, u_long, struct image_header *, 46 Elf_Word); 47 48 u_long 49 ELFNAME(copy_elf)(int ifd, const char *iname, int ofd, const char *oname, 50 u_long crc, struct image_header *ih) 51 { 52 ssize_t nbytes; 53 Elf_Ehdr ehdr, elf; 54 Elf_Phdr phdr; 55 Elf_Addr vaddr, ovaddr, svaddr, off, ssym; 56 Elf_Shdr *shp, *wshp; 57 Elf_Addr esym = 0, esymval; 58 int i, sz, havesyms; 59 60 nbytes = read(ifd, &ehdr, sizeof ehdr); 61 if (nbytes == -1) 62 err(1, "%s", iname); 63 if (nbytes != sizeof ehdr) 64 return 0; 65 66 elf = ehdr; 67 68 if (lseek(ifd, (off_t)elfoff2h(elf.e_shoff), SEEK_SET) == -1) 69 err(1, "%s unable to seek to section header", iname); 70 71 sz = letoh16(elf.e_shnum) * sizeof(Elf_Shdr); 72 shp = calloc(sz, 1); 73 if (read(ifd, shp, sz) != sz) 74 err(1, "%s: read section headers", iname); 75 76 wshp = calloc(sz, 1); 77 memcpy(wshp, shp, sz); 78 79 /* first walk the load sections to find the kernel addresses */ 80 /* next we walk the sections to find the 81 * location of esym (first address of data space 82 */ 83 for (i = 0; i < letoh16(ehdr.e_phnum); i++) { 84 if (lseek(ifd, elfoff2h(ehdr.e_phoff) + i * 85 letoh16(ehdr.e_phentsize), SEEK_SET) == (off_t)-1) 86 err(1, "%s", iname); 87 if (read(ifd, &phdr, sizeof phdr) != sizeof(phdr)) 88 err(1, "%s", iname); 89 /* assumes it loads in incrementing address order */ 90 if (letoh32(phdr.p_type) == PT_LOAD) 91 vaddr = elfoff2h(phdr.p_vaddr) + 92 elfoff2h(phdr.p_memsz); 93 } 94 95 /* ok, we need to write the elf header and section header 96 * which contains info about the not yet written section data 97 * however due to crc the data all has to be written in order 98 * which means walking the structures twice once to precompute 99 * the data, once to write the data. 100 */ 101 ssym = vaddr; 102 vaddr += roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr)); 103 off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr)); 104 for (i = 0; i < letoh16(elf.e_shnum); i++) { 105 if (esym == 0 && elfoff2h(shp[i].sh_flags) & SHF_WRITE && 106 elfoff2h(shp[i].sh_flags) & SHF_ALLOC) 107 esym = elfoff2h(shp[i].sh_addr); 108 109 if (letoh32(shp[i].sh_type) == SHT_SYMTAB || 110 letoh32(shp[i].sh_type) == SHT_STRTAB) { 111 #ifdef DEBUG 112 fprintf(stderr, "shdr %d %d/%d off %lx\n", i, 113 letoh32(shp[i].sh_type), roundup(elfoff2h(shp[i].sh_size), 114 sizeof(Elf_Addr)), off); 115 #endif 116 /* data is at shp[i].sh_offset of len shp[i].sh_size */ 117 wshp[i].sh_offset = h2elfoff(off); 118 off += roundup(elfoff2h(shp[i].sh_size), 119 sizeof(Elf_Addr)); 120 vaddr += roundup(elfoff2h(shp[i].sh_size), 121 sizeof(Elf_Addr)); 122 } 123 } 124 esymval = vaddr; 125 #ifdef DEBUG 126 fprintf(stderr, "esymval %lx size %ld\n", esymval, esymval - ssym); 127 #endif 128 129 for (i = 0; i < letoh16(ehdr.e_phnum); i++) { 130 #ifdef DEBUG 131 fprintf(stderr, "phdr %d/%d\n", i, letoh16(ehdr.e_phnum)); 132 #endif 133 if (lseek(ifd, elfoff2h(ehdr.e_phoff) + i * 134 letoh16(ehdr.e_phentsize), SEEK_SET) == (off_t)-1) 135 err(1, "%s", iname); 136 if (read(ifd, &phdr, sizeof phdr) != sizeof(phdr)) 137 err(1, "%s", iname); 138 139 #ifdef DEBUG 140 fprintf(stderr, 141 "vaddr %p type %#x offset %p filesz %p memsz %p\n", 142 elfoff2h(phdr.p_vaddr), letoh32(phdr.p_type), 143 elfoff2h(phdr.p_offset), elfoff2h(phdr.p_filesz), 144 elfoff2h(phdr.p_memsz)); 145 #endif 146 147 switch (letoh32(phdr.p_type)) { 148 case PT_LOAD: 149 break; 150 case PT_NULL: 151 case PT_NOTE: 152 case PT_OPENBSD_RANDOMIZE: 153 #ifdef DEBUG 154 fprintf(stderr, "skipping segment type %#x\n", 155 letoh32(phdr.p_type)); 156 #endif 157 continue; 158 default: 159 errx(1, "unexpected segment type %#x", 160 letoh32(phdr.p_type)); 161 } 162 163 if (i == 0) 164 vaddr = elfoff2h(phdr.p_vaddr); 165 else if (vaddr != elfoff2h(phdr.p_vaddr)) { 166 #ifdef DEBUG 167 fprintf(stderr, "gap %p->%p\n", vaddr, 168 elfoff2h(phdr.p_vaddr)); 169 #endif 170 /* fill the gap between the previous phdr if any */ 171 crc = fill_zeroes(ofd, oname, crc, ih, 172 elfoff2h(phdr.p_vaddr) - vaddr); 173 vaddr = elfoff2h(phdr.p_vaddr); 174 } 175 176 if (elfoff2h(phdr.p_filesz) != 0) { 177 #ifdef DEBUG 178 fprintf(stderr, "copying %p from infile %p\n", 179 elfoff2h(phdr.p_filesz), elfoff2h(phdr.p_offset)); 180 #endif 181 /* esym will be in the data portion of a region */ 182 if (esym >= elfoff2h(phdr.p_vaddr) && 183 esym < elfoff2h(phdr.p_vaddr) + 184 elfoff2h(phdr.p_filesz)) { 185 /* load the region up to the esym 186 * (may be empty) 187 */ 188 Elf_Addr loadlen = esym - 189 elfoff2h(phdr.p_vaddr); 190 191 if (lseek(ifd, elfoff2h(phdr.p_offset), 192 SEEK_SET) == (off_t)-1) 193 err(1, "%s", iname); 194 crc = copy_data(ifd, iname, ofd, oname, crc, 195 ih, loadlen); 196 197 crc = copy_mem(&esymval, ofd, oname, crc, ih, 198 sizeof(esymval)); 199 200 if (lseek(ifd, elfoff2h(phdr.p_offset) + 201 loadlen + sizeof(esymval), SEEK_SET) == 202 (off_t)-1) 203 err(1, "%s", iname); 204 crc = copy_data(ifd, iname, ofd, oname, crc, 205 ih, elfoff2h(phdr.p_filesz) - loadlen - 206 sizeof(esymval)); 207 } else { 208 209 if (lseek(ifd, elfoff2h(phdr.p_offset), 210 SEEK_SET) == (off_t)-1) 211 err(1, "%s", iname); 212 crc = copy_data(ifd, iname, ofd, oname, crc, 213 ih, elfoff2h(phdr.p_filesz)); 214 } 215 if (elfoff2h(phdr.p_memsz) - elfoff2h(phdr.p_filesz) 216 != 0) { 217 #ifdef DEBUG 218 fprintf(stderr, "zeroing %p\n", 219 elfoff2h(phdr.p_memsz) - 220 elfoff2h(phdr.p_filesz)); 221 #endif 222 crc = fill_zeroes(ofd, oname, crc, ih, 223 elfoff2h(phdr.p_memsz) - 224 elfoff2h(phdr.p_filesz)); 225 } 226 ovaddr = vaddr + elfoff2h(phdr.p_memsz); 227 } else { 228 ovaddr = vaddr; 229 } 230 /* 231 * If p_filesz == 0, this is likely .bss, which we do not 232 * need to provide. If it's not the last phdr, the gap 233 * filling code will output the necessary zeroes anyway. 234 */ 235 vaddr += elfoff2h(phdr.p_memsz); 236 } 237 238 vaddr = roundup(vaddr, sizeof(Elf_Addr)); 239 if (vaddr != ovaddr) { 240 #ifdef DEBUG 241 fprintf(stderr, "gap %p->%p\n", vaddr, elfoff2h(phdr.p_vaddr)); 242 #endif 243 /* fill the gap between the previous phdr if not aligned */ 244 crc = fill_zeroes(ofd, oname, crc, ih, vaddr - ovaddr); 245 } 246 247 for (havesyms = i = 0; i < letoh16(elf.e_shnum); i++) 248 if (letoh32(shp[i].sh_type) == SHT_SYMTAB) 249 havesyms = 1; 250 251 if (havesyms == 0) 252 return crc; 253 254 elf.e_phoff = 0; 255 elf.e_shoff = h2elfoff(sizeof(Elf_Ehdr)); 256 elf.e_phentsize = 0; 257 elf.e_phnum = 0; 258 crc = copy_mem(&elf, ofd, oname, crc, ih, sizeof(elf)); 259 crc = copy_mem(wshp, ofd, oname, crc, ih, sz); 260 off = sizeof(elf) + sz; 261 vaddr += sizeof(elf) + sz; 262 263 off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr)); 264 for (i = 0; i < letoh16(elf.e_shnum); i++) { 265 if (letoh32(shp[i].sh_type) == SHT_SYMTAB || 266 letoh32(shp[i].sh_type) == SHT_STRTAB) { 267 Elf_Addr align; 268 /* data is at shp[i].sh_offset of len shp[i].sh_size */ 269 if (lseek(ifd, elfoff2h(shp[i].sh_offset), SEEK_SET) 270 == -1) 271 err(1, "%s", iname); 272 273 off += elfoff2h(shp[i].sh_size); 274 vaddr += elfoff2h(shp[i].sh_size); 275 crc = copy_data(ifd, iname, ofd, oname, crc, ih, 276 elfoff2h(shp[i].sh_size)); 277 align = roundup(elfoff2h(shp[i].sh_size), 278 sizeof(Elf_Addr)) - elfoff2h(shp[i].sh_size); 279 if (align != 0) { 280 vaddr += align; 281 crc = fill_zeroes(ofd, oname, crc, ih, align); 282 } 283 } 284 } 285 286 if (vaddr != esymval) 287 warnx("esymval and vaddr mismatch %llx %llx\n", 288 (long long)esymval, (long long)vaddr); 289 290 return crc; 291 } 292