]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/kldxref/ef.c
periodic: Make daily diff(1) flags configurable with daily_diff_flags
[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
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
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
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
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
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
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
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                         if (shdr[i].sh_offset != phdyn->p_offset ||
252                             shdr[i].sh_size != phdyn->p_filesz) {
253                                 warnx(".dynamic section doesn't match phdr");
254                                 error = EFTYPE;
255                                 goto out;
256                         }
257                         if (dynamic_idx != -1) {
258                                 warnx("multiple SHT_DYNAMIC sections");
259                                 error = EFTYPE;
260                                 goto out;
261                         }
262                         dynamic_idx = i;
263                 }
264         }
265
266         error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn);
267         if (error != 0)
268                 goto out;
269
270         hash_off = rel_off = rela_off = sym_off = str_off = 0;
271         rel_sz = rela_sz = 0;
272         for (i = 0; i < ndyn; i++) {
273                 dp = &dyn[i];
274                 if (dp->d_tag == DT_NULL)
275                         break;
276
277                 switch (dp->d_tag) {
278                 case DT_HASH:
279                         if (hash_off != 0)
280                                 warnx("second DT_HASH entry ignored");
281                         else
282                                 hash_off = ef_get_offset(ef, dp->d_un.d_ptr);
283                         break;
284                 case DT_STRTAB:
285                         if (str_off != 0)
286                                 warnx("second DT_STRTAB entry ignored");
287                         else
288                                 str_off = ef_get_offset(ef, dp->d_un.d_ptr);
289                         break;
290                 case DT_SYMTAB:
291                         if (sym_off != 0)
292                                 warnx("second DT_SYMTAB entry ignored");
293                         else
294                                 sym_off = ef_get_offset(ef, dp->d_un.d_ptr);
295                         break;
296                 case DT_SYMENT:
297                         if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
298                             ELF_T_SYM)) {
299                                 error = EFTYPE;
300                                 goto out;
301                         }
302                         break;
303                 case DT_REL:
304                         if (rel_off != 0)
305                                 warnx("second DT_REL entry ignored");
306                         else
307                                 rel_off = ef_get_offset(ef, dp->d_un.d_ptr);
308                         break;
309                 case DT_RELSZ:
310                         if (rel_sz != 0)
311                                 warnx("second DT_RELSZ entry ignored");
312                         else
313                                 rel_sz = dp->d_un.d_val;
314                         break;
315                 case DT_RELENT:
316                         if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
317                             ELF_T_REL)) {
318                                 error = EFTYPE;
319                                 goto out;
320                         }
321                         break;
322                 case DT_RELA:
323                         if (rela_off != 0)
324                                 warnx("second DT_RELA entry ignored");
325                         else
326                                 rela_off = ef_get_offset(ef, dp->d_un.d_ptr);
327                         break;
328                 case DT_RELASZ:
329                         if (rela_sz != 0)
330                                 warnx("second DT_RELSZ entry ignored");
331                         else
332                                 rela_sz = dp->d_un.d_val;
333                         break;
334                 case DT_RELAENT:
335                         if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
336                             ELF_T_RELA)) {
337                                 error = EFTYPE;
338                                 goto out;
339                         }
340                         break;
341                 }
342         }
343         if (hash_off == 0) {
344                 warnx("%s: no .hash section found\n", ef->ef_name);
345                 error = EFTYPE;
346                 goto out;
347         }
348         if (sym_off == 0) {
349                 warnx("%s: no .dynsym section found\n", ef->ef_name);
350                 error = EFTYPE;
351                 goto out;
352         }
353         if (str_off == 0) {
354                 warnx("%s: no .dynstr section found\n", ef->ef_name);
355                 error = EFTYPE;
356                 goto out;
357         }
358         if (rel_off == 0 && rela_off == 0) {
359                 warnx("%s: no ELF relocation table found\n", ef->ef_name);
360                 error = EFTYPE;
361                 goto out;
362         }
363
364         nsym = 0;
365         for (i = 0; i < nshdr; i++) {
366                 switch (shdr[i].sh_type) {
367                 case SHT_HASH:
368                         if (shdr[i].sh_offset != hash_off) {
369                                 warnx("%s: ignoring SHT_HASH at different offset from DT_HASH",
370                                     ef->ef_name);
371                                 break;
372                         }
373
374                         /*
375                          * libelf(3) mentions ELF_T_HASH, but it is
376                          * not defined.
377                          */
378                         if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) {
379                                 warnx("hash section too small");
380                                 error = EFTYPE;
381                                 goto out;
382                         }
383                         error = elf_read_data(ef->ef_efile, ELF_T_WORD,
384                             shdr[i].sh_offset, shdr[i].sh_size,
385                             (void **)&ef->ef_hashtab);
386                         if (error != 0) {
387                                 warnc(error, "can't read hash table");
388                                 goto out;
389                         }
390                         ef->ef_nbuckets = ef->ef_hashtab[0];
391                         ef->ef_nchains = ef->ef_hashtab[1];
392                         if ((2 + ef->ef_nbuckets + ef->ef_nchains) *
393                             sizeof(*ef->ef_hashtab) != shdr[i].sh_size) {
394                                 warnx("inconsistent hash section size");
395                                 error = EFTYPE;
396                                 goto out;
397                         }
398
399                         ef->ef_buckets = ef->ef_hashtab + 2;
400                         ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
401                         break;
402                 case SHT_DYNSYM:
403                         if (shdr[i].sh_offset != sym_off) {
404                                 warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB",
405                                     ef->ef_name);
406                                 break;
407                         }
408                         error = elf_read_symbols(ef->ef_efile, i, &nsym,
409                             &ef->ef_symtab);
410                         if (error != 0) {
411                                 if (ef->ef_verbose)
412                                         warnx("%s: can't load .dynsym section (0x%jx)",
413                                             ef->ef_name, (uintmax_t)sym_off);
414                                 goto out;
415                         }
416                         break;
417                 case SHT_STRTAB:
418                         if (shdr[i].sh_offset != str_off)
419                                 break;
420                         error = elf_read_string_table(ef->ef_efile,
421                             &shdr[i], &ef->ef_strsz, &ef->ef_strtab);
422                         if (error != 0) {
423                                 warnx("can't load .dynstr section");
424                                 error = EIO;
425                                 goto out;
426                         }
427                         break;
428                 case SHT_REL:
429                         if (shdr[i].sh_offset != rel_off)
430                                 break;
431                         if (shdr[i].sh_size != rel_sz) {
432                                 warnx("%s: size mismatch for DT_REL section",
433                                     ef->ef_name);
434                                 error = EFTYPE;
435                                 goto out;
436                         }
437                         error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz,
438                             &ef->ef_rel);
439                         if (error != 0) {
440                                 warnx("%s: cannot load DT_REL section",
441                                     ef->ef_name);
442                                 goto out;
443                         }
444                         break;
445                 case SHT_RELA:
446                         if (shdr[i].sh_offset != rela_off)
447                                 break;
448                         if (shdr[i].sh_size != rela_sz) {
449                                 warnx("%s: size mismatch for DT_RELA section",
450                                     ef->ef_name);
451                                 error = EFTYPE;
452                                 goto out;
453                         }
454                         error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz,
455                             &ef->ef_rela);
456                         if (error != 0) {
457                                 warnx("%s: cannot load DT_RELA section",
458                                     ef->ef_name);
459                                 goto out;
460                         }
461                         break;
462                 }
463         }
464
465         if (ef->ef_hashtab == NULL) {
466                 warnx("%s: did not find a symbol hash table", ef->ef_name);
467                 error = EFTYPE;
468                 goto out;
469         }
470         if (ef->ef_symtab == NULL) {
471                 warnx("%s: did not find a dynamic symbol table", ef->ef_name);
472                 error = EFTYPE;
473                 goto out;
474         }
475         if (nsym != ef->ef_nchains) {
476                 warnx("%s: symbol count mismatch", ef->ef_name);
477                 error = EFTYPE;
478                 goto out;
479         }
480         if (ef->ef_strtab == NULL) {
481                 warnx("%s: did not find a dynamic string table", ef->ef_name);
482                 error = EFTYPE;
483                 goto out;
484         }
485         if (rel_off != 0 && ef->ef_rel == NULL) {
486                 warnx("%s: did not find a DT_REL relocation table",
487                     ef->ef_name);
488                 error = EFTYPE;
489                 goto out;
490         }
491         if (rela_off != 0 && ef->ef_rela == NULL) {
492                 warnx("%s: did not find a DT_RELA relocation table",
493                     ef->ef_name);
494                 error = EFTYPE;
495                 goto out;
496         }
497
498         error = 0;
499 out:
500         free(dyn);
501         free(shdr);
502         return (error);
503 }
504
505 static int
506 ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
507 {
508         GElf_Off ofs;
509         const GElf_Rela *a;
510         const GElf_Rel *r;
511         int error;
512
513         ofs = ef_get_offset(ef, address);
514         if (ofs == 0) {
515                 if (ef->ef_verbose)
516                         warnx("ef_seg_read_rel(%s): bad address (%jx)",
517                             ef->ef_name, (uintmax_t)address);
518                 return (EFAULT);
519         }
520         error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
521         if (error != 0)
522                 return (error);
523
524         for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
525                 error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address,
526                     len, dest);
527                 if (error != 0)
528                         return (error);
529         }
530         for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
531                 error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address,
532                     len, dest);
533                 if (error != 0)
534                         return (error);
535         }
536         return (0);
537 }
538
539 static int
540 ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
541 {
542         GElf_Off ofs;
543         int error;
544
545         ofs = ef_get_offset(ef, address);
546         if (ofs == 0) {
547                 if (ef->ef_verbose)
548                         warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)",
549                             ef->ef_name, (uintmax_t)address, (uintmax_t)ofs);
550                 return (EFAULT);
551         }
552
553         error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
554         if (error != 0)
555                 return (error);
556         if (strnlen(dest, len) == len)
557                 return (EFAULT);
558
559         return (0);
560 }
561
562 int
563 ef_open(struct elf_file *efile, int verbose)
564 {
565         elf_file_t ef;
566         GElf_Ehdr *hdr;
567         size_t i, nphdr, nsegs;
568         int error;
569         GElf_Phdr *phdr, *phdyn;
570
571         hdr = &efile->ef_hdr;
572         if (hdr->e_phnum == 0 ||
573             hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) ||
574             hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
575             hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
576                 return (EFTYPE);
577
578         ef = malloc(sizeof(*ef));
579         if (ef == NULL)
580                 return (errno);
581
582         efile->ef_ef = ef;
583         efile->ef_ops = &ef_file_ops;
584
585         bzero(ef, sizeof(*ef));
586         ef->ef_verbose = verbose;
587         ef->ef_name = strdup(efile->ef_filename);
588         ef->ef_efile = efile;
589
590         error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph);
591         if (error != 0) {
592                 phdr = NULL;
593                 goto out;
594         }
595
596         error = EFTYPE;
597         nsegs = 0;
598         phdyn = NULL;
599         phdr = ef->ef_ph;
600         for (i = 0; i < nphdr; i++, phdr++) {
601                 if (verbose > 1)
602                         ef_print_phdr(phdr);
603                 switch (phdr->p_type) {
604                 case PT_LOAD:
605                         if (nsegs < MAXSEGS)
606                                 ef->ef_segs[nsegs] = phdr;
607                         nsegs++;
608                         break;
609                 case PT_PHDR:
610                         break;
611                 case PT_DYNAMIC:
612                         phdyn = phdr;
613                         break;
614                 }
615         }
616         if (verbose > 1)
617                 printf("\n");
618         if (phdyn == NULL) {
619                 warnx("Skipping %s: not dynamically-linked",
620                     ef->ef_name);
621                 goto out;
622         }
623
624         if (nsegs > MAXSEGS) {
625                 warnx("%s: too many segments", ef->ef_name);
626                 goto out;
627         }
628         ef->ef_nsegs = nsegs;
629
630         error = ef_parse_dynamic(ef, phdyn);
631 out:
632         if (error != 0)
633                 ef_close(ef);
634         return (error);
635 }
636
637 static void
638 ef_close(elf_file_t ef)
639 {
640         free(ef->ef_rela);
641         free(ef->ef_rel);
642         free(ef->ef_strtab);
643         free(ef->ef_symtab);
644         free(ef->ef_hashtab);
645         free(ef->ef_ph);
646         if (ef->ef_name)
647                 free(ef->ef_name);
648         ef->ef_efile->ef_ops = NULL;
649         ef->ef_efile->ef_ef = NULL;
650         free(ef);
651 }