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