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