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