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