]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/kldxref/ef.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / kldxref / ef.c
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$
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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("Skipping %s: not dynamically-linked",
603                             filename);
604                         break;
605                 }
606                 if (ef_read_entry(ef, phdyn->p_offset,
607                         phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
608                         printf("ef_read_entry failed\n");
609                         break;
610                 }
611                 error = ef_parse_dynamic(ef);
612                 if (error)
613                         break;
614                 if (hdr->e_type == ET_DYN) {
615                         ef->ef_type = EFT_KLD;
616 /*                      pad = (u_int)dest & PAGE_MASK;
617                         if (pad)
618                                 dest += PAGE_SIZE - pad;*/
619                         error = 0;
620                 } else if (hdr->e_type == ET_EXEC) {
621 /*                      dest = hdr->e_entry;
622                         if (dest == 0)
623                                 break;*/
624                         ef->ef_type = EFT_KERNEL;
625                         error = 0;
626                 } else
627                         break;
628         } while(0);
629         if (error)
630                 ef_close(ef);
631         return error;
632 }
633
634 static int
635 ef_close(elf_file_t ef)
636 {
637         close(ef->ef_fd);
638 /*      if (ef->ef_fpage)
639                 free(ef->ef_fpage);*/
640         if (ef->ef_name)
641                 free(ef->ef_name);
642         ef->ef_efile->ef_ops = NULL;
643         ef->ef_efile->ef_ef = NULL;
644         free(ef);
645         return 0;
646 }