elf.c revision 1.2 1 /* $NetBSD: elf.c,v 1.2 2017/10/11 16:21:06 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 void
260 elf_build_info(vaddr_t baseva)
261 {
262 vaddr_t secva, minva, maxva;
263 size_t secsz;
264 size_t i, j;
265
266 memset(&eif, 0, sizeof(struct elfinfo));
267
268 eif.ehdr = (Elf_Ehdr *)baseva;
269 eif.shdr = (Elf_Shdr *)((uint8_t *)eif.ehdr + eif.ehdr->e_shoff);
270
271 if (elf_check_header(&eif) == -1) {
272 fatal("elf_build_info: wrong kernel ELF header");
273 }
274
275 /* Locate the section names */
276 j = eif.ehdr->e_shstrndx;
277 if (j == SHN_UNDEF) {
278 fatal("elf_build_info: shstrtab not found");
279 }
280 if (j >= eif.ehdr->e_shnum) {
281 fatal("elf_build_info: wrong shstrtab index");
282 }
283 eif.shstrtab = (char *)((uint8_t *)eif.ehdr + eif.shdr[j].sh_offset);
284 eif.shstrsz = eif.shdr[j].sh_size;
285
286 /* Locate the symbol table */
287 for (i = 0; i < eif.ehdr->e_shnum; i++) {
288 if (eif.shdr[i].sh_type == SHT_SYMTAB)
289 break;
290 }
291 if (i == eif.ehdr->e_shnum) {
292 fatal("elf_build_info: symtab not found");
293 }
294 eif.symtab = (Elf_Sym *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset);
295 eif.symcnt = eif.shdr[i].sh_size / sizeof(Elf_Sym);
296
297 /* Also locate the string table */
298 j = eif.shdr[i].sh_link;
299 if (j == SHN_UNDEF || j >= eif.ehdr->e_shnum) {
300 fatal("elf_build_info: wrong strtab index");
301 }
302 if (eif.shdr[j].sh_type != SHT_STRTAB) {
303 fatal("elf_build_info: wrong strtab type");
304 }
305 eif.strtab = (char *)((uint8_t *)eif.ehdr + eif.shdr[j].sh_offset);
306 eif.strsz = eif.shdr[j].sh_size;
307
308 /*
309 * Save the locations of the kernel segments. Attention: there is a
310 * difference between "segment" and "section". A segment can contain
311 * several sections.
312 */
313
314 /* text */
315 minva = 0xFFFFFFFFFFFFFFFF, maxva = 0;
316 for (i = 0; i < eif.ehdr->e_shnum; i++) {
317 if (eif.shdr[i].sh_type != SHT_NOBITS &&
318 eif.shdr[i].sh_type != SHT_PROGBITS) {
319 continue;
320 }
321 if (!(eif.shdr[i].sh_flags & SHF_EXECINSTR)) {
322 continue;
323 }
324 secva = baseva + eif.shdr[i].sh_offset;
325 secsz = eif.shdr[i].sh_size;
326 if (secva < minva) {
327 minva = secva;
328 }
329 if (secva + secsz > maxva) {
330 maxva = secva + secsz;
331 }
332 }
333 eif.text.va = minva;
334 eif.text.sz = roundup(maxva - minva, PAGE_SIZE);
335 ASSERT(eif.text.va % PAGE_SIZE == 0);
336
337 /* rodata */
338 minva = 0xFFFFFFFFFFFFFFFF, maxva = 0;
339 for (i = 0; i < eif.ehdr->e_shnum; i++) {
340 if (eif.shdr[i].sh_type != SHT_NOBITS &&
341 eif.shdr[i].sh_type != SHT_PROGBITS) {
342 continue;
343 }
344 if ((eif.shdr[i].sh_flags & (SHF_EXECINSTR|SHF_WRITE))) {
345 continue;
346 }
347 secva = baseva + eif.shdr[i].sh_offset;
348 secsz = eif.shdr[i].sh_size;
349 if (secva < minva) {
350 minva = secva;
351 }
352 if (secva + secsz > maxva) {
353 maxva = secva + secsz;
354 }
355 }
356 eif.rodata.va = minva;
357 eif.rodata.sz = roundup(maxva - minva, PAGE_SIZE);
358 ASSERT(eif.rodata.va % PAGE_SIZE == 0);
359
360 /* data */
361 minva = 0xFFFFFFFFFFFFFFFF, maxva = 0;
362 for (i = 0; i < eif.ehdr->e_shnum; i++) {
363 if (eif.shdr[i].sh_type != SHT_NOBITS &&
364 eif.shdr[i].sh_type != SHT_PROGBITS) {
365 continue;
366 }
367 if (!(eif.shdr[i].sh_flags & SHF_WRITE) ||
368 (eif.shdr[i].sh_flags & SHF_EXECINSTR)) {
369 continue;
370 }
371 secva = baseva + eif.shdr[i].sh_offset;
372 secsz = eif.shdr[i].sh_size;
373 if (secva < minva) {
374 minva = secva;
375 }
376 if (secva + secsz > maxva) {
377 maxva = secva + secsz;
378 }
379 }
380 eif.data.va = minva;
381 eif.data.sz = roundup(maxva - minva, PAGE_SIZE);
382 ASSERT(eif.data.va % PAGE_SIZE == 0);
383 }
384
385 vaddr_t
386 elf_kernel_reloc(vaddr_t baseva)
387 {
388 vaddr_t secva, ent;
389 Elf_Sym *sym;
390 size_t i, j;
391
392 elf_build_info(baseva);
393
394 print_state(true, "ELF info created");
395
396 /*
397 * The loaded sections are: SHT_PROGBITS, SHT_NOBITS, SHT_STRTAB,
398 * SHT_SYMTAB.
399 */
400
401 /*
402 * Update all symbol values with the appropriate offset.
403 */
404 for (i = 0; i < eif.ehdr->e_shnum; i++) {
405 if (eif.shdr[i].sh_type != SHT_NOBITS &&
406 eif.shdr[i].sh_type != SHT_PROGBITS) {
407 continue;
408 }
409 secva = baseva + eif.shdr[i].sh_offset;
410 for (j = 0; j < eif.symcnt; j++) {
411 sym = &eif.symtab[j];
412 if (sym->st_shndx != i) {
413 continue;
414 }
415 sym->st_value += (Elf_Addr)secva;
416 }
417 }
418
419 print_state(true, "Symbol values updated");
420
421 /*
422 * Perform relocations without addend if there are any.
423 */
424 for (i = 0; i < eif.ehdr->e_shnum; i++) {
425 Elf_Rel *reltab, *rel;
426 size_t secidx, nrel;
427 uintptr_t base;
428
429 if (eif.shdr[i].sh_type != SHT_REL)
430 continue;
431
432 reltab = (Elf_Rel *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset);
433 nrel = eif.shdr[i].sh_size / sizeof(Elf_Rel);
434
435 secidx = eif.shdr[i].sh_info;
436 if (secidx >= eif.ehdr->e_shnum) {
437 fatal("elf_kernel_reloc: wrong REL relocation");
438 }
439 base = (uintptr_t)eif.ehdr + eif.shdr[secidx].sh_offset;
440
441 for (j = 0; j < nrel; j++) {
442 rel = &reltab[j];
443 elf_apply_reloc(base, rel, false);
444 }
445 }
446
447 print_state(true, "REL relocations applied");
448
449 /*
450 * Perform relocations with addend if there are any.
451 */
452 for (i = 0; i < eif.ehdr->e_shnum; i++) {
453 Elf_Rela *relatab, *rela;
454 size_t secidx, nrela;
455 uintptr_t base;
456
457 if (eif.shdr[i].sh_type != SHT_RELA)
458 continue;
459
460 relatab = (Elf_Rela *)((uint8_t *)eif.ehdr + eif.shdr[i].sh_offset);
461 nrela = eif.shdr[i].sh_size / sizeof(Elf_Rela);
462
463 secidx = eif.shdr[i].sh_info;
464 if (secidx >= eif.ehdr->e_shnum) {
465 fatal("elf_kernel_reloc: wrong RELA relocation");
466 }
467 base = (uintptr_t)eif.ehdr + eif.shdr[secidx].sh_offset;
468
469 for (j = 0; j < nrela; j++) {
470 rela = &relatab[j];
471 elf_apply_reloc(base, rela, true);
472 }
473 }
474
475 print_state(true, "RELA relocations applied");
476
477 /*
478 * Get the entry point.
479 */
480 ent = elf_get_entrypoint(&eif);
481 if (ent == 0) {
482 fatal("elf_kernel_reloc: entry point not found");
483 }
484
485 print_state(true, "Entry point found");
486
487 /*
488 * Remap the code segments with proper permissions.
489 */
490 mm_mprotect(eif.text.va, eif.text.sz, MM_PROT_READ|MM_PROT_EXECUTE);
491 mm_mprotect(eif.rodata.va, eif.rodata.sz, MM_PROT_READ);
492 mm_mprotect(eif.data.va, eif.data.sz, MM_PROT_READ|MM_PROT_WRITE);
493
494 print_state(true, "Segments protection updated");
495
496 return ent;
497 }
498
499 void
500 elf_get_text(vaddr_t *va, paddr_t *pa, size_t *sz)
501 {
502 *va = eif.text.va;
503 *pa = mm_vatopa(eif.text.va);
504 *sz = eif.text.sz;
505 }
506
507 void
508 elf_get_rodata(vaddr_t *va, paddr_t *pa, size_t *sz)
509 {
510 *va = eif.rodata.va;
511 *pa = mm_vatopa(eif.rodata.va);
512 *sz = eif.rodata.sz;
513 }
514
515 void
516 elf_get_data(vaddr_t *va, paddr_t *pa, size_t *sz)
517 {
518 *va = eif.data.va;
519 *pa = mm_vatopa(eif.data.va);
520 *sz = eif.data.sz;
521 }
522