1df57947fSPedro F. Giffuni /*-
2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni *
49c6f9240SPeter Wemm * Copyright (c) 2000, Boris Popov
59c6f9240SPeter Wemm * All rights reserved.
69c6f9240SPeter Wemm *
79c6f9240SPeter Wemm * Redistribution and use in source and binary forms, with or without
89c6f9240SPeter Wemm * modification, are permitted provided that the following conditions
99c6f9240SPeter Wemm * are met:
109c6f9240SPeter Wemm * 1. Redistributions of source code must retain the above copyright
119c6f9240SPeter Wemm * notice, this list of conditions and the following disclaimer.
129c6f9240SPeter Wemm * 2. Redistributions in binary form must reproduce the above copyright
139c6f9240SPeter Wemm * notice, this list of conditions and the following disclaimer in the
149c6f9240SPeter Wemm * documentation and/or other materials provided with the distribution.
159c6f9240SPeter Wemm * 3. All advertising materials mentioning features or use of this software
169c6f9240SPeter Wemm * must display the following acknowledgement:
179c6f9240SPeter Wemm * This product includes software developed by Boris Popov.
189c6f9240SPeter Wemm * 4. Neither the name of the author nor the names of any co-contributors
199c6f9240SPeter Wemm * may be used to endorse or promote products derived from this software
209c6f9240SPeter Wemm * without specific prior written permission.
219c6f9240SPeter Wemm *
229c6f9240SPeter Wemm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
239c6f9240SPeter Wemm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249c6f9240SPeter Wemm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259c6f9240SPeter Wemm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
269c6f9240SPeter Wemm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279c6f9240SPeter Wemm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289c6f9240SPeter Wemm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299c6f9240SPeter Wemm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309c6f9240SPeter Wemm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319c6f9240SPeter Wemm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329c6f9240SPeter Wemm * SUCH DAMAGE.
339c6f9240SPeter Wemm */
349c6f9240SPeter Wemm
359c6f9240SPeter Wemm #include <sys/param.h>
369c6f9240SPeter Wemm
379c6f9240SPeter Wemm #include <err.h>
38e2d0802cSEd Maste #include <errno.h>
390299afdfSJohn Baldwin #include <gelf.h>
40e2d0802cSEd Maste #include <stdio.h>
41e2d0802cSEd Maste #include <stdlib.h>
42e2d0802cSEd Maste #include <string.h>
439c6f9240SPeter Wemm
449c6f9240SPeter Wemm #include "ef.h"
459c6f9240SPeter Wemm
4605c312a3SDimitry Andric #define MAXSEGS 16
479772dc2aSIan Dowse struct ef_file {
489772dc2aSIan Dowse char *ef_name;
499772dc2aSIan Dowse struct elf_file *ef_efile;
500299afdfSJohn Baldwin GElf_Phdr *ef_ph;
519772dc2aSIan Dowse void *ef_fpage; /* First block of the file */
529772dc2aSIan Dowse int ef_fplen; /* length of first block */
530299afdfSJohn Baldwin GElf_Hashelt ef_nbuckets;
540299afdfSJohn Baldwin GElf_Hashelt ef_nchains;
550299afdfSJohn Baldwin GElf_Hashelt *ef_buckets;
560299afdfSJohn Baldwin GElf_Hashelt *ef_chains;
570299afdfSJohn Baldwin GElf_Hashelt *ef_hashtab;
589772dc2aSIan Dowse caddr_t ef_strtab;
590299afdfSJohn Baldwin long ef_strsz;
600299afdfSJohn Baldwin GElf_Sym *ef_symtab;
619772dc2aSIan Dowse int ef_nsegs;
620299afdfSJohn Baldwin GElf_Phdr *ef_segs[MAXSEGS];
639772dc2aSIan Dowse int ef_verbose;
640299afdfSJohn Baldwin GElf_Rel *ef_rel; /* relocation table */
650299afdfSJohn Baldwin long ef_relsz; /* number of entries */
660299afdfSJohn Baldwin GElf_Rela *ef_rela; /* relocation table */
670299afdfSJohn Baldwin long ef_relasz; /* number of entries */
689772dc2aSIan Dowse };
699772dc2aSIan Dowse
700299afdfSJohn Baldwin static void ef_print_phdr(GElf_Phdr *);
710299afdfSJohn Baldwin static GElf_Off ef_get_offset(elf_file_t, GElf_Addr);
7287e5cd7cSMike Heffner
730299afdfSJohn Baldwin static void ef_close(elf_file_t ef);
74e2d0802cSEd Maste
750299afdfSJohn Baldwin static int ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len,
76e2d0802cSEd Maste void *dest);
770299afdfSJohn Baldwin static int ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len,
78da67e6e6SEd Maste char *dest);
79e2d0802cSEd Maste
800299afdfSJohn Baldwin static GElf_Addr ef_symaddr(elf_file_t ef, GElf_Size symidx);
810299afdfSJohn Baldwin static int ef_lookup_set(elf_file_t ef, const char *name,
820299afdfSJohn Baldwin GElf_Addr *startp, GElf_Addr *stopp, long *countp);
83e2d0802cSEd Maste static int ef_lookup_symbol(elf_file_t ef, const char *name,
840299afdfSJohn Baldwin GElf_Sym **sym);
859772dc2aSIan Dowse
869772dc2aSIan Dowse static struct elf_file_ops ef_file_ops = {
87e2d0802cSEd Maste .close = ef_close,
88e2d0802cSEd Maste .seg_read_rel = ef_seg_read_rel,
89e2d0802cSEd Maste .seg_read_string = ef_seg_read_string,
90e2d0802cSEd Maste .symaddr = ef_symaddr,
91e2d0802cSEd Maste .lookup_set = ef_lookup_set,
929772dc2aSIan Dowse };
939772dc2aSIan Dowse
949772dc2aSIan Dowse static void
ef_print_phdr(GElf_Phdr * phdr)950299afdfSJohn Baldwin ef_print_phdr(GElf_Phdr *phdr)
969c6f9240SPeter Wemm {
979c6f9240SPeter Wemm
989c6f9240SPeter Wemm if ((phdr->p_flags & PF_W) == 0) {
99b6274d17SEd Maste printf("text=0x%jx ", (uintmax_t)phdr->p_filesz);
1009c6f9240SPeter Wemm } else {
101b6274d17SEd Maste printf("data=0x%jx", (uintmax_t)phdr->p_filesz);
1029c6f9240SPeter Wemm if (phdr->p_filesz < phdr->p_memsz)
103b6274d17SEd Maste printf("+0x%jx",
104b6274d17SEd Maste (uintmax_t)(phdr->p_memsz - phdr->p_filesz));
1059c6f9240SPeter Wemm printf(" ");
1069c6f9240SPeter Wemm }
1079c6f9240SPeter Wemm }
1089c6f9240SPeter Wemm
1090299afdfSJohn Baldwin static GElf_Off
ef_get_offset(elf_file_t ef,GElf_Addr addr)1100299afdfSJohn Baldwin ef_get_offset(elf_file_t ef, GElf_Addr addr)
1119c6f9240SPeter Wemm {
1120299afdfSJohn Baldwin GElf_Phdr *ph;
1139c6f9240SPeter Wemm int i;
1149c6f9240SPeter Wemm
1159c6f9240SPeter Wemm for (i = 0; i < ef->ef_nsegs; i++) {
1169c6f9240SPeter Wemm ph = ef->ef_segs[i];
1170299afdfSJohn Baldwin if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) {
1180299afdfSJohn Baldwin return (ph->p_offset + (addr - ph->p_vaddr));
1199c6f9240SPeter Wemm }
1209c6f9240SPeter Wemm }
121e2d0802cSEd Maste return (0);
1229c6f9240SPeter Wemm }
1239c6f9240SPeter Wemm
1249c6f9240SPeter Wemm /*
1250299afdfSJohn Baldwin * next two functions copied from link_elf.c
1269c6f9240SPeter Wemm */
1279772dc2aSIan Dowse static int
ef_lookup_symbol(elf_file_t ef,const char * name,GElf_Sym ** sym)1280299afdfSJohn Baldwin ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym)
1299c6f9240SPeter Wemm {
130e2d0802cSEd Maste unsigned long hash, symnum;
1310299afdfSJohn Baldwin GElf_Sym *symp;
1329c6f9240SPeter Wemm char *strp;
1339c6f9240SPeter Wemm
1349c6f9240SPeter Wemm /* First, search hashed global symbols */
1359c6f9240SPeter Wemm hash = elf_hash(name);
1369c6f9240SPeter Wemm symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
1379c6f9240SPeter Wemm
1389c6f9240SPeter Wemm while (symnum != STN_UNDEF) {
1399c6f9240SPeter Wemm if (symnum >= ef->ef_nchains) {
1409c6f9240SPeter Wemm warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
1419c6f9240SPeter Wemm ef->ef_name);
142e2d0802cSEd Maste return (ENOENT);
1439c6f9240SPeter Wemm }
1449c6f9240SPeter Wemm
1459c6f9240SPeter Wemm symp = ef->ef_symtab + symnum;
1469c6f9240SPeter Wemm if (symp->st_name == 0) {
1479c6f9240SPeter Wemm warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
1489c6f9240SPeter Wemm ef->ef_name);
149e2d0802cSEd Maste return (ENOENT);
1509c6f9240SPeter Wemm }
1519c6f9240SPeter Wemm
1529c6f9240SPeter Wemm strp = ef->ef_strtab + symp->st_name;
1539c6f9240SPeter Wemm
1549c6f9240SPeter Wemm if (strcmp(name, strp) == 0) {
1559c6f9240SPeter Wemm if (symp->st_shndx != SHN_UNDEF ||
1569c6f9240SPeter Wemm (symp->st_value != 0 &&
1570299afdfSJohn Baldwin GELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
1589c6f9240SPeter Wemm *sym = symp;
159e2d0802cSEd Maste return (0);
1609c6f9240SPeter Wemm } else
161e2d0802cSEd Maste return (ENOENT);
1629c6f9240SPeter Wemm }
1639c6f9240SPeter Wemm
1649c6f9240SPeter Wemm symnum = ef->ef_chains[symnum];
1659c6f9240SPeter Wemm }
1669c6f9240SPeter Wemm
167e2d0802cSEd Maste return (ENOENT);
1689c6f9240SPeter Wemm }
1699c6f9240SPeter Wemm
1709772dc2aSIan Dowse static int
ef_lookup_set(elf_file_t ef,const char * name,GElf_Addr * startp,GElf_Addr * stopp,long * countp)1710299afdfSJohn Baldwin ef_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp,
1720299afdfSJohn Baldwin GElf_Addr *stopp, long *countp)
1739772dc2aSIan Dowse {
1740299afdfSJohn Baldwin GElf_Sym *sym;
1759772dc2aSIan Dowse char *setsym;
1769772dc2aSIan Dowse int error, len;
1779772dc2aSIan Dowse
1789772dc2aSIan Dowse len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
1799772dc2aSIan Dowse setsym = malloc(len);
1809772dc2aSIan Dowse if (setsym == NULL)
181e2d0802cSEd Maste return (errno);
1829772dc2aSIan Dowse
1839772dc2aSIan Dowse /* get address of first entry */
1849772dc2aSIan Dowse snprintf(setsym, len, "%s%s", "__start_set_", name);
1859772dc2aSIan Dowse error = ef_lookup_symbol(ef, setsym, &sym);
186e2d0802cSEd Maste if (error != 0)
1879772dc2aSIan Dowse goto out;
1889772dc2aSIan Dowse *startp = sym->st_value;
1899772dc2aSIan Dowse
1909772dc2aSIan Dowse /* get address of last entry */
1919772dc2aSIan Dowse snprintf(setsym, len, "%s%s", "__stop_set_", name);
1929772dc2aSIan Dowse error = ef_lookup_symbol(ef, setsym, &sym);
193e2d0802cSEd Maste if (error != 0)
1949772dc2aSIan Dowse goto out;
1959772dc2aSIan Dowse *stopp = sym->st_value;
1969772dc2aSIan Dowse
1979772dc2aSIan Dowse /* and the number of entries */
1980299afdfSJohn Baldwin *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile);
1999772dc2aSIan Dowse
2009772dc2aSIan Dowse out:
2019772dc2aSIan Dowse free(setsym);
2029772dc2aSIan Dowse return (error);
2039772dc2aSIan Dowse }
2049772dc2aSIan Dowse
2050299afdfSJohn Baldwin static GElf_Addr
ef_symaddr(elf_file_t ef,GElf_Size symidx)2060299afdfSJohn Baldwin ef_symaddr(elf_file_t ef, GElf_Size symidx)
2079772dc2aSIan Dowse {
2080299afdfSJohn Baldwin const GElf_Sym *sym;
2099772dc2aSIan Dowse
2109772dc2aSIan Dowse if (symidx >= ef->ef_nchains)
2119772dc2aSIan Dowse return (0);
2129772dc2aSIan Dowse sym = ef->ef_symtab + symidx;
2139772dc2aSIan Dowse
2140299afdfSJohn Baldwin if (GELF_ST_BIND(sym->st_info) == STB_LOCAL &&
2159772dc2aSIan Dowse sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
2169772dc2aSIan Dowse return (sym->st_value);
2179772dc2aSIan Dowse return (0);
2189772dc2aSIan Dowse }
2199772dc2aSIan Dowse
2209772dc2aSIan Dowse static int
ef_parse_dynamic(elf_file_t ef,const GElf_Phdr * phdyn)2210299afdfSJohn Baldwin ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn)
2229c6f9240SPeter Wemm {
2230299afdfSJohn Baldwin GElf_Shdr *shdr;
2240299afdfSJohn Baldwin GElf_Dyn *dyn, *dp;
2250299afdfSJohn Baldwin size_t i, ndyn, nshdr, nsym;
2269c6f9240SPeter Wemm int error;
2270299afdfSJohn Baldwin GElf_Off hash_off, sym_off, str_off;
2280299afdfSJohn Baldwin GElf_Off rel_off;
2290299afdfSJohn Baldwin GElf_Off rela_off;
2301eed250aSJake Burkholder int rel_sz;
2311eed250aSJake Burkholder int rela_sz;
2320299afdfSJohn Baldwin int dynamic_idx;
2339c6f9240SPeter Wemm
2340299afdfSJohn Baldwin /*
2350299afdfSJohn Baldwin * The kernel linker parses the PT_DYNAMIC segment to find
2360299afdfSJohn Baldwin * various important tables. The gelf API of libelf is
2370299afdfSJohn Baldwin * section-oriented and requires extracting data from sections
2380299afdfSJohn Baldwin * instead of segments (program headers). As a result,
2390299afdfSJohn Baldwin * iterate over section headers to read various tables after
2400299afdfSJohn Baldwin * parsing values from PT_DYNAMIC.
2410299afdfSJohn Baldwin */
2420299afdfSJohn Baldwin error = elf_read_shdrs(ef->ef_efile, &nshdr, &shdr);
2430299afdfSJohn Baldwin if (error != 0)
2440299afdfSJohn Baldwin return (EFTYPE);
2450299afdfSJohn Baldwin dyn = NULL;
2460299afdfSJohn Baldwin
2470299afdfSJohn Baldwin /* Find section for .dynamic. */
2480299afdfSJohn Baldwin dynamic_idx = -1;
2490299afdfSJohn Baldwin for (i = 0; i < nshdr; i++) {
2500299afdfSJohn Baldwin if (shdr[i].sh_type == SHT_DYNAMIC) {
2516631e2f9SJohn Baldwin /*
2526631e2f9SJohn Baldwin * PowerPC kernels contain additional sections
2536631e2f9SJohn Baldwin * beyond .dynamic in PT_DYNAMIC due to a linker
2546631e2f9SJohn Baldwin * script bug. Permit a section with a smaller
2556631e2f9SJohn Baldwin * size as a workaround.
2566631e2f9SJohn Baldwin */
2570299afdfSJohn Baldwin if (shdr[i].sh_offset != phdyn->p_offset ||
2586631e2f9SJohn Baldwin ((elf_machine(ef->ef_efile) == EM_PPC ||
2596631e2f9SJohn Baldwin elf_machine(ef->ef_efile) == EM_PPC64) ?
2606631e2f9SJohn Baldwin shdr[i].sh_size > phdyn->p_filesz :
2616631e2f9SJohn Baldwin shdr[i].sh_size != phdyn->p_filesz)) {
2620299afdfSJohn Baldwin warnx(".dynamic section doesn't match phdr");
2630299afdfSJohn Baldwin error = EFTYPE;
2640299afdfSJohn Baldwin goto out;
2650299afdfSJohn Baldwin }
2660299afdfSJohn Baldwin if (dynamic_idx != -1) {
2670299afdfSJohn Baldwin warnx("multiple SHT_DYNAMIC sections");
2680299afdfSJohn Baldwin error = EFTYPE;
2690299afdfSJohn Baldwin goto out;
2700299afdfSJohn Baldwin }
2710299afdfSJohn Baldwin dynamic_idx = i;
2720299afdfSJohn Baldwin }
2730299afdfSJohn Baldwin }
2740299afdfSJohn Baldwin
2750299afdfSJohn Baldwin error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn);
2760299afdfSJohn Baldwin if (error != 0)
2770299afdfSJohn Baldwin goto out;
2780299afdfSJohn Baldwin
2790299afdfSJohn Baldwin hash_off = rel_off = rela_off = sym_off = str_off = 0;
2801eed250aSJake Burkholder rel_sz = rela_sz = 0;
2810299afdfSJohn Baldwin for (i = 0; i < ndyn; i++) {
2820299afdfSJohn Baldwin dp = &dyn[i];
2830299afdfSJohn Baldwin if (dp->d_tag == DT_NULL)
2840299afdfSJohn Baldwin break;
2850299afdfSJohn Baldwin
2869c6f9240SPeter Wemm switch (dp->d_tag) {
2879c6f9240SPeter Wemm case DT_HASH:
2880299afdfSJohn Baldwin if (hash_off != 0)
2890299afdfSJohn Baldwin warnx("second DT_HASH entry ignored");
2900299afdfSJohn Baldwin else
2910299afdfSJohn Baldwin hash_off = ef_get_offset(ef, dp->d_un.d_ptr);
2929c6f9240SPeter Wemm break;
2939c6f9240SPeter Wemm case DT_STRTAB:
2940299afdfSJohn Baldwin if (str_off != 0)
2950299afdfSJohn Baldwin warnx("second DT_STRTAB entry ignored");
2960299afdfSJohn Baldwin else
2970299afdfSJohn Baldwin str_off = ef_get_offset(ef, dp->d_un.d_ptr);
2989c6f9240SPeter Wemm break;
2999c6f9240SPeter Wemm case DT_SYMTAB:
3000299afdfSJohn Baldwin if (sym_off != 0)
3010299afdfSJohn Baldwin warnx("second DT_SYMTAB entry ignored");
3020299afdfSJohn Baldwin else
3030299afdfSJohn Baldwin sym_off = ef_get_offset(ef, dp->d_un.d_ptr);
3049c6f9240SPeter Wemm break;
3059c6f9240SPeter Wemm case DT_SYMENT:
3060299afdfSJohn Baldwin if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
3070299afdfSJohn Baldwin ELF_T_SYM)) {
3080299afdfSJohn Baldwin error = EFTYPE;
3090299afdfSJohn Baldwin goto out;
3100299afdfSJohn Baldwin }
3119c6f9240SPeter Wemm break;
3121eed250aSJake Burkholder case DT_REL:
3131eed250aSJake Burkholder if (rel_off != 0)
3141eed250aSJake Burkholder warnx("second DT_REL entry ignored");
3150299afdfSJohn Baldwin else
3160299afdfSJohn Baldwin rel_off = ef_get_offset(ef, dp->d_un.d_ptr);
3171eed250aSJake Burkholder break;
3181eed250aSJake Burkholder case DT_RELSZ:
3191eed250aSJake Burkholder if (rel_sz != 0)
3201eed250aSJake Burkholder warnx("second DT_RELSZ entry ignored");
3210299afdfSJohn Baldwin else
3221eed250aSJake Burkholder rel_sz = dp->d_un.d_val;
3231eed250aSJake Burkholder break;
3241eed250aSJake Burkholder case DT_RELENT:
3250299afdfSJohn Baldwin if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
3260299afdfSJohn Baldwin ELF_T_REL)) {
3270299afdfSJohn Baldwin error = EFTYPE;
3280299afdfSJohn Baldwin goto out;
3290299afdfSJohn Baldwin }
3301eed250aSJake Burkholder break;
3311eed250aSJake Burkholder case DT_RELA:
3321eed250aSJake Burkholder if (rela_off != 0)
3331eed250aSJake Burkholder warnx("second DT_RELA entry ignored");
3340299afdfSJohn Baldwin else
3350299afdfSJohn Baldwin rela_off = ef_get_offset(ef, dp->d_un.d_ptr);
3361eed250aSJake Burkholder break;
3371eed250aSJake Burkholder case DT_RELASZ:
3381eed250aSJake Burkholder if (rela_sz != 0)
3390299afdfSJohn Baldwin warnx("second DT_RELSZ entry ignored");
3400299afdfSJohn Baldwin else
3411eed250aSJake Burkholder rela_sz = dp->d_un.d_val;
3421eed250aSJake Burkholder break;
3431eed250aSJake Burkholder case DT_RELAENT:
3440299afdfSJohn Baldwin if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
3450299afdfSJohn Baldwin ELF_T_RELA)) {
3460299afdfSJohn Baldwin error = EFTYPE;
3470299afdfSJohn Baldwin goto out;
3480299afdfSJohn Baldwin }
3491eed250aSJake Burkholder break;
3509c6f9240SPeter Wemm }
3519c6f9240SPeter Wemm }
3520299afdfSJohn Baldwin if (hash_off == 0) {
3530299afdfSJohn Baldwin warnx("%s: no .hash section found\n", ef->ef_name);
3540299afdfSJohn Baldwin error = EFTYPE;
3550299afdfSJohn Baldwin goto out;
3560299afdfSJohn Baldwin }
3570299afdfSJohn Baldwin if (sym_off == 0) {
3589c6f9240SPeter Wemm warnx("%s: no .dynsym section found\n", ef->ef_name);
3590299afdfSJohn Baldwin error = EFTYPE;
3600299afdfSJohn Baldwin goto out;
3619c6f9240SPeter Wemm }
3620299afdfSJohn Baldwin if (str_off == 0) {
3639c6f9240SPeter Wemm warnx("%s: no .dynstr section found\n", ef->ef_name);
3640299afdfSJohn Baldwin error = EFTYPE;
3650299afdfSJohn Baldwin goto out;
3669c6f9240SPeter Wemm }
3670299afdfSJohn Baldwin if (rel_off == 0 && rela_off == 0) {
3680299afdfSJohn Baldwin warnx("%s: no ELF relocation table found\n", ef->ef_name);
3690299afdfSJohn Baldwin error = EFTYPE;
3700299afdfSJohn Baldwin goto out;
3710299afdfSJohn Baldwin }
3720299afdfSJohn Baldwin
373d281feceSJohn Baldwin nsym = 0;
3740299afdfSJohn Baldwin for (i = 0; i < nshdr; i++) {
3750299afdfSJohn Baldwin switch (shdr[i].sh_type) {
3760299afdfSJohn Baldwin case SHT_HASH:
3770299afdfSJohn Baldwin if (shdr[i].sh_offset != hash_off) {
3780299afdfSJohn Baldwin warnx("%s: ignoring SHT_HASH at different offset from DT_HASH",
3790299afdfSJohn Baldwin ef->ef_name);
3800299afdfSJohn Baldwin break;
3810299afdfSJohn Baldwin }
3820299afdfSJohn Baldwin
3830299afdfSJohn Baldwin /*
3840299afdfSJohn Baldwin * libelf(3) mentions ELF_T_HASH, but it is
3850299afdfSJohn Baldwin * not defined.
3860299afdfSJohn Baldwin */
3870299afdfSJohn Baldwin if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) {
3880299afdfSJohn Baldwin warnx("hash section too small");
3890299afdfSJohn Baldwin error = EFTYPE;
3900299afdfSJohn Baldwin goto out;
3910299afdfSJohn Baldwin }
3920299afdfSJohn Baldwin error = elf_read_data(ef->ef_efile, ELF_T_WORD,
3930299afdfSJohn Baldwin shdr[i].sh_offset, shdr[i].sh_size,
3940299afdfSJohn Baldwin (void **)&ef->ef_hashtab);
3950299afdfSJohn Baldwin if (error != 0) {
3960299afdfSJohn Baldwin warnc(error, "can't read hash table");
3970299afdfSJohn Baldwin goto out;
3980299afdfSJohn Baldwin }
3990299afdfSJohn Baldwin ef->ef_nbuckets = ef->ef_hashtab[0];
4000299afdfSJohn Baldwin ef->ef_nchains = ef->ef_hashtab[1];
4010299afdfSJohn Baldwin if ((2 + ef->ef_nbuckets + ef->ef_nchains) *
4020299afdfSJohn Baldwin sizeof(*ef->ef_hashtab) != shdr[i].sh_size) {
4030299afdfSJohn Baldwin warnx("inconsistent hash section size");
4040299afdfSJohn Baldwin error = EFTYPE;
4050299afdfSJohn Baldwin goto out;
4060299afdfSJohn Baldwin }
4070299afdfSJohn Baldwin
4080299afdfSJohn Baldwin ef->ef_buckets = ef->ef_hashtab + 2;
4090299afdfSJohn Baldwin ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
4100299afdfSJohn Baldwin break;
4110299afdfSJohn Baldwin case SHT_DYNSYM:
4120299afdfSJohn Baldwin if (shdr[i].sh_offset != sym_off) {
4130299afdfSJohn Baldwin warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB",
4140299afdfSJohn Baldwin ef->ef_name);
4150299afdfSJohn Baldwin break;
4160299afdfSJohn Baldwin }
4170299afdfSJohn Baldwin error = elf_read_symbols(ef->ef_efile, i, &nsym,
4180299afdfSJohn Baldwin &ef->ef_symtab);
4190299afdfSJohn Baldwin if (error != 0) {
4209c6f9240SPeter Wemm if (ef->ef_verbose)
421b6274d17SEd Maste warnx("%s: can't load .dynsym section (0x%jx)",
4220299afdfSJohn Baldwin ef->ef_name, (uintmax_t)sym_off);
4230299afdfSJohn Baldwin goto out;
4249c6f9240SPeter Wemm }
4250299afdfSJohn Baldwin break;
4260299afdfSJohn Baldwin case SHT_STRTAB:
4270299afdfSJohn Baldwin if (shdr[i].sh_offset != str_off)
4280299afdfSJohn Baldwin break;
4290299afdfSJohn Baldwin error = elf_read_string_table(ef->ef_efile,
4300299afdfSJohn Baldwin &shdr[i], &ef->ef_strsz, &ef->ef_strtab);
4310299afdfSJohn Baldwin if (error != 0) {
4329c6f9240SPeter Wemm warnx("can't load .dynstr section");
4330299afdfSJohn Baldwin error = EIO;
4340299afdfSJohn Baldwin goto out;
4359c6f9240SPeter Wemm }
4360299afdfSJohn Baldwin break;
4370299afdfSJohn Baldwin case SHT_REL:
4380299afdfSJohn Baldwin if (shdr[i].sh_offset != rel_off)
4390299afdfSJohn Baldwin break;
4400299afdfSJohn Baldwin if (shdr[i].sh_size != rel_sz) {
4410299afdfSJohn Baldwin warnx("%s: size mismatch for DT_REL section",
4421eed250aSJake Burkholder ef->ef_name);
4430299afdfSJohn Baldwin error = EFTYPE;
4440299afdfSJohn Baldwin goto out;
4451eed250aSJake Burkholder }
4460299afdfSJohn Baldwin error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz,
4470299afdfSJohn Baldwin &ef->ef_rel);
4480299afdfSJohn Baldwin if (error != 0) {
4490299afdfSJohn Baldwin warnx("%s: cannot load DT_REL section",
4501eed250aSJake Burkholder ef->ef_name);
4510299afdfSJohn Baldwin goto out;
4521eed250aSJake Burkholder }
4530299afdfSJohn Baldwin break;
4540299afdfSJohn Baldwin case SHT_RELA:
4550299afdfSJohn Baldwin if (shdr[i].sh_offset != rela_off)
4560299afdfSJohn Baldwin break;
4570299afdfSJohn Baldwin if (shdr[i].sh_size != rela_sz) {
4580299afdfSJohn Baldwin warnx("%s: size mismatch for DT_RELA section",
4590299afdfSJohn Baldwin ef->ef_name);
4600299afdfSJohn Baldwin error = EFTYPE;
4610299afdfSJohn Baldwin goto out;
4621eed250aSJake Burkholder }
4630299afdfSJohn Baldwin error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz,
4640299afdfSJohn Baldwin &ef->ef_rela);
4650299afdfSJohn Baldwin if (error != 0) {
4660299afdfSJohn Baldwin warnx("%s: cannot load DT_RELA section",
4670299afdfSJohn Baldwin ef->ef_name);
4680299afdfSJohn Baldwin goto out;
4691eed250aSJake Burkholder }
4700299afdfSJohn Baldwin break;
4711eed250aSJake Burkholder }
4729c6f9240SPeter Wemm }
4739c6f9240SPeter Wemm
4740299afdfSJohn Baldwin if (ef->ef_hashtab == NULL) {
4750299afdfSJohn Baldwin warnx("%s: did not find a symbol hash table", ef->ef_name);
4760299afdfSJohn Baldwin error = EFTYPE;
4770299afdfSJohn Baldwin goto out;
4780299afdfSJohn Baldwin }
4790299afdfSJohn Baldwin if (ef->ef_symtab == NULL) {
4800299afdfSJohn Baldwin warnx("%s: did not find a dynamic symbol table", ef->ef_name);
4810299afdfSJohn Baldwin error = EFTYPE;
4820299afdfSJohn Baldwin goto out;
4830299afdfSJohn Baldwin }
4840299afdfSJohn Baldwin if (nsym != ef->ef_nchains) {
4850299afdfSJohn Baldwin warnx("%s: symbol count mismatch", ef->ef_name);
4860299afdfSJohn Baldwin error = EFTYPE;
4870299afdfSJohn Baldwin goto out;
4880299afdfSJohn Baldwin }
4890299afdfSJohn Baldwin if (ef->ef_strtab == NULL) {
4900299afdfSJohn Baldwin warnx("%s: did not find a dynamic string table", ef->ef_name);
4910299afdfSJohn Baldwin error = EFTYPE;
4920299afdfSJohn Baldwin goto out;
4930299afdfSJohn Baldwin }
4940299afdfSJohn Baldwin if (rel_off != 0 && ef->ef_rel == NULL) {
4950299afdfSJohn Baldwin warnx("%s: did not find a DT_REL relocation table",
4960299afdfSJohn Baldwin ef->ef_name);
4970299afdfSJohn Baldwin error = EFTYPE;
4980299afdfSJohn Baldwin goto out;
4990299afdfSJohn Baldwin }
5000299afdfSJohn Baldwin if (rela_off != 0 && ef->ef_rela == NULL) {
5010299afdfSJohn Baldwin warnx("%s: did not find a DT_RELA relocation table",
5020299afdfSJohn Baldwin ef->ef_name);
5030299afdfSJohn Baldwin error = EFTYPE;
5040299afdfSJohn Baldwin goto out;
5059c6f9240SPeter Wemm }
50687e5cd7cSMike Heffner
5070299afdfSJohn Baldwin error = 0;
5080299afdfSJohn Baldwin out:
5090299afdfSJohn Baldwin free(dyn);
5100299afdfSJohn Baldwin free(shdr);
511e2d0802cSEd Maste return (error);
5129c6f9240SPeter Wemm }
5139c6f9240SPeter Wemm
5149772dc2aSIan Dowse static int
ef_seg_read_rel(elf_file_t ef,GElf_Addr address,size_t len,void * dest)5150299afdfSJohn Baldwin ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
5169c6f9240SPeter Wemm {
5170299afdfSJohn Baldwin GElf_Off ofs;
5180299afdfSJohn Baldwin const GElf_Rela *a;
5190299afdfSJohn Baldwin const GElf_Rel *r;
5201eed250aSJake Burkholder int error;
5211eed250aSJake Burkholder
5220299afdfSJohn Baldwin ofs = ef_get_offset(ef, address);
5231eed250aSJake Burkholder if (ofs == 0) {
5241eed250aSJake Burkholder if (ef->ef_verbose)
5256d46e2e0SJohn Baldwin warnx("ef_seg_read_rel(%s): bad address (%jx)",
5266d46e2e0SJohn Baldwin ef->ef_name, (uintmax_t)address);
527e2d0802cSEd Maste return (EFAULT);
5281eed250aSJake Burkholder }
5290299afdfSJohn Baldwin error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
5300299afdfSJohn Baldwin if (error != 0)
5311eed250aSJake Burkholder return (error);
5329772dc2aSIan Dowse
5339772dc2aSIan Dowse for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
5340299afdfSJohn Baldwin error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address,
5350299afdfSJohn Baldwin len, dest);
5369772dc2aSIan Dowse if (error != 0)
5379772dc2aSIan Dowse return (error);
5389772dc2aSIan Dowse }
5399772dc2aSIan Dowse for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
5400299afdfSJohn Baldwin error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address,
5410299afdfSJohn Baldwin len, dest);
5429772dc2aSIan Dowse if (error != 0)
5439772dc2aSIan Dowse return (error);
5449772dc2aSIan Dowse }
5459772dc2aSIan Dowse return (0);
5461eed250aSJake Burkholder }
5471eed250aSJake Burkholder
5489772dc2aSIan Dowse static int
ef_seg_read_string(elf_file_t ef,GElf_Addr address,size_t len,char * dest)5490299afdfSJohn Baldwin ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
550da67e6e6SEd Maste {
5510299afdfSJohn Baldwin GElf_Off ofs;
552da67e6e6SEd Maste
5530299afdfSJohn Baldwin ofs = ef_get_offset(ef, address);
5546d46e2e0SJohn Baldwin if (ofs == 0) {
555da67e6e6SEd Maste if (ef->ef_verbose)
556b6274d17SEd Maste warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)",
5570299afdfSJohn Baldwin ef->ef_name, (uintmax_t)address, (uintmax_t)ofs);
558da67e6e6SEd Maste return (EFAULT);
559da67e6e6SEd Maste }
560da67e6e6SEd Maste
561785600d0SJohn Baldwin return (elf_read_raw_string(ef->ef_efile, ofs, dest, len));
562da67e6e6SEd Maste }
563da67e6e6SEd Maste
5641eed250aSJake Burkholder int
ef_open(struct elf_file * efile,int verbose)5650299afdfSJohn Baldwin ef_open(struct elf_file *efile, int verbose)
5669c6f9240SPeter Wemm {
5679772dc2aSIan Dowse elf_file_t ef;
5680299afdfSJohn Baldwin GElf_Ehdr *hdr;
5690299afdfSJohn Baldwin size_t i, nphdr, nsegs;
5709c6f9240SPeter Wemm int error;
5710299afdfSJohn Baldwin GElf_Phdr *phdr, *phdyn;
5729c6f9240SPeter Wemm
5730299afdfSJohn Baldwin hdr = &efile->ef_hdr;
5740299afdfSJohn Baldwin if (hdr->e_phnum == 0 ||
5750299afdfSJohn Baldwin hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) ||
5760299afdfSJohn Baldwin hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
5770299afdfSJohn Baldwin hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
5780299afdfSJohn Baldwin return (EFTYPE);
5799772dc2aSIan Dowse
5809772dc2aSIan Dowse ef = malloc(sizeof(*ef));
5810299afdfSJohn Baldwin if (ef == NULL)
582e2d0802cSEd Maste return (errno);
5839772dc2aSIan Dowse
5849772dc2aSIan Dowse efile->ef_ef = ef;
5859772dc2aSIan Dowse efile->ef_ops = &ef_file_ops;
5869772dc2aSIan Dowse
5879772dc2aSIan Dowse bzero(ef, sizeof(*ef));
5889772dc2aSIan Dowse ef->ef_verbose = verbose;
5890299afdfSJohn Baldwin ef->ef_name = strdup(efile->ef_filename);
5909772dc2aSIan Dowse ef->ef_efile = efile;
5910299afdfSJohn Baldwin
5920299afdfSJohn Baldwin error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph);
5930299afdfSJohn Baldwin if (error != 0) {
5940299afdfSJohn Baldwin phdr = NULL;
5950299afdfSJohn Baldwin goto out;
5960299afdfSJohn Baldwin }
5970299afdfSJohn Baldwin
5989c6f9240SPeter Wemm error = EFTYPE;
5999c6f9240SPeter Wemm nsegs = 0;
6009c6f9240SPeter Wemm phdyn = NULL;
6010299afdfSJohn Baldwin phdr = ef->ef_ph;
6020299afdfSJohn Baldwin for (i = 0; i < nphdr; i++, phdr++) {
6039c6f9240SPeter Wemm if (verbose > 1)
6049c6f9240SPeter Wemm ef_print_phdr(phdr);
6059c6f9240SPeter Wemm switch (phdr->p_type) {
6069c6f9240SPeter Wemm case PT_LOAD:
6072bd7b9e5SEd Maste if (nsegs < MAXSEGS)
6082bd7b9e5SEd Maste ef->ef_segs[nsegs] = phdr;
6092bd7b9e5SEd Maste nsegs++;
6109c6f9240SPeter Wemm break;
6119c6f9240SPeter Wemm case PT_PHDR:
6129c6f9240SPeter Wemm break;
6139c6f9240SPeter Wemm case PT_DYNAMIC:
6149c6f9240SPeter Wemm phdyn = phdr;
6159c6f9240SPeter Wemm break;
6169c6f9240SPeter Wemm }
6179c6f9240SPeter Wemm }
6189c6f9240SPeter Wemm if (verbose > 1)
6199c6f9240SPeter Wemm printf("\n");
6209c6f9240SPeter Wemm if (phdyn == NULL) {
6215db4d677STim Kientzle warnx("Skipping %s: not dynamically-linked",
6220299afdfSJohn Baldwin ef->ef_name);
6230299afdfSJohn Baldwin goto out;
6240299afdfSJohn Baldwin }
6250299afdfSJohn Baldwin
6260299afdfSJohn Baldwin if (nsegs > MAXSEGS) {
6270299afdfSJohn Baldwin warnx("%s: too many segments", ef->ef_name);
6280299afdfSJohn Baldwin goto out;
6299c6f9240SPeter Wemm }
6302bd7b9e5SEd Maste ef->ef_nsegs = nsegs;
6310299afdfSJohn Baldwin
6320299afdfSJohn Baldwin error = ef_parse_dynamic(ef, phdyn);
6330299afdfSJohn Baldwin out:
634e2d0802cSEd Maste if (error != 0)
6359c6f9240SPeter Wemm ef_close(ef);
636e2d0802cSEd Maste return (error);
6379c6f9240SPeter Wemm }
6389c6f9240SPeter Wemm
6390299afdfSJohn Baldwin static void
ef_close(elf_file_t ef)6409c6f9240SPeter Wemm ef_close(elf_file_t ef)
6419c6f9240SPeter Wemm {
6420299afdfSJohn Baldwin free(ef->ef_rela);
6430299afdfSJohn Baldwin free(ef->ef_rel);
6440299afdfSJohn Baldwin free(ef->ef_strtab);
6450299afdfSJohn Baldwin free(ef->ef_symtab);
6460299afdfSJohn Baldwin free(ef->ef_hashtab);
6470299afdfSJohn Baldwin free(ef->ef_ph);
6489c6f9240SPeter Wemm if (ef->ef_name)
6499c6f9240SPeter Wemm free(ef->ef_name);
6509772dc2aSIan Dowse ef->ef_efile->ef_ops = NULL;
6519772dc2aSIan Dowse ef->ef_efile->ef_ef = NULL;
6529772dc2aSIan Dowse free(ef);
6539c6f9240SPeter Wemm }
654