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