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