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