xref: /freebsd/usr.sbin/kldxref/ef.c (revision 785600d0)
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