1 /* $NetBSD: elf.c,v 1.17 2017/11/21 07:56:05 maxv Exp $ */ 2 3 /* 4 * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Maxime Villard. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #define ELFSIZE 64 32 33 #include "prekern.h" 34 #include <sys/exec_elf.h> 35 36 struct elfinfo { 37 Elf_Ehdr *ehdr; 38 Elf_Shdr *shdr; 39 char *shstrtab; 40 size_t shstrsz; 41 Elf_Sym *symtab; 42 size_t symcnt; 43 char *strtab; 44 size_t strsz; 45 }; 46 47 extern paddr_t kernpa_start, kernpa_end; 48 49 static struct elfinfo eif; 50 static const char entrypoint[] = "start_prekern"; 51 52 static int 53 elf_check_header(void) 54 { 55 if (memcmp((char *)eif.ehdr->e_ident, ELFMAG, SELFMAG) != 0 || 56 eif.ehdr->e_ident[EI_CLASS] != ELFCLASS || 57 eif.ehdr->e_type != ET_REL) { 58 return -1; 59 } 60 return 0; 61 } 62 63 static vaddr_t 64 elf_get_entrypoint(void) 65 { 66 Elf_Sym *sym; 67 size_t i; 68 char *buf; 69 70 for (i = 0; i < eif.symcnt; i++) { 71 sym = &eif.symtab[i]; 72 73 if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) 74 continue; 75 if (sym->st_name == 0) 76 continue; 77 if (sym->st_shndx == SHN_UNDEF) 78 continue; /* Skip external references */ 79 buf = eif.strtab + sym->st_name; 80 81 if (!memcmp(buf, entrypoint, sizeof(entrypoint))) { 82 return (vaddr_t)sym->st_value; 83 } 84 } 85 86 return 0; 87 } 88 89 static Elf_Shdr * 90 elf_find_section(char *name) 91 { 92 char *buf; 93 size_t i; 94 95 for (i = 0; i < eif.ehdr->e_shnum; i++) { 96 if (eif.shdr[i].sh_name == 0) { 97 continue; 98 } 99 buf = eif.shstrtab + eif.shdr[i].sh_name; 100 if (!strcmp(name, buf)) { 101 return &eif.shdr[i]; 102 } 103 } 104 105 return NULL; 106 } 107 108 static uintptr_t 109 elf_sym_lookup(size_t symidx) 110 { 111 const Elf_Sym *sym; 112 char *buf, *secname; 113 Elf_Shdr *sec; 114 115 if (symidx == STN_UNDEF) { 116 return 0; 117 } 118 119 if (symidx >= eif.symcnt) { 120 fatal("elf_sym_lookup: symbol beyond table"); 121 } 122 sym = &eif.symtab[symidx]; 123 buf = eif.strtab + sym->st_name; 124 125 if (sym->st_shndx == SHN_UNDEF) { 126 if (!memcmp(buf, "__start_link_set", 16)) { 127 secname = buf + 8; 128 sec = elf_find_section(secname); 129 if (sec == NULL) { 130 fatal("elf_sym_lookup: unknown start link set"); 131 } 132 return (uintptr_t)((uint8_t *)eif.ehdr + 133 sec->sh_offset); 134 } 135 if (!memcmp(buf, "__stop_link_set", 15)) { 136 secname = buf + 7; 137 sec = elf_find_section(secname); 138 if (sec == NULL) { 139 fatal("elf_sym_lookup: unknown stop link set"); 140 } 141 return (uintptr_t)((uint8_t *)eif.ehdr + 142 sec->sh_offset + sec->sh_size); 143 } 144 145 fatal("elf_sym_lookup: external symbol"); 146 } 147 if (sym->st_value == 0) { 148 fatal("elf_sym_lookup: zero value"); 149 } 150 return (uintptr_t)sym->st_value; 151 } 152 153 static void 154 elf_apply_reloc(uintptr_t relocbase, const void *data, bool isrela) 155 { 156 Elf64_Addr *where, val; 157 Elf32_Addr *where32, val32; 158 Elf64_Addr addr; 159 Elf64_Addr addend; 160 uintptr_t rtype, symidx; 161 const Elf_Rel *rel; 162 const Elf_Rela *rela; 163 164 if (isrela) { 165 rela = (const Elf_Rela *)data; 166 where = (Elf64_Addr *)(relocbase + rela->r_offset); 167 addend = rela->r_addend; 168 rtype = ELF_R_TYPE(rela->r_info); 169 symidx = ELF_R_SYM(rela->r_info); 170 } else { 171 rel = (const Elf_Rel *)data; 172 where = (Elf64_Addr *)(relocbase + rel->r_offset); 173 rtype = ELF_R_TYPE(rel->r_info); 174 symidx = ELF_R_SYM(rel->r_info); 175 /* Addend is 32 bit on 32 bit relocs */ 176 switch (rtype) { 177 case R_X86_64_PC32: 178 case R_X86_64_32: 179 case R_X86_64_32S: 180 addend = *(Elf32_Addr *)where; 181 break; 182 default: 183 addend = *where; 184 break; 185 } 186 } 187 188 switch (rtype) { 189 case R_X86_64_NONE: /* none */ 190 break; 191 192 case R_X86_64_64: /* S + A */ 193 addr = elf_sym_lookup(symidx); 194 val = addr + addend; 195 *where = val; 196 break; 197 198 case R_X86_64_PC32: /* S + A - P */ 199 addr = elf_sym_lookup(symidx); 200 where32 = (Elf32_Addr *)where; 201 val32 = (Elf32_Addr)(addr + addend - (Elf64_Addr)where); 202 *where32 = val32; 203 break; 204 205 case R_X86_64_32: /* S + A */ 206 case R_X86_64_32S: /* S + A sign extend */ 207 addr = elf_sym_lookup(symidx); 208 val32 = (Elf32_Addr)(addr + addend); 209 where32 = (Elf32_Addr *)where; 210 *where32 = val32; 211 break; 212 213 case R_X86_64_GLOB_DAT: /* S */ 214 case R_X86_64_JUMP_SLOT:/* XXX need addend + offset */ 215 addr = elf_sym_lookup(symidx); 216 *where = addr; 217 break; 218 219 case R_X86_64_RELATIVE: /* B + A */ 220 addr = relocbase + addend; 221 val = addr; 222 *where = val; 223 break; 224 225 default: 226 fatal("elf_apply_reloc: unexpected relocation type"); 227 } 228 } 229 230 /* -------------------------------------------------------------------------- */ 231 232 size_t 233 elf_get_head_size(vaddr_t headva) 234 { 235 Elf_Ehdr *ehdr; 236 Elf_Shdr *shdr; 237 size_t size; 238 239 ehdr = (Elf_Ehdr *)headva; 240 shdr = (Elf_Shdr *)((uint8_t *)ehdr + ehdr->e_shoff); 241 242 size = (vaddr_t)shdr + (vaddr_t)(ehdr->e_shnum * sizeof(Elf_Shdr)) - 243 (vaddr_t)ehdr; 244 245 return roundup(size, PAGE_SIZE); 246 } 247 248 void 249 elf_build_head(vaddr_t headva) 250 { 251 memset(&eif, 0, sizeof(struct elfinfo)); 252 253 eif.ehdr = (Elf_Ehdr *)headva; 254 eif.shdr = (Elf_Shdr *)((uint8_t *)eif.ehdr + eif.ehdr->e_shoff); 255 256 if (elf_check_header() == -1) { 257 fatal("elf_build_head: wrong kernel ELF header"); 258 } 259 } 260 261 void 262 elf_map_sections(void) 263 { 264 const paddr_t basepa = kernpa_start; 265 const vaddr_t headva = (vaddr_t)eif.ehdr; 266 Elf_Shdr *shdr; 267 int segtype; 268 vaddr_t secva; 269 paddr_t secpa; 270 size_t i, secsz, secalign; 271 272 for (i = 0; i < eif.ehdr->e_shnum; i++) { 273 shdr = &eif.shdr[i]; 274 275 if (!(shdr->sh_flags & SHF_ALLOC)) { 276 continue; 277 } 278 if (shdr->sh_type != SHT_NOBITS && 279 shdr->sh_type != SHT_PROGBITS) { 280 continue; 281 } 282 283 if (shdr->sh_flags & SHF_EXECINSTR) { 284 segtype = BTSEG_TEXT; 285 } else if (shdr->sh_flags & SHF_WRITE) { 286 segtype = BTSEG_DATA; 287 } else { 288 segtype = BTSEG_RODATA; 289 } 290 secpa = basepa + shdr->sh_offset; 291 secsz = shdr->sh_size; 292 secalign = shdr->sh_addralign; 293 ASSERT(shdr->sh_offset != 0); 294 ASSERT(secpa % PAGE_SIZE == 0); 295 ASSERT(secpa + secsz <= kernpa_end); 296 297 secva = mm_map_segment(segtype, secpa, secsz, secalign); 298 299 /* We want (headva + sh_offset) to be the VA of the section. */ 300 ASSERT(secva > headva); 301 shdr->sh_offset = secva - headva; 302 } 303 } 304 305 void 306 elf_build_boot(vaddr_t bootva, paddr_t bootpa) 307 { 308 const paddr_t basepa = kernpa_start; 309 const vaddr_t headva = (vaddr_t)eif.ehdr; 310 size_t i, j, offboot; 311 312 for (i = 0; i < eif.ehdr->e_shnum; i++) { 313 if (eif.shdr[i].sh_type != SHT_STRTAB && 314 eif.shdr[i].sh_type != SHT_REL && 315 eif.shdr[i].sh_type != SHT_RELA && 316 eif.shdr[i].sh_type != SHT_SYMTAB) { 317 continue; 318 } 319 if (eif.shdr[i].sh_offset == 0) { 320 /* hasn't been loaded */ 321 continue; 322 } 323 324 /* Offset of the section within the boot region. */ 325 offboot = basepa + eif.shdr[i].sh_offset - bootpa; 326 327 /* We want (headva + sh_offset) to be the VA of the region. */ 328 eif.shdr[i].sh_offset = (bootva + offboot - headva); 329 } 330 331 /* Locate the section names */ 332 j = eif.ehdr->e_shstrndx; 333 if (j == SHN_UNDEF) { 334 fatal("elf_build_boot: shstrtab not found"); 335 } 336 if (j >= eif.ehdr->e_shnum) { 337 fatal("elf_build_boot: wrong shstrtab index"); 338 } 339 eif.shstrtab = (char *)((uint8_t *)eif.ehdr + eif.shdr[j].sh_offset); 340 eif.shstrsz = eif.shdr[j].sh_size; 341 342 /* Locate the symbol table */ 343 for (i = 0; i < eif.ehdr->e_shnum; i++) { 344 if (eif.shdr[i].sh_type == SHT_SYMTAB) 345 break; 346 } 347 if (i == eif.ehdr->e_shnum) { 348 fatal("elf_build_boot: symtab not found"); 349 } 350 if (eif.shdr[i].sh_offset == 0) { 351 fatal("elf_build_boot: symtab not loaded"); 352 } 353 eif.symtab = (Elf_Sym *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset); 354 eif.symcnt = eif.shdr[i].sh_size / sizeof(Elf_Sym); 355 356 /* Also locate the string table */ 357 j = eif.shdr[i].sh_link; 358 if (j == SHN_UNDEF || j >= eif.ehdr->e_shnum) { 359 fatal("elf_build_boot: wrong strtab index"); 360 } 361 if (eif.shdr[j].sh_type != SHT_STRTAB) { 362 fatal("elf_build_boot: wrong strtab type"); 363 } 364 if (eif.shdr[j].sh_offset == 0) { 365 fatal("elf_build_boot: strtab not loaded"); 366 } 367 eif.strtab = (char *)((uint8_t *)eif.ehdr + eif.shdr[j].sh_offset); 368 eif.strsz = eif.shdr[j].sh_size; 369 } 370 371 vaddr_t 372 elf_kernel_reloc(void) 373 { 374 const vaddr_t baseva = (vaddr_t)eif.ehdr; 375 vaddr_t secva, ent; 376 Elf_Sym *sym; 377 size_t i, j; 378 379 print_state(true, "ELF info created"); 380 381 /* 382 * Update all symbol values with the appropriate offset. 383 */ 384 for (i = 0; i < eif.ehdr->e_shnum; i++) { 385 if (eif.shdr[i].sh_type != SHT_NOBITS && 386 eif.shdr[i].sh_type != SHT_PROGBITS) { 387 continue; 388 } 389 ASSERT(eif.shdr[i].sh_offset != 0); 390 secva = baseva + eif.shdr[i].sh_offset; 391 for (j = 0; j < eif.symcnt; j++) { 392 sym = &eif.symtab[j]; 393 if (sym->st_shndx != i) { 394 continue; 395 } 396 sym->st_value += (Elf_Addr)secva; 397 } 398 } 399 400 print_state(true, "Symbol values updated"); 401 402 /* 403 * Perform relocations without addend if there are any. 404 */ 405 for (i = 0; i < eif.ehdr->e_shnum; i++) { 406 Elf_Rel *reltab, *rel; 407 size_t secidx, nrel; 408 uintptr_t base; 409 410 if (eif.shdr[i].sh_type != SHT_REL) { 411 continue; 412 } 413 ASSERT(eif.shdr[i].sh_offset != 0); 414 reltab = (Elf_Rel *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset); 415 nrel = eif.shdr[i].sh_size / sizeof(Elf_Rel); 416 417 secidx = eif.shdr[i].sh_info; 418 if (secidx >= eif.ehdr->e_shnum) { 419 fatal("elf_kernel_reloc: wrong REL relocation"); 420 } 421 base = (uintptr_t)eif.ehdr + eif.shdr[secidx].sh_offset; 422 423 for (j = 0; j < nrel; j++) { 424 rel = &reltab[j]; 425 elf_apply_reloc(base, rel, false); 426 } 427 } 428 429 print_state(true, "REL relocations applied"); 430 431 /* 432 * Perform relocations with addend if there are any. 433 */ 434 for (i = 0; i < eif.ehdr->e_shnum; i++) { 435 Elf_Rela *relatab, *rela; 436 size_t secidx, nrela; 437 uintptr_t base; 438 439 if (eif.shdr[i].sh_type != SHT_RELA) { 440 continue; 441 } 442 ASSERT(eif.shdr[i].sh_offset != 0); 443 relatab = (Elf_Rela *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset); 444 nrela = eif.shdr[i].sh_size / sizeof(Elf_Rela); 445 446 secidx = eif.shdr[i].sh_info; 447 if (secidx >= eif.ehdr->e_shnum) { 448 fatal("elf_kernel_reloc: wrong RELA relocation"); 449 } 450 base = (uintptr_t)eif.ehdr + eif.shdr[secidx].sh_offset; 451 452 for (j = 0; j < nrela; j++) { 453 rela = &relatab[j]; 454 elf_apply_reloc(base, rela, true); 455 } 456 } 457 458 print_state(true, "RELA relocations applied"); 459 460 /* 461 * Get the entry point. 462 */ 463 ent = elf_get_entrypoint(); 464 if (ent == 0) { 465 fatal("elf_kernel_reloc: entry point not found"); 466 } 467 468 print_state(true, "Entry point found"); 469 470 return ent; 471 } 472