ef.c revision 109607
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: head/usr.sbin/kldxref/ef.c 109607 2003-01-21 03:51:53Z jake $
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#include <link.h>
46
47#include <err.h>
48
49#include "ef.h"
50
51static void ef_print_phdr(Elf_Phdr *);
52static u_long ef_get_offset(elf_file_t, Elf_Off);
53static int ef_parse_dynamic(elf_file_t);
54
55void
56ef_print_phdr(Elf_Phdr *phdr)
57{
58
59	if ((phdr->p_flags & PF_W) == 0) {
60		printf("text=0x%lx ", (long)phdr->p_filesz);
61	} else {
62		printf("data=0x%lx", (long)phdr->p_filesz);
63		if (phdr->p_filesz < phdr->p_memsz)
64			printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
65		printf(" ");
66	}
67}
68
69u_long
70ef_get_offset(elf_file_t ef, Elf_Off off)
71{
72	Elf_Phdr *ph;
73	int i;
74
75	for (i = 0; i < ef->ef_nsegs; i++) {
76		ph = ef->ef_segs[i];
77		if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
78			return ph->p_offset + (off - ph->p_vaddr);
79		}
80	}
81	return 0;
82}
83
84/*
85 * next three functions copied from link_elf.c
86 */
87static unsigned long
88elf_hash(const char *name)
89{
90	const unsigned char *p = (const unsigned char *) name;
91	unsigned long h = 0;
92	unsigned long g;
93
94	while (*p != '\0') {
95		h = (h << 4) + *p++;
96		if ((g = h & 0xf0000000) != 0)
97			h ^= g >> 24;
98		h &= ~g;
99	}
100	return h;
101}
102
103int
104ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
105{
106	unsigned long symnum;
107	Elf_Sym* symp;
108	char *strp;
109	unsigned long hash;
110
111	/* First, search hashed global symbols */
112	hash = elf_hash(name);
113	symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
114
115	while (symnum != STN_UNDEF) {
116		if (symnum >= ef->ef_nchains) {
117			warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
118			    ef->ef_name);
119			return ENOENT;
120		}
121
122		symp = ef->ef_symtab + symnum;
123		if (symp->st_name == 0) {
124			warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
125			    ef->ef_name);
126			return ENOENT;
127		}
128
129		strp = ef->ef_strtab + symp->st_name;
130
131		if (strcmp(name, strp) == 0) {
132			if (symp->st_shndx != SHN_UNDEF ||
133			    (symp->st_value != 0 &&
134				ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
135				*sym = symp;
136				return 0;
137			} else
138				return ENOENT;
139		}
140
141		symnum = ef->ef_chains[symnum];
142	}
143
144	return ENOENT;
145}
146
147int
148ef_parse_dynamic(elf_file_t ef)
149{
150	Elf_Dyn *dp;
151	Elf_Hashelt hashhdr[2];
152/*	int plttype = DT_REL;*/
153	int error;
154	Elf_Off rel_off;
155	Elf_Off rela_off;
156	int rel_sz;
157	int rela_sz;
158	int rel_entry;
159	int rela_entry;
160
161	rel_off = rela_off = 0;
162	rel_sz = rela_sz = 0;
163	rel_entry = rela_entry = 0;
164	for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
165		switch (dp->d_tag) {
166		case DT_HASH:
167			error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
168			    sizeof(hashhdr),  hashhdr);
169			if (error) {
170				warnx("can't read hash header (%lx)",
171				    ef_get_offset(ef, dp->d_un.d_ptr));
172				return error;
173			}
174			ef->ef_nbuckets = hashhdr[0];
175			ef->ef_nchains = hashhdr[1];
176			error = ef_read_entry(ef, -1,
177			    (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt),
178			    (void**)&ef->ef_hashtab);
179			if (error) {
180				warnx("can't read hash table");
181				return error;
182			}
183			ef->ef_buckets = ef->ef_hashtab;
184			ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
185			break;
186		case DT_STRTAB:
187			ef->ef_stroff = dp->d_un.d_ptr;
188			break;
189		case DT_STRSZ:
190			ef->ef_strsz = dp->d_un.d_val;
191			break;
192		case DT_SYMTAB:
193			ef->ef_symoff = dp->d_un.d_ptr;
194			break;
195		case DT_SYMENT:
196			if (dp->d_un.d_val != sizeof(Elf_Sym))
197				return EFTYPE;
198			break;
199		case DT_REL:
200			if (rel_off != 0)
201				warnx("second DT_REL entry ignored");
202			rel_off = dp->d_un.d_ptr;
203			break;
204		case DT_RELSZ:
205			if (rel_sz != 0)
206				warnx("second DT_RELSZ entry ignored");
207			rel_sz = dp->d_un.d_val;
208			break;
209		case DT_RELENT:
210			if (rel_entry != 0)
211				warnx("second DT_RELENT entry ignored");
212			rel_entry = dp->d_un.d_val;
213			break;
214		case DT_RELA:
215			if (rela_off != 0)
216				warnx("second DT_RELA entry ignored");
217			rela_off = dp->d_un.d_ptr;
218			break;
219		case DT_RELASZ:
220			if (rela_sz != 0)
221				warnx("second DT_RELASZ entry ignored");
222			rela_sz = dp->d_un.d_val;
223			break;
224		case DT_RELAENT:
225			if (rela_entry != 0)
226				warnx("second DT_RELAENT entry ignored");
227			rela_entry = dp->d_un.d_val;
228			break;
229		}
230	}
231	if (ef->ef_symoff == 0) {
232		warnx("%s: no .dynsym section found\n", ef->ef_name);
233		return EFTYPE;
234	}
235	if (ef->ef_stroff == 0) {
236		warnx("%s: no .dynstr section found\n", ef->ef_name);
237		return EFTYPE;
238	}
239	if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
240	    ef->ef_nchains * sizeof(Elf_Sym),
241		(void**)&ef->ef_symtab) != 0) {
242		if (ef->ef_verbose)
243			warnx("%s: can't load .dynsym section (0x%lx)",
244			    ef->ef_name, (long)ef->ef_symoff);
245		return EIO;
246	}
247	if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
248		(void**)&ef->ef_strtab) != 0) {
249		warnx("can't load .dynstr section");
250		return EIO;
251	}
252	if (rel_off != 0) {
253		if (rel_entry == 0) {
254			warnx("%s: no DT_RELENT for DT_REL", ef->ef_name);
255			return (EFTYPE);
256		}
257		if (rel_entry != sizeof(Elf_Rel)) {
258			warnx("%s: inconsistent DT_RELENT value",
259			    ef->ef_name);
260			return (EFTYPE);
261		}
262		if (rel_sz % rel_entry != 0) {
263			warnx("%s: inconsistent values for DT_RELSZ and "
264			    "DT_RELENT", ef->ef_name);
265			return (EFTYPE);
266		}
267		if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz,
268		    (void **)&ef->ef_rel) != 0) {
269			warnx("%s: cannot load DT_REL section", ef->ef_name);
270			return (EIO);
271		}
272		ef->ef_relsz = rel_sz / rel_entry;
273		if (ef->ef_verbose)
274			warnx("%s: %d REL entries", ef->ef_name,
275			    ef->ef_relsz);
276	}
277	if (rela_off != 0) {
278		if (rela_entry == 0) {
279			warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name);
280			return (EFTYPE);
281		}
282		if (rela_entry != sizeof(Elf_Rela)) {
283			warnx("%s: inconsistent DT_RELAENT value",
284			    ef->ef_name);
285			return (EFTYPE);
286		}
287		if (rela_sz % rela_entry != 0) {
288			warnx("%s: inconsistent values for DT_RELASZ and "
289			    "DT_RELAENT", ef->ef_name);
290			return (EFTYPE);
291		}
292		if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz,
293		    (void **)&ef->ef_rela) != 0) {
294			warnx("%s: cannot load DT_RELA section", ef->ef_name);
295			return (EIO);
296		}
297		ef->ef_relasz = rela_sz / rela_entry;
298		if (ef->ef_verbose)
299			warnx("%s: %d RELA entries", ef->ef_name,
300			    ef->ef_relasz);
301	}
302	return 0;
303}
304
305int
306ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
307{
308	ssize_t r;
309
310	if (offset != (Elf_Off)-1) {
311		if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
312			return EIO;
313	}
314
315	r = read(ef->ef_fd, dest, len);
316	if (r != -1 && (size_t)r == len)
317		return 0;
318	else
319		return EIO;
320}
321
322int
323ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
324{
325	int error;
326
327	*ptr = malloc(len);
328	if (*ptr == NULL)
329		return ENOMEM;
330	error = ef_read(ef, offset, len, *ptr);
331	if (error)
332		free(*ptr);
333	return error;
334}
335
336int
337ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
338{
339	u_long ofs = ef_get_offset(ef, offset);
340
341	if (ofs == 0) {
342		if (ef->ef_verbose)
343			warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
344			    ef->ef_name, (long)offset, ofs);
345		return EFAULT;
346	}
347	return ef_read(ef, ofs, len, dest);
348}
349
350int
351ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
352{
353	u_long ofs = ef_get_offset(ef, offset);
354	int error;
355
356	if (ofs == 0) {
357		if (ef->ef_verbose)
358			warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
359			    ef->ef_name, (long)offset, ofs);
360		return EFAULT;
361	}
362	if ((error = ef_read(ef, ofs, len, dest)) != 0)
363		return (error);
364	return (ef_reloc(ef, offset, len, dest));
365}
366
367int
368ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
369{
370	int error;
371
372	*ptr = malloc(len);
373	if (*ptr == NULL)
374		return ENOMEM;
375	error = ef_seg_read(ef, offset, len, *ptr);
376	if (error)
377		free(*ptr);
378	return error;
379}
380
381int
382ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
383{
384	int error;
385
386	*ptr = malloc(len);
387	if (*ptr == NULL)
388		return ENOMEM;
389	error = ef_seg_read_rel(ef, offset, len, *ptr);
390	if (error)
391		free(*ptr);
392	return error;
393}
394
395int
396ef_open(const char *filename, elf_file_t ef, int verbose)
397{
398	Elf_Ehdr *hdr;
399	int fd;
400	int error;
401	int phlen, res;
402	int nsegs;
403	Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit;
404
405	bzero(ef, sizeof(*ef));
406	if (filename == NULL)
407		return EFTYPE;
408	ef->ef_verbose = verbose;
409	if ((fd = open(filename, O_RDONLY)) == -1)
410		return errno;
411	ef->ef_fd = fd;
412	ef->ef_name = strdup(filename);
413	hdr = (Elf_Ehdr *)&ef->ef_hdr;
414	do {
415		res = read(fd, hdr, sizeof(*hdr));
416		error = EFTYPE;
417		if (res != sizeof(*hdr))
418			break;
419		if (!IS_ELF(*hdr))
420			break;
421		if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
422		    hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
423		    hdr->e_ident[EI_VERSION] != EV_CURRENT ||
424		    hdr->e_version != EV_CURRENT ||
425		    hdr->e_machine != ELF_TARG_MACH ||
426		    hdr->e_phentsize != sizeof(Elf_Phdr))
427			break;
428		phlen = hdr->e_phnum * sizeof(Elf_Phdr);
429		if (ef_read_entry(ef, hdr->e_phoff, phlen,
430		    (void**)&ef->ef_ph) != 0)
431			break;
432		phdr = ef->ef_ph;
433		phlimit = phdr + hdr->e_phnum;
434		nsegs = 0;
435		phdyn = NULL;
436		phphdr = NULL;
437		while (phdr < phlimit) {
438			if (verbose > 1)
439				ef_print_phdr(phdr);
440			switch (phdr->p_type) {
441			case PT_LOAD:
442				if (nsegs == 2) {
443					warnx("%s: too many sections",
444					    filename);
445					break;
446				}
447				ef->ef_segs[nsegs++] = phdr;
448				break;
449			case PT_PHDR:
450				phphdr = phdr;
451				break;
452			case PT_DYNAMIC:
453				phdyn = phdr;
454				break;
455			}
456			phdr++;
457		}
458		if (verbose > 1)
459			printf("\n");
460		ef->ef_nsegs = nsegs;
461		if (phdyn == NULL) {
462			warnx("file isn't dynamically-linked");
463			break;
464		}
465		if (ef_read_entry(ef, phdyn->p_offset,
466			phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
467			printf("ef_read_entry failed\n");
468			break;
469		}
470		error = ef_parse_dynamic(ef);
471		if (error)
472			break;
473		if (hdr->e_type == ET_DYN) {
474			ef->ef_type = EFT_KLD;
475/*			pad = (u_int)dest & PAGE_MASK;
476			if (pad)
477				dest += PAGE_SIZE - pad;*/
478			error = 0;
479		} else if (hdr->e_type == ET_EXEC) {
480/*			dest = hdr->e_entry;
481			if (dest == 0)
482				break;*/
483			ef->ef_type = EFT_KERNEL;
484			error = 0;
485		} else
486			break;
487	} while(0);
488	if (error) {
489		ef_close(ef);
490		if (ef->ef_verbose)
491			warnc(error, "elf_open(%s)", filename);
492	}
493	return error;
494}
495
496int
497ef_close(elf_file_t ef)
498{
499	close(ef->ef_fd);
500/*	if (ef->ef_fpage)
501		free(ef->ef_fpage);*/
502	if (ef->ef_name)
503		free(ef->ef_name);
504	return 0;
505}
506