elf.c revision 1.6 1 /* $NetBSD: elf.c,v 1.6 2017/11/01 17:00:17 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 struct {
46 vaddr_t va;
47 size_t sz;
48 } text;
49 struct {
50 vaddr_t va;
51 size_t sz;
52 } rodata;
53 struct {
54 vaddr_t va;
55 size_t sz;
56 } data;
57 };
58
59 extern paddr_t kernpa_start, kernpa_end;
60
61 static struct elfinfo eif;
62 static const char entrypoint[] = "start_prekern";
63
64 /* XXX */
65 static int
66 memcmp(const char *a, const char *b, size_t c)
67 {
68 size_t i;
69 for (i = 0; i < c; i++) {
70 if (a[i] != b[i])
71 return 1;
72 }
73 return 0;
74 }
75 static int
76 strcmp(char *a, char *b)
77 {
78 size_t i;
79 for (i = 0; a[i] != '\0'; i++) {
80 if (a[i] != b[i])
81 return 1;
82 }
83 return 0;
84 }
85
86
87 static int
88 elf_check_header()
89 {
90 if (memcmp((char *)eif.ehdr->e_ident, ELFMAG, SELFMAG) != 0 ||
91 eif.ehdr->e_ident[EI_CLASS] != ELFCLASS ||
92 eif.ehdr->e_type != ET_REL) {
93 return -1;
94 }
95 return 0;
96 }
97
98 static vaddr_t
99 elf_get_entrypoint()
100 {
101 Elf_Sym *sym;
102 size_t i;
103 char *buf;
104
105 for (i = 0; i < eif.symcnt; i++) {
106 sym = &eif.symtab[i];
107
108 if (ELF_ST_TYPE(sym->st_info) != STT_FUNC)
109 continue;
110 if (sym->st_name == 0)
111 continue;
112 if (sym->st_shndx == SHN_UNDEF)
113 continue; /* Skip external references */
114 buf = eif.strtab + sym->st_name;
115
116 if (!memcmp(buf, entrypoint, sizeof(entrypoint))) {
117 return (vaddr_t)sym->st_value;
118 }
119 }
120
121 return 0;
122 }
123
124 static Elf_Shdr *
125 elf_find_section(char *name)
126 {
127 char *buf;
128 size_t i;
129
130 for (i = 0; i < eif.ehdr->e_shnum; i++) {
131 if (eif.shdr[i].sh_name == 0) {
132 continue;
133 }
134 buf = eif.shstrtab + eif.shdr[i].sh_name;
135 if (!strcmp(name, buf)) {
136 return &eif.shdr[i];
137 }
138 }
139
140 return NULL;
141 }
142
143 static uintptr_t
144 elf_sym_lookup(size_t symidx)
145 {
146 const Elf_Sym *sym;
147 char *buf, *secname;
148 Elf_Shdr *sec;
149
150 if (symidx == STN_UNDEF) {
151 return 0;
152 }
153
154 if (symidx >= eif.symcnt) {
155 fatal("elf_sym_lookup: symbol beyond table");
156 }
157 sym = &eif.symtab[symidx];
158 buf = eif.strtab + sym->st_name;
159
160 if (sym->st_shndx == SHN_UNDEF) {
161 if (!memcmp(buf, "__start_link_set", 16)) {
162 secname = buf + 8;
163 sec = elf_find_section(secname);
164 if (sec == NULL) {
165 fatal("elf_sym_lookup: unknown start link set");
166 }
167 return (uintptr_t)((uint8_t *)eif.ehdr +
168 sec->sh_offset);
169 }
170 if (!memcmp(buf, "__stop_link_set", 15)) {
171 secname = buf + 7;
172 sec = elf_find_section(secname);
173 if (sec == NULL) {
174 fatal("elf_sym_lookup: unknown stop link set");
175 }
176 return (uintptr_t)((uint8_t *)eif.ehdr +
177 sec->sh_offset + sec->sh_size);
178 }
179
180 fatal("elf_sym_lookup: external symbol");
181 }
182 if (sym->st_value == 0) {
183 fatal("elf_sym_lookup: zero value");
184 }
185 return (uintptr_t)sym->st_value;
186 }
187
188 static void
189 elf_apply_reloc(uintptr_t relocbase, const void *data, bool isrela)
190 {
191 Elf64_Addr *where, val;
192 Elf32_Addr *where32, val32;
193 Elf64_Addr addr;
194 Elf64_Addr addend;
195 uintptr_t rtype, symidx;
196 const Elf_Rel *rel;
197 const Elf_Rela *rela;
198
199 if (isrela) {
200 rela = (const Elf_Rela *)data;
201 where = (Elf64_Addr *)(relocbase + rela->r_offset);
202 addend = rela->r_addend;
203 rtype = ELF_R_TYPE(rela->r_info);
204 symidx = ELF_R_SYM(rela->r_info);
205 } else {
206 rel = (const Elf_Rel *)data;
207 where = (Elf64_Addr *)(relocbase + rel->r_offset);
208 rtype = ELF_R_TYPE(rel->r_info);
209 symidx = ELF_R_SYM(rel->r_info);
210 /* Addend is 32 bit on 32 bit relocs */
211 switch (rtype) {
212 case R_X86_64_PC32:
213 case R_X86_64_32:
214 case R_X86_64_32S:
215 addend = *(Elf32_Addr *)where;
216 break;
217 default:
218 addend = *where;
219 break;
220 }
221 }
222
223 switch (rtype) {
224 case R_X86_64_NONE: /* none */
225 break;
226
227 case R_X86_64_64: /* S + A */
228 addr = elf_sym_lookup(symidx);
229 val = addr + addend;
230 *where = val;
231 break;
232
233 case R_X86_64_PC32: /* S + A - P */
234 addr = elf_sym_lookup(symidx);
235 where32 = (Elf32_Addr *)where;
236 val32 = (Elf32_Addr)(addr + addend - (Elf64_Addr)where);
237 *where32 = val32;
238 break;
239
240 case R_X86_64_32: /* S + A */
241 case R_X86_64_32S: /* S + A sign extend */
242 addr = elf_sym_lookup(symidx);
243 val32 = (Elf32_Addr)(addr + addend);
244 where32 = (Elf32_Addr *)where;
245 *where32 = val32;
246 break;
247
248 case R_X86_64_GLOB_DAT: /* S */
249 case R_X86_64_JUMP_SLOT:/* XXX need addend + offset */
250 addr = elf_sym_lookup(symidx);
251 *where = addr;
252 break;
253
254 case R_X86_64_RELATIVE: /* B + A */
255 addr = relocbase + addend;
256 val = addr;
257 *where = val;
258 break;
259
260 default:
261 fatal("elf_apply_reloc: unexpected relocation type");
262 }
263 }
264
265 /* -------------------------------------------------------------------------- */
266
267 size_t
268 elf_get_head_size(vaddr_t headva)
269 {
270 Elf_Ehdr *ehdr;
271 Elf_Shdr *shdr;
272 size_t size;
273
274 ehdr = (Elf_Ehdr *)headva;
275 shdr = (Elf_Shdr *)((uint8_t *)ehdr + ehdr->e_shoff);
276
277 size = (vaddr_t)shdr + (vaddr_t)(ehdr->e_shnum * sizeof(Elf_Shdr)) -
278 (vaddr_t)ehdr;
279
280 return roundup(size, PAGE_SIZE);
281 }
282
283 void
284 elf_build_head(vaddr_t headva)
285 {
286 memset(&eif, 0, sizeof(struct elfinfo));
287
288 eif.ehdr = (Elf_Ehdr *)headva;
289 eif.shdr = (Elf_Shdr *)((uint8_t *)eif.ehdr + eif.ehdr->e_shoff);
290
291 if (elf_check_header() == -1) {
292 fatal("elf_build_head: wrong kernel ELF header");
293 }
294 }
295
296 static bool
297 elf_section_is_text(Elf_Shdr *shdr)
298 {
299 if (shdr->sh_type != SHT_NOBITS &&
300 shdr->sh_type != SHT_PROGBITS) {
301 return false;
302 }
303 if (!(shdr->sh_flags & SHF_EXECINSTR)) {
304 return false;
305 }
306 return true;
307 }
308
309 static bool
310 elf_section_is_rodata(Elf_Shdr *shdr)
311 {
312 if (shdr->sh_type != SHT_NOBITS &&
313 shdr->sh_type != SHT_PROGBITS) {
314 return false;
315 }
316 if (shdr->sh_flags & (SHF_EXECINSTR|SHF_WRITE)) {
317 return false;
318 }
319 return true;
320 }
321
322 static bool
323 elf_section_is_data(Elf_Shdr *shdr)
324 {
325 if (shdr->sh_type != SHT_NOBITS &&
326 shdr->sh_type != SHT_PROGBITS) {
327 return false;
328 }
329 if (!(shdr->sh_flags & SHF_WRITE) ||
330 (shdr->sh_flags & SHF_EXECINSTR)) {
331 return false;
332 }
333 return true;
334 }
335
336 void
337 elf_get_text(paddr_t *pa, size_t *sz)
338 {
339 const paddr_t basepa = kernpa_start;
340 paddr_t minpa, maxpa, secpa;
341 size_t i, secsz;
342
343 minpa = 0xFFFFFFFFFFFFFFFF, maxpa = 0;
344 for (i = 0; i < eif.ehdr->e_shnum; i++) {
345 if (!elf_section_is_text(&eif.shdr[i])) {
346 continue;
347 }
348 secpa = basepa + eif.shdr[i].sh_offset;
349 secsz = eif.shdr[i].sh_size;
350 if (secpa < minpa) {
351 minpa = secpa;
352 }
353 if (secpa + secsz > maxpa) {
354 maxpa = secpa + secsz;
355 }
356 }
357 ASSERT(minpa % PAGE_SIZE == 0);
358
359 *pa = minpa;
360 *sz = roundup(maxpa - minpa, PAGE_SIZE);
361 }
362
363 void
364 elf_build_text(vaddr_t textva, paddr_t textpa, size_t textsz)
365 {
366 const paddr_t basepa = kernpa_start;
367 const vaddr_t headva = (vaddr_t)eif.ehdr;
368 size_t i, offtext;
369
370 eif.text.va = textva;
371 eif.text.sz = textsz;
372
373 for (i = 0; i < eif.ehdr->e_shnum; i++) {
374 if (!elf_section_is_text(&eif.shdr[i])) {
375 continue;
376 }
377
378 /* Offset of the section within the text segment. */
379 offtext = basepa + eif.shdr[i].sh_offset - textpa;
380
381 /* We want (headva + sh_offset) to be the VA of the section. */
382 eif.shdr[i].sh_offset = (eif.text.va + offtext - headva);
383 }
384 }
385
386 void
387 elf_get_rodata(paddr_t *pa, size_t *sz)
388 {
389 const paddr_t basepa = kernpa_start;
390 paddr_t minpa, maxpa, secpa;
391 size_t i, secsz;
392
393 minpa = 0xFFFFFFFFFFFFFFFF, maxpa = 0;
394 for (i = 0; i < eif.ehdr->e_shnum; i++) {
395 if (!elf_section_is_rodata(&eif.shdr[i])) {
396 continue;
397 }
398 secpa = basepa + eif.shdr[i].sh_offset;
399 secsz = eif.shdr[i].sh_size;
400 if (secpa < minpa) {
401 minpa = secpa;
402 }
403 if (secpa + secsz > maxpa) {
404 maxpa = secpa + secsz;
405 }
406 }
407 ASSERT(minpa % PAGE_SIZE == 0);
408
409 *pa = minpa;
410 *sz = roundup(maxpa - minpa, PAGE_SIZE);
411 }
412
413 void
414 elf_build_rodata(vaddr_t rodatava, paddr_t rodatapa, size_t rodatasz)
415 {
416 const paddr_t basepa = kernpa_start;
417 const vaddr_t headva = (vaddr_t)eif.ehdr;
418 size_t i, offrodata;
419
420 eif.rodata.va = rodatava;
421 eif.rodata.sz = rodatasz;
422
423 for (i = 0; i < eif.ehdr->e_shnum; i++) {
424 if (!elf_section_is_rodata(&eif.shdr[i])) {
425 continue;
426 }
427
428 /* Offset of the section within the rodata segment. */
429 offrodata = basepa + eif.shdr[i].sh_offset - rodatapa;
430
431 /* We want (headva + sh_offset) to be the VA of the section. */
432 eif.shdr[i].sh_offset = (eif.rodata.va + offrodata - headva);
433 }
434 }
435
436 void
437 elf_get_data(paddr_t *pa, size_t *sz)
438 {
439 const paddr_t basepa = kernpa_start;
440 paddr_t minpa, maxpa, secpa;
441 size_t i, secsz;
442
443 minpa = 0xFFFFFFFFFFFFFFFF, maxpa = 0;
444 for (i = 0; i < eif.ehdr->e_shnum; i++) {
445 if (!elf_section_is_data(&eif.shdr[i])) {
446 continue;
447 }
448 secpa = basepa + eif.shdr[i].sh_offset;
449 secsz = eif.shdr[i].sh_size;
450 if (secpa < minpa) {
451 minpa = secpa;
452 }
453 if (secpa + secsz > maxpa) {
454 maxpa = secpa + secsz;
455 }
456 }
457 ASSERT(minpa % PAGE_SIZE == 0);
458
459 *pa = minpa;
460 *sz = roundup(maxpa - minpa, PAGE_SIZE);
461 }
462
463 void
464 elf_build_data(vaddr_t datava, paddr_t datapa, size_t datasz)
465 {
466 const paddr_t basepa = kernpa_start;
467 const vaddr_t headva = (vaddr_t)eif.ehdr;
468 size_t i, offdata;
469
470 eif.data.va = datava;
471 eif.data.sz = datasz;
472
473 for (i = 0; i < eif.ehdr->e_shnum; i++) {
474 if (!elf_section_is_data(&eif.shdr[i])) {
475 continue;
476 }
477
478 /* Offset of the section within the data segment. */
479 offdata = basepa + eif.shdr[i].sh_offset - datapa;
480
481 /* We want (headva + sh_offset) to be the VA of the section. */
482 eif.shdr[i].sh_offset = (eif.data.va + offdata - headva);
483 }
484 }
485
486 void
487 elf_build_boot(vaddr_t bootva, paddr_t bootpa)
488 {
489 const paddr_t basepa = kernpa_start;
490 const vaddr_t headva = (vaddr_t)eif.ehdr;
491 size_t i, j, offboot;
492
493 for (i = 0; i < eif.ehdr->e_shnum; i++) {
494 if (eif.shdr[i].sh_type != SHT_STRTAB &&
495 eif.shdr[i].sh_type != SHT_REL &&
496 eif.shdr[i].sh_type != SHT_RELA &&
497 eif.shdr[i].sh_type != SHT_SYMTAB) {
498 continue;
499 }
500 if (eif.shdr[i].sh_offset == 0) {
501 /* hasn't been loaded */
502 continue;
503 }
504
505 /* Offset of the section within the boot region. */
506 offboot = basepa + eif.shdr[i].sh_offset - bootpa;
507
508 /* We want (headva + sh_offset) to be the VA of the region. */
509 eif.shdr[i].sh_offset = (bootva + offboot - headva);
510 }
511
512 /* Locate the section names */
513 j = eif.ehdr->e_shstrndx;
514 if (j == SHN_UNDEF) {
515 fatal("elf_build_boot: shstrtab not found");
516 }
517 if (j >= eif.ehdr->e_shnum) {
518 fatal("elf_build_boot: wrong shstrtab index");
519 }
520 eif.shstrtab = (char *)((uint8_t *)eif.ehdr + eif.shdr[j].sh_offset);
521 eif.shstrsz = eif.shdr[j].sh_size;
522
523 /* Locate the symbol table */
524 for (i = 0; i < eif.ehdr->e_shnum; i++) {
525 if (eif.shdr[i].sh_type == SHT_SYMTAB)
526 break;
527 }
528 if (i == eif.ehdr->e_shnum) {
529 fatal("elf_build_boot: symtab not found");
530 }
531 eif.symtab = (Elf_Sym *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset);
532 eif.symcnt = eif.shdr[i].sh_size / sizeof(Elf_Sym);
533
534 /* Also locate the string table */
535 j = eif.shdr[i].sh_link;
536 if (j == SHN_UNDEF || j >= eif.ehdr->e_shnum) {
537 fatal("elf_build_boot: wrong strtab index");
538 }
539 if (eif.shdr[j].sh_type != SHT_STRTAB) {
540 fatal("elf_build_boot: wrong strtab type");
541 }
542 eif.strtab = (char *)((uint8_t *)eif.ehdr + eif.shdr[j].sh_offset);
543 eif.strsz = eif.shdr[j].sh_size;
544 }
545
546 vaddr_t
547 elf_kernel_reloc()
548 {
549 const vaddr_t baseva = (vaddr_t)eif.ehdr;
550 vaddr_t secva, ent;
551 Elf_Sym *sym;
552 size_t i, j;
553
554 print_state(true, "ELF info created");
555
556 /*
557 * The loaded sections are: SHT_PROGBITS, SHT_NOBITS, SHT_STRTAB,
558 * SHT_SYMTAB.
559 */
560
561 /*
562 * Update all symbol values with the appropriate offset.
563 */
564 for (i = 0; i < eif.ehdr->e_shnum; i++) {
565 if (eif.shdr[i].sh_type != SHT_NOBITS &&
566 eif.shdr[i].sh_type != SHT_PROGBITS) {
567 continue;
568 }
569 secva = baseva + eif.shdr[i].sh_offset;
570 for (j = 0; j < eif.symcnt; j++) {
571 sym = &eif.symtab[j];
572 if (sym->st_shndx != i) {
573 continue;
574 }
575 sym->st_value += (Elf_Addr)secva;
576 }
577 }
578
579 print_state(true, "Symbol values updated");
580
581 /*
582 * Perform relocations without addend if there are any.
583 */
584 for (i = 0; i < eif.ehdr->e_shnum; i++) {
585 Elf_Rel *reltab, *rel;
586 size_t secidx, nrel;
587 uintptr_t base;
588
589 if (eif.shdr[i].sh_type != SHT_REL)
590 continue;
591
592 reltab = (Elf_Rel *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset);
593 nrel = eif.shdr[i].sh_size / sizeof(Elf_Rel);
594
595 secidx = eif.shdr[i].sh_info;
596 if (secidx >= eif.ehdr->e_shnum) {
597 fatal("elf_kernel_reloc: wrong REL relocation");
598 }
599 base = (uintptr_t)eif.ehdr + eif.shdr[secidx].sh_offset;
600
601 for (j = 0; j < nrel; j++) {
602 rel = &reltab[j];
603 elf_apply_reloc(base, rel, false);
604 }
605 }
606
607 print_state(true, "REL relocations applied");
608
609 /*
610 * Perform relocations with addend if there are any.
611 */
612 for (i = 0; i < eif.ehdr->e_shnum; i++) {
613 Elf_Rela *relatab, *rela;
614 size_t secidx, nrela;
615 uintptr_t base;
616
617 if (eif.shdr[i].sh_type != SHT_RELA)
618 continue;
619
620 relatab = (Elf_Rela *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset);
621 nrela = eif.shdr[i].sh_size / sizeof(Elf_Rela);
622
623 secidx = eif.shdr[i].sh_info;
624 if (secidx >= eif.ehdr->e_shnum) {
625 fatal("elf_kernel_reloc: wrong RELA relocation");
626 }
627 base = (uintptr_t)eif.ehdr + eif.shdr[secidx].sh_offset;
628
629 for (j = 0; j < nrela; j++) {
630 rela = &relatab[j];
631 elf_apply_reloc(base, rela, true);
632 }
633 }
634
635 print_state(true, "RELA relocations applied");
636
637 /*
638 * Get the entry point.
639 */
640 ent = elf_get_entrypoint(&eif);
641 if (ent == 0) {
642 fatal("elf_kernel_reloc: entry point not found");
643 }
644
645 print_state(true, "Entry point found");
646
647 /*
648 * Remap the code segments with proper permissions.
649 */
650 mm_mprotect(eif.text.va, eif.text.sz, MM_PROT_READ|MM_PROT_EXECUTE);
651 mm_mprotect(eif.rodata.va, eif.rodata.sz, MM_PROT_READ);
652 mm_mprotect(eif.data.va, eif.data.sz, MM_PROT_READ|MM_PROT_WRITE);
653
654 print_state(true, "Segments protection updated");
655
656 return ent;
657 }
658
659