1 /*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 2000, Boris Popov
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/param.h>
36
37 #include <err.h>
38 #include <errno.h>
39 #include <gelf.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "kldelf.h"
45
46 #define MAXSEGS 16
47 struct ef_file {
48 char *ef_name;
49 struct elf_file *ef_efile;
50 GElf_Phdr *ef_ph;
51 void *ef_fpage; /* First block of the file */
52 int ef_fplen; /* length of first block */
53 GElf_Hashelt ef_nbuckets;
54 GElf_Hashelt ef_nchains;
55 GElf_Hashelt *ef_buckets;
56 GElf_Hashelt *ef_chains;
57 GElf_Hashelt *ef_hashtab;
58 caddr_t ef_strtab;
59 long ef_strsz;
60 GElf_Sym *ef_symtab;
61 int ef_nsegs;
62 GElf_Phdr *ef_segs[MAXSEGS];
63 int ef_verbose;
64 GElf_Rel *ef_rel; /* relocation table */
65 long ef_relsz; /* number of entries */
66 GElf_Rela *ef_rela; /* relocation table */
67 long ef_relasz; /* number of entries */
68 };
69
70 static void ef_print_phdr(GElf_Phdr *);
71 static GElf_Off ef_get_offset(elf_file_t, GElf_Addr);
72
73 static void ef_close(elf_file_t ef);
74
75 static int ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len,
76 void *dest);
77 static int ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len,
78 char *dest);
79
80 static GElf_Addr ef_symaddr(elf_file_t ef, GElf_Size symidx);
81 static int ef_lookup_set(elf_file_t ef, const char *name,
82 GElf_Addr *startp, GElf_Addr *stopp, long *countp);
83 static int ef_lookup_symbol(elf_file_t ef, const char *name,
84 GElf_Sym **sym, bool see_local);
85
86 static struct elf_file_ops ef_file_ops = {
87 .close = ef_close,
88 .seg_read_rel = ef_seg_read_rel,
89 .seg_read_string = ef_seg_read_string,
90 .symaddr = ef_symaddr,
91 .lookup_set = ef_lookup_set,
92 .lookup_symbol = ef_lookup_symbol,
93 };
94
95 static void
ef_print_phdr(GElf_Phdr * phdr)96 ef_print_phdr(GElf_Phdr *phdr)
97 {
98
99 if ((phdr->p_flags & PF_W) == 0) {
100 printf("text=0x%jx ", (uintmax_t)phdr->p_filesz);
101 } else {
102 printf("data=0x%jx", (uintmax_t)phdr->p_filesz);
103 if (phdr->p_filesz < phdr->p_memsz)
104 printf("+0x%jx",
105 (uintmax_t)(phdr->p_memsz - phdr->p_filesz));
106 printf(" ");
107 }
108 }
109
110 static GElf_Off
ef_get_offset(elf_file_t ef,GElf_Addr addr)111 ef_get_offset(elf_file_t ef, GElf_Addr addr)
112 {
113 GElf_Phdr *ph;
114 int i;
115
116 for (i = 0; i < ef->ef_nsegs; i++) {
117 ph = ef->ef_segs[i];
118 if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) {
119 return (ph->p_offset + (addr - ph->p_vaddr));
120 }
121 }
122 return (0);
123 }
124
125 /*
126 * next two functions copied from link_elf.c
127 */
128 static int
ef_lookup_symbol(elf_file_t ef,const char * name,GElf_Sym ** sym,bool see_local)129 ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym, bool see_local)
130 {
131 unsigned long hash, symnum;
132 GElf_Sym *symp;
133 char *strp;
134
135 /* First, search hashed global symbols */
136 hash = elf_hash(name);
137 symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
138
139 while (symnum != STN_UNDEF) {
140 if (symnum >= ef->ef_nchains) {
141 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
142 ef->ef_name);
143 return (ENOENT);
144 }
145
146 symp = ef->ef_symtab + symnum;
147 if (symp->st_name == 0) {
148 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
149 ef->ef_name);
150 return (ENOENT);
151 }
152
153 strp = ef->ef_strtab + symp->st_name;
154
155 if (strcmp(name, strp) == 0) {
156 if (symp->st_shndx != SHN_UNDEF ||
157 (symp->st_value != 0 &&
158 GELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
159 if (see_local ||
160 GELF_ST_BIND(symp->st_info) != STB_LOCAL) {
161 *sym = symp;
162 return (0);
163 }
164 } else
165 return (ENOENT);
166 }
167
168 symnum = ef->ef_chains[symnum];
169 }
170
171 return (ENOENT);
172 }
173
174 static int
ef_lookup_set(elf_file_t ef,const char * name,GElf_Addr * startp,GElf_Addr * stopp,long * countp)175 ef_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp,
176 GElf_Addr *stopp, long *countp)
177 {
178 GElf_Sym *sym;
179 char *setsym;
180 int error, len;
181
182 len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
183 setsym = malloc(len);
184 if (setsym == NULL)
185 return (errno);
186
187 /* get address of first entry */
188 snprintf(setsym, len, "%s%s", "__start_set_", name);
189 error = ef_lookup_symbol(ef, setsym, &sym, true);
190 if (error != 0)
191 goto out;
192 *startp = sym->st_value;
193
194 /* get address of last entry */
195 snprintf(setsym, len, "%s%s", "__stop_set_", name);
196 error = ef_lookup_symbol(ef, setsym, &sym, true);
197 if (error != 0)
198 goto out;
199 *stopp = sym->st_value;
200
201 /* and the number of entries */
202 *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile);
203
204 out:
205 free(setsym);
206 return (error);
207 }
208
209 static GElf_Addr
ef_symaddr(elf_file_t ef,GElf_Size symidx)210 ef_symaddr(elf_file_t ef, GElf_Size symidx)
211 {
212 const GElf_Sym *sym;
213
214 if (symidx >= ef->ef_nchains)
215 return (0);
216 sym = ef->ef_symtab + symidx;
217
218 if (GELF_ST_BIND(sym->st_info) == STB_LOCAL &&
219 sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
220 return (sym->st_value);
221 return (0);
222 }
223
224 static int
ef_parse_dynamic(elf_file_t ef,const GElf_Phdr * phdyn)225 ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn)
226 {
227 GElf_Shdr *shdr;
228 GElf_Dyn *dyn, *dp;
229 size_t i, ndyn, nshdr, nsym;
230 int error;
231 GElf_Off hash_off, sym_off, str_off;
232 GElf_Off rel_off;
233 GElf_Off rela_off;
234 int rel_sz;
235 int rela_sz;
236 int dynamic_idx;
237
238 /*
239 * The kernel linker parses the PT_DYNAMIC segment to find
240 * various important tables. The gelf API of libelf is
241 * section-oriented and requires extracting data from sections
242 * instead of segments (program headers). As a result,
243 * iterate over section headers to read various tables after
244 * parsing values from PT_DYNAMIC.
245 */
246 error = elf_read_shdrs(ef->ef_efile, &nshdr, &shdr);
247 if (error != 0)
248 return (EFTYPE);
249 dyn = NULL;
250
251 /* Find section for .dynamic. */
252 dynamic_idx = -1;
253 for (i = 0; i < nshdr; i++) {
254 if (shdr[i].sh_type == SHT_DYNAMIC) {
255 /*
256 * PowerPC kernels contain additional sections
257 * beyond .dynamic in PT_DYNAMIC due to a linker
258 * script bug. Permit a section with a smaller
259 * size as a workaround.
260 */
261 if (shdr[i].sh_offset != phdyn->p_offset ||
262 ((elf_machine(ef->ef_efile) == EM_PPC ||
263 elf_machine(ef->ef_efile) == EM_PPC64) ?
264 shdr[i].sh_size > phdyn->p_filesz :
265 shdr[i].sh_size != phdyn->p_filesz)) {
266 warnx(".dynamic section doesn't match phdr");
267 error = EFTYPE;
268 goto out;
269 }
270 if (dynamic_idx != -1) {
271 warnx("multiple SHT_DYNAMIC sections");
272 error = EFTYPE;
273 goto out;
274 }
275 dynamic_idx = i;
276 }
277 }
278
279 error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn);
280 if (error != 0)
281 goto out;
282
283 hash_off = rel_off = rela_off = sym_off = str_off = 0;
284 rel_sz = rela_sz = 0;
285 for (i = 0; i < ndyn; i++) {
286 dp = &dyn[i];
287 if (dp->d_tag == DT_NULL)
288 break;
289
290 switch (dp->d_tag) {
291 case DT_HASH:
292 if (hash_off != 0)
293 warnx("second DT_HASH entry ignored");
294 else
295 hash_off = ef_get_offset(ef, dp->d_un.d_ptr);
296 break;
297 case DT_STRTAB:
298 if (str_off != 0)
299 warnx("second DT_STRTAB entry ignored");
300 else
301 str_off = ef_get_offset(ef, dp->d_un.d_ptr);
302 break;
303 case DT_SYMTAB:
304 if (sym_off != 0)
305 warnx("second DT_SYMTAB entry ignored");
306 else
307 sym_off = ef_get_offset(ef, dp->d_un.d_ptr);
308 break;
309 case DT_SYMENT:
310 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
311 ELF_T_SYM)) {
312 error = EFTYPE;
313 goto out;
314 }
315 break;
316 case DT_REL:
317 if (rel_off != 0)
318 warnx("second DT_REL entry ignored");
319 else
320 rel_off = ef_get_offset(ef, dp->d_un.d_ptr);
321 break;
322 case DT_RELSZ:
323 if (rel_sz != 0)
324 warnx("second DT_RELSZ entry ignored");
325 else
326 rel_sz = dp->d_un.d_val;
327 break;
328 case DT_RELENT:
329 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
330 ELF_T_REL)) {
331 error = EFTYPE;
332 goto out;
333 }
334 break;
335 case DT_RELA:
336 if (rela_off != 0)
337 warnx("second DT_RELA entry ignored");
338 else
339 rela_off = ef_get_offset(ef, dp->d_un.d_ptr);
340 break;
341 case DT_RELASZ:
342 if (rela_sz != 0)
343 warnx("second DT_RELSZ entry ignored");
344 else
345 rela_sz = dp->d_un.d_val;
346 break;
347 case DT_RELAENT:
348 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
349 ELF_T_RELA)) {
350 error = EFTYPE;
351 goto out;
352 }
353 break;
354 }
355 }
356 if (hash_off == 0) {
357 warnx("%s: no .hash section found\n", ef->ef_name);
358 error = EFTYPE;
359 goto out;
360 }
361 if (sym_off == 0) {
362 warnx("%s: no .dynsym section found\n", ef->ef_name);
363 error = EFTYPE;
364 goto out;
365 }
366 if (str_off == 0) {
367 warnx("%s: no .dynstr section found\n", ef->ef_name);
368 error = EFTYPE;
369 goto out;
370 }
371
372 nsym = 0;
373 for (i = 0; i < nshdr; i++) {
374 switch (shdr[i].sh_type) {
375 case SHT_HASH:
376 if (shdr[i].sh_offset != hash_off) {
377 warnx("%s: ignoring SHT_HASH at different offset from DT_HASH",
378 ef->ef_name);
379 break;
380 }
381
382 /*
383 * libelf(3) mentions ELF_T_HASH, but it is
384 * not defined.
385 */
386 if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) {
387 warnx("hash section too small");
388 error = EFTYPE;
389 goto out;
390 }
391 error = elf_read_data(ef->ef_efile, ELF_T_WORD,
392 shdr[i].sh_offset, shdr[i].sh_size,
393 (void **)&ef->ef_hashtab);
394 if (error != 0) {
395 warnc(error, "can't read hash table");
396 goto out;
397 }
398 ef->ef_nbuckets = ef->ef_hashtab[0];
399 ef->ef_nchains = ef->ef_hashtab[1];
400 if ((2 + ef->ef_nbuckets + ef->ef_nchains) *
401 sizeof(*ef->ef_hashtab) != shdr[i].sh_size) {
402 warnx("inconsistent hash section size");
403 error = EFTYPE;
404 goto out;
405 }
406
407 ef->ef_buckets = ef->ef_hashtab + 2;
408 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
409 break;
410 case SHT_DYNSYM:
411 if (shdr[i].sh_offset != sym_off) {
412 warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB",
413 ef->ef_name);
414 break;
415 }
416 error = elf_read_symbols(ef->ef_efile, i, &nsym,
417 &ef->ef_symtab);
418 if (error != 0) {
419 if (ef->ef_verbose)
420 warnx("%s: can't load .dynsym section (0x%jx)",
421 ef->ef_name, (uintmax_t)sym_off);
422 goto out;
423 }
424 break;
425 case SHT_STRTAB:
426 if (shdr[i].sh_offset != str_off)
427 break;
428 error = elf_read_string_table(ef->ef_efile,
429 &shdr[i], &ef->ef_strsz, &ef->ef_strtab);
430 if (error != 0) {
431 warnx("can't load .dynstr section");
432 error = EIO;
433 goto out;
434 }
435 break;
436 case SHT_REL:
437 if (shdr[i].sh_offset != rel_off)
438 break;
439 if (shdr[i].sh_size != rel_sz) {
440 warnx("%s: size mismatch for DT_REL section",
441 ef->ef_name);
442 error = EFTYPE;
443 goto out;
444 }
445 error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz,
446 &ef->ef_rel);
447 if (error != 0) {
448 warnx("%s: cannot load DT_REL section",
449 ef->ef_name);
450 goto out;
451 }
452 break;
453 case SHT_RELA:
454 if (shdr[i].sh_offset != rela_off)
455 break;
456 if (shdr[i].sh_size != rela_sz) {
457 warnx("%s: size mismatch for DT_RELA section",
458 ef->ef_name);
459 error = EFTYPE;
460 goto out;
461 }
462 error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz,
463 &ef->ef_rela);
464 if (error != 0) {
465 warnx("%s: cannot load DT_RELA section",
466 ef->ef_name);
467 goto out;
468 }
469 break;
470 }
471 }
472
473 if (ef->ef_hashtab == NULL) {
474 warnx("%s: did not find a symbol hash table", ef->ef_name);
475 error = EFTYPE;
476 goto out;
477 }
478 if (ef->ef_symtab == NULL) {
479 warnx("%s: did not find a dynamic symbol table", ef->ef_name);
480 error = EFTYPE;
481 goto out;
482 }
483 if (nsym != ef->ef_nchains) {
484 warnx("%s: symbol count mismatch", ef->ef_name);
485 error = EFTYPE;
486 goto out;
487 }
488 if (ef->ef_strtab == NULL) {
489 warnx("%s: did not find a dynamic string table", ef->ef_name);
490 error = EFTYPE;
491 goto out;
492 }
493 if (rel_off != 0 && ef->ef_rel == NULL) {
494 warnx("%s: did not find a DT_REL relocation table",
495 ef->ef_name);
496 error = EFTYPE;
497 goto out;
498 }
499 if (rela_off != 0 && ef->ef_rela == NULL) {
500 warnx("%s: did not find a DT_RELA relocation table",
501 ef->ef_name);
502 error = EFTYPE;
503 goto out;
504 }
505
506 error = 0;
507 out:
508 free(dyn);
509 free(shdr);
510 return (error);
511 }
512
513 static int
ef_seg_read_rel(elf_file_t ef,GElf_Addr address,size_t len,void * dest)514 ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
515 {
516 GElf_Off ofs;
517 const GElf_Rela *a;
518 const GElf_Rel *r;
519 int error;
520
521 ofs = ef_get_offset(ef, address);
522 if (ofs == 0) {
523 if (ef->ef_verbose)
524 warnx("ef_seg_read_rel(%s): bad address (%jx)",
525 ef->ef_name, (uintmax_t)address);
526 return (EFAULT);
527 }
528 error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
529 if (error != 0)
530 return (error);
531
532 for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
533 error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address,
534 len, dest);
535 if (error != 0)
536 return (error);
537 }
538 for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
539 error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address,
540 len, dest);
541 if (error != 0)
542 return (error);
543 }
544 return (0);
545 }
546
547 static int
ef_seg_read_string(elf_file_t ef,GElf_Addr address,size_t len,char * dest)548 ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
549 {
550 GElf_Off ofs;
551
552 ofs = ef_get_offset(ef, address);
553 if (ofs == 0) {
554 if (ef->ef_verbose)
555 warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)",
556 ef->ef_name, (uintmax_t)address, (uintmax_t)ofs);
557 return (EFAULT);
558 }
559
560 return (elf_read_raw_string(ef->ef_efile, ofs, dest, len));
561 }
562
563 int
ef_open(struct elf_file * efile,int verbose)564 ef_open(struct elf_file *efile, int verbose)
565 {
566 elf_file_t ef;
567 GElf_Ehdr *hdr;
568 size_t i, nphdr, nsegs;
569 int error;
570 GElf_Phdr *phdr, *phdyn;
571
572 hdr = &efile->ef_hdr;
573 if (hdr->e_phnum == 0 ||
574 hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) ||
575 hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
576 hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
577 return (EFTYPE);
578
579 ef = malloc(sizeof(*ef));
580 if (ef == NULL)
581 return (errno);
582
583 efile->ef_ef = ef;
584 efile->ef_ops = &ef_file_ops;
585
586 bzero(ef, sizeof(*ef));
587 ef->ef_verbose = verbose;
588 ef->ef_name = strdup(efile->ef_filename);
589 ef->ef_efile = efile;
590
591 error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph);
592 if (error != 0) {
593 phdr = NULL;
594 goto out;
595 }
596
597 error = EFTYPE;
598 nsegs = 0;
599 phdyn = NULL;
600 phdr = ef->ef_ph;
601 for (i = 0; i < nphdr; i++, phdr++) {
602 if (verbose > 1)
603 ef_print_phdr(phdr);
604 switch (phdr->p_type) {
605 case PT_LOAD:
606 if (nsegs < MAXSEGS)
607 ef->ef_segs[nsegs] = phdr;
608 nsegs++;
609 break;
610 case PT_PHDR:
611 break;
612 case PT_DYNAMIC:
613 phdyn = phdr;
614 break;
615 }
616 }
617 if (verbose > 1)
618 printf("\n");
619 if (phdyn == NULL) {
620 warnx("Skipping %s: not dynamically-linked",
621 ef->ef_name);
622 goto out;
623 }
624
625 if (nsegs > MAXSEGS) {
626 warnx("%s: too many segments", ef->ef_name);
627 goto out;
628 }
629 ef->ef_nsegs = nsegs;
630
631 error = ef_parse_dynamic(ef, phdyn);
632 out:
633 if (error != 0)
634 ef_close(ef);
635 return (error);
636 }
637
638 static void
ef_close(elf_file_t ef)639 ef_close(elf_file_t ef)
640 {
641 free(ef->ef_rela);
642 free(ef->ef_rel);
643 free(ef->ef_strtab);
644 free(ef->ef_symtab);
645 free(ef->ef_hashtab);
646 free(ef->ef_ph);
647 if (ef->ef_name)
648 free(ef->ef_name);
649 ef->ef_efile->ef_ops = NULL;
650 ef->ef_efile->ef_ef = NULL;
651 free(ef);
652 }
653