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 "ef.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);
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 };
93
94 static void
ef_print_phdr(GElf_Phdr * phdr)95 ef_print_phdr(GElf_Phdr *phdr)
96 {
97
98 if ((phdr->p_flags & PF_W) == 0) {
99 printf("text=0x%jx ", (uintmax_t)phdr->p_filesz);
100 } else {
101 printf("data=0x%jx", (uintmax_t)phdr->p_filesz);
102 if (phdr->p_filesz < phdr->p_memsz)
103 printf("+0x%jx",
104 (uintmax_t)(phdr->p_memsz - phdr->p_filesz));
105 printf(" ");
106 }
107 }
108
109 static GElf_Off
ef_get_offset(elf_file_t ef,GElf_Addr addr)110 ef_get_offset(elf_file_t ef, GElf_Addr addr)
111 {
112 GElf_Phdr *ph;
113 int i;
114
115 for (i = 0; i < ef->ef_nsegs; i++) {
116 ph = ef->ef_segs[i];
117 if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) {
118 return (ph->p_offset + (addr - ph->p_vaddr));
119 }
120 }
121 return (0);
122 }
123
124 /*
125 * next two functions copied from link_elf.c
126 */
127 static int
ef_lookup_symbol(elf_file_t ef,const char * name,GElf_Sym ** sym)128 ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym)
129 {
130 unsigned long hash, symnum;
131 GElf_Sym *symp;
132 char *strp;
133
134 /* First, search hashed global symbols */
135 hash = elf_hash(name);
136 symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
137
138 while (symnum != STN_UNDEF) {
139 if (symnum >= ef->ef_nchains) {
140 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
141 ef->ef_name);
142 return (ENOENT);
143 }
144
145 symp = ef->ef_symtab + symnum;
146 if (symp->st_name == 0) {
147 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
148 ef->ef_name);
149 return (ENOENT);
150 }
151
152 strp = ef->ef_strtab + symp->st_name;
153
154 if (strcmp(name, strp) == 0) {
155 if (symp->st_shndx != SHN_UNDEF ||
156 (symp->st_value != 0 &&
157 GELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
158 *sym = symp;
159 return (0);
160 } else
161 return (ENOENT);
162 }
163
164 symnum = ef->ef_chains[symnum];
165 }
166
167 return (ENOENT);
168 }
169
170 static int
ef_lookup_set(elf_file_t ef,const char * name,GElf_Addr * startp,GElf_Addr * stopp,long * countp)171 ef_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp,
172 GElf_Addr *stopp, long *countp)
173 {
174 GElf_Sym *sym;
175 char *setsym;
176 int error, len;
177
178 len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
179 setsym = malloc(len);
180 if (setsym == NULL)
181 return (errno);
182
183 /* get address of first entry */
184 snprintf(setsym, len, "%s%s", "__start_set_", name);
185 error = ef_lookup_symbol(ef, setsym, &sym);
186 if (error != 0)
187 goto out;
188 *startp = sym->st_value;
189
190 /* get address of last entry */
191 snprintf(setsym, len, "%s%s", "__stop_set_", name);
192 error = ef_lookup_symbol(ef, setsym, &sym);
193 if (error != 0)
194 goto out;
195 *stopp = sym->st_value;
196
197 /* and the number of entries */
198 *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile);
199
200 out:
201 free(setsym);
202 return (error);
203 }
204
205 static GElf_Addr
ef_symaddr(elf_file_t ef,GElf_Size symidx)206 ef_symaddr(elf_file_t ef, GElf_Size symidx)
207 {
208 const GElf_Sym *sym;
209
210 if (symidx >= ef->ef_nchains)
211 return (0);
212 sym = ef->ef_symtab + symidx;
213
214 if (GELF_ST_BIND(sym->st_info) == STB_LOCAL &&
215 sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
216 return (sym->st_value);
217 return (0);
218 }
219
220 static int
ef_parse_dynamic(elf_file_t ef,const GElf_Phdr * phdyn)221 ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn)
222 {
223 GElf_Shdr *shdr;
224 GElf_Dyn *dyn, *dp;
225 size_t i, ndyn, nshdr, nsym;
226 int error;
227 GElf_Off hash_off, sym_off, str_off;
228 GElf_Off rel_off;
229 GElf_Off rela_off;
230 int rel_sz;
231 int rela_sz;
232 int dynamic_idx;
233
234 /*
235 * The kernel linker parses the PT_DYNAMIC segment to find
236 * various important tables. The gelf API of libelf is
237 * section-oriented and requires extracting data from sections
238 * instead of segments (program headers). As a result,
239 * iterate over section headers to read various tables after
240 * parsing values from PT_DYNAMIC.
241 */
242 error = elf_read_shdrs(ef->ef_efile, &nshdr, &shdr);
243 if (error != 0)
244 return (EFTYPE);
245 dyn = NULL;
246
247 /* Find section for .dynamic. */
248 dynamic_idx = -1;
249 for (i = 0; i < nshdr; i++) {
250 if (shdr[i].sh_type == SHT_DYNAMIC) {
251 /*
252 * PowerPC kernels contain additional sections
253 * beyond .dynamic in PT_DYNAMIC due to a linker
254 * script bug. Permit a section with a smaller
255 * size as a workaround.
256 */
257 if (shdr[i].sh_offset != phdyn->p_offset ||
258 ((elf_machine(ef->ef_efile) == EM_PPC ||
259 elf_machine(ef->ef_efile) == EM_PPC64) ?
260 shdr[i].sh_size > phdyn->p_filesz :
261 shdr[i].sh_size != phdyn->p_filesz)) {
262 warnx(".dynamic section doesn't match phdr");
263 error = EFTYPE;
264 goto out;
265 }
266 if (dynamic_idx != -1) {
267 warnx("multiple SHT_DYNAMIC sections");
268 error = EFTYPE;
269 goto out;
270 }
271 dynamic_idx = i;
272 }
273 }
274
275 error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn);
276 if (error != 0)
277 goto out;
278
279 hash_off = rel_off = rela_off = sym_off = str_off = 0;
280 rel_sz = rela_sz = 0;
281 for (i = 0; i < ndyn; i++) {
282 dp = &dyn[i];
283 if (dp->d_tag == DT_NULL)
284 break;
285
286 switch (dp->d_tag) {
287 case DT_HASH:
288 if (hash_off != 0)
289 warnx("second DT_HASH entry ignored");
290 else
291 hash_off = ef_get_offset(ef, dp->d_un.d_ptr);
292 break;
293 case DT_STRTAB:
294 if (str_off != 0)
295 warnx("second DT_STRTAB entry ignored");
296 else
297 str_off = ef_get_offset(ef, dp->d_un.d_ptr);
298 break;
299 case DT_SYMTAB:
300 if (sym_off != 0)
301 warnx("second DT_SYMTAB entry ignored");
302 else
303 sym_off = ef_get_offset(ef, dp->d_un.d_ptr);
304 break;
305 case DT_SYMENT:
306 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
307 ELF_T_SYM)) {
308 error = EFTYPE;
309 goto out;
310 }
311 break;
312 case DT_REL:
313 if (rel_off != 0)
314 warnx("second DT_REL entry ignored");
315 else
316 rel_off = ef_get_offset(ef, dp->d_un.d_ptr);
317 break;
318 case DT_RELSZ:
319 if (rel_sz != 0)
320 warnx("second DT_RELSZ entry ignored");
321 else
322 rel_sz = dp->d_un.d_val;
323 break;
324 case DT_RELENT:
325 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
326 ELF_T_REL)) {
327 error = EFTYPE;
328 goto out;
329 }
330 break;
331 case DT_RELA:
332 if (rela_off != 0)
333 warnx("second DT_RELA entry ignored");
334 else
335 rela_off = ef_get_offset(ef, dp->d_un.d_ptr);
336 break;
337 case DT_RELASZ:
338 if (rela_sz != 0)
339 warnx("second DT_RELSZ entry ignored");
340 else
341 rela_sz = dp->d_un.d_val;
342 break;
343 case DT_RELAENT:
344 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
345 ELF_T_RELA)) {
346 error = EFTYPE;
347 goto out;
348 }
349 break;
350 }
351 }
352 if (hash_off == 0) {
353 warnx("%s: no .hash section found\n", ef->ef_name);
354 error = EFTYPE;
355 goto out;
356 }
357 if (sym_off == 0) {
358 warnx("%s: no .dynsym section found\n", ef->ef_name);
359 error = EFTYPE;
360 goto out;
361 }
362 if (str_off == 0) {
363 warnx("%s: no .dynstr section found\n", ef->ef_name);
364 error = EFTYPE;
365 goto out;
366 }
367 if (rel_off == 0 && rela_off == 0) {
368 warnx("%s: no ELF relocation table found\n", ef->ef_name);
369 error = EFTYPE;
370 goto out;
371 }
372
373 nsym = 0;
374 for (i = 0; i < nshdr; i++) {
375 switch (shdr[i].sh_type) {
376 case SHT_HASH:
377 if (shdr[i].sh_offset != hash_off) {
378 warnx("%s: ignoring SHT_HASH at different offset from DT_HASH",
379 ef->ef_name);
380 break;
381 }
382
383 /*
384 * libelf(3) mentions ELF_T_HASH, but it is
385 * not defined.
386 */
387 if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) {
388 warnx("hash section too small");
389 error = EFTYPE;
390 goto out;
391 }
392 error = elf_read_data(ef->ef_efile, ELF_T_WORD,
393 shdr[i].sh_offset, shdr[i].sh_size,
394 (void **)&ef->ef_hashtab);
395 if (error != 0) {
396 warnc(error, "can't read hash table");
397 goto out;
398 }
399 ef->ef_nbuckets = ef->ef_hashtab[0];
400 ef->ef_nchains = ef->ef_hashtab[1];
401 if ((2 + ef->ef_nbuckets + ef->ef_nchains) *
402 sizeof(*ef->ef_hashtab) != shdr[i].sh_size) {
403 warnx("inconsistent hash section size");
404 error = EFTYPE;
405 goto out;
406 }
407
408 ef->ef_buckets = ef->ef_hashtab + 2;
409 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
410 break;
411 case SHT_DYNSYM:
412 if (shdr[i].sh_offset != sym_off) {
413 warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB",
414 ef->ef_name);
415 break;
416 }
417 error = elf_read_symbols(ef->ef_efile, i, &nsym,
418 &ef->ef_symtab);
419 if (error != 0) {
420 if (ef->ef_verbose)
421 warnx("%s: can't load .dynsym section (0x%jx)",
422 ef->ef_name, (uintmax_t)sym_off);
423 goto out;
424 }
425 break;
426 case SHT_STRTAB:
427 if (shdr[i].sh_offset != str_off)
428 break;
429 error = elf_read_string_table(ef->ef_efile,
430 &shdr[i], &ef->ef_strsz, &ef->ef_strtab);
431 if (error != 0) {
432 warnx("can't load .dynstr section");
433 error = EIO;
434 goto out;
435 }
436 break;
437 case SHT_REL:
438 if (shdr[i].sh_offset != rel_off)
439 break;
440 if (shdr[i].sh_size != rel_sz) {
441 warnx("%s: size mismatch for DT_REL section",
442 ef->ef_name);
443 error = EFTYPE;
444 goto out;
445 }
446 error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz,
447 &ef->ef_rel);
448 if (error != 0) {
449 warnx("%s: cannot load DT_REL section",
450 ef->ef_name);
451 goto out;
452 }
453 break;
454 case SHT_RELA:
455 if (shdr[i].sh_offset != rela_off)
456 break;
457 if (shdr[i].sh_size != rela_sz) {
458 warnx("%s: size mismatch for DT_RELA section",
459 ef->ef_name);
460 error = EFTYPE;
461 goto out;
462 }
463 error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz,
464 &ef->ef_rela);
465 if (error != 0) {
466 warnx("%s: cannot load DT_RELA section",
467 ef->ef_name);
468 goto out;
469 }
470 break;
471 }
472 }
473
474 if (ef->ef_hashtab == NULL) {
475 warnx("%s: did not find a symbol hash table", ef->ef_name);
476 error = EFTYPE;
477 goto out;
478 }
479 if (ef->ef_symtab == NULL) {
480 warnx("%s: did not find a dynamic symbol table", ef->ef_name);
481 error = EFTYPE;
482 goto out;
483 }
484 if (nsym != ef->ef_nchains) {
485 warnx("%s: symbol count mismatch", ef->ef_name);
486 error = EFTYPE;
487 goto out;
488 }
489 if (ef->ef_strtab == NULL) {
490 warnx("%s: did not find a dynamic string table", ef->ef_name);
491 error = EFTYPE;
492 goto out;
493 }
494 if (rel_off != 0 && ef->ef_rel == NULL) {
495 warnx("%s: did not find a DT_REL relocation table",
496 ef->ef_name);
497 error = EFTYPE;
498 goto out;
499 }
500 if (rela_off != 0 && ef->ef_rela == NULL) {
501 warnx("%s: did not find a DT_RELA relocation table",
502 ef->ef_name);
503 error = EFTYPE;
504 goto out;
505 }
506
507 error = 0;
508 out:
509 free(dyn);
510 free(shdr);
511 return (error);
512 }
513
514 static int
ef_seg_read_rel(elf_file_t ef,GElf_Addr address,size_t len,void * dest)515 ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
516 {
517 GElf_Off ofs;
518 const GElf_Rela *a;
519 const GElf_Rel *r;
520 int error;
521
522 ofs = ef_get_offset(ef, address);
523 if (ofs == 0) {
524 if (ef->ef_verbose)
525 warnx("ef_seg_read_rel(%s): bad address (%jx)",
526 ef->ef_name, (uintmax_t)address);
527 return (EFAULT);
528 }
529 error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
530 if (error != 0)
531 return (error);
532
533 for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
534 error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address,
535 len, dest);
536 if (error != 0)
537 return (error);
538 }
539 for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
540 error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address,
541 len, dest);
542 if (error != 0)
543 return (error);
544 }
545 return (0);
546 }
547
548 static int
ef_seg_read_string(elf_file_t ef,GElf_Addr address,size_t len,char * dest)549 ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
550 {
551 GElf_Off ofs;
552
553 ofs = ef_get_offset(ef, address);
554 if (ofs == 0) {
555 if (ef->ef_verbose)
556 warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)",
557 ef->ef_name, (uintmax_t)address, (uintmax_t)ofs);
558 return (EFAULT);
559 }
560
561 return (elf_read_raw_string(ef->ef_efile, ofs, dest, len));
562 }
563
564 int
ef_open(struct elf_file * efile,int verbose)565 ef_open(struct elf_file *efile, int verbose)
566 {
567 elf_file_t ef;
568 GElf_Ehdr *hdr;
569 size_t i, nphdr, nsegs;
570 int error;
571 GElf_Phdr *phdr, *phdyn;
572
573 hdr = &efile->ef_hdr;
574 if (hdr->e_phnum == 0 ||
575 hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) ||
576 hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
577 hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
578 return (EFTYPE);
579
580 ef = malloc(sizeof(*ef));
581 if (ef == NULL)
582 return (errno);
583
584 efile->ef_ef = ef;
585 efile->ef_ops = &ef_file_ops;
586
587 bzero(ef, sizeof(*ef));
588 ef->ef_verbose = verbose;
589 ef->ef_name = strdup(efile->ef_filename);
590 ef->ef_efile = efile;
591
592 error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph);
593 if (error != 0) {
594 phdr = NULL;
595 goto out;
596 }
597
598 error = EFTYPE;
599 nsegs = 0;
600 phdyn = NULL;
601 phdr = ef->ef_ph;
602 for (i = 0; i < nphdr; i++, phdr++) {
603 if (verbose > 1)
604 ef_print_phdr(phdr);
605 switch (phdr->p_type) {
606 case PT_LOAD:
607 if (nsegs < MAXSEGS)
608 ef->ef_segs[nsegs] = phdr;
609 nsegs++;
610 break;
611 case PT_PHDR:
612 break;
613 case PT_DYNAMIC:
614 phdyn = phdr;
615 break;
616 }
617 }
618 if (verbose > 1)
619 printf("\n");
620 if (phdyn == NULL) {
621 warnx("Skipping %s: not dynamically-linked",
622 ef->ef_name);
623 goto out;
624 }
625
626 if (nsegs > MAXSEGS) {
627 warnx("%s: too many segments", ef->ef_name);
628 goto out;
629 }
630 ef->ef_nsegs = nsegs;
631
632 error = ef_parse_dynamic(ef, phdyn);
633 out:
634 if (error != 0)
635 ef_close(ef);
636 return (error);
637 }
638
639 static void
ef_close(elf_file_t ef)640 ef_close(elf_file_t ef)
641 {
642 free(ef->ef_rela);
643 free(ef->ef_rel);
644 free(ef->ef_strtab);
645 free(ef->ef_symtab);
646 free(ef->ef_hashtab);
647 free(ef->ef_ph);
648 if (ef->ef_name)
649 free(ef->ef_name);
650 ef->ef_efile->ef_ops = NULL;
651 ef->ef_efile->ef_ef = NULL;
652 free(ef);
653 }
654