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