ef.c revision 87551
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 87551 2001-12-09 07:22:26Z mikeh $
33 */
34
35#include <sys/param.h>
36#include <sys/exec.h>
37#include <sys/queue.h>
38#include <sys/kernel.h>
39#include <sys/reboot.h>
40#include <sys/linker.h>
41#include <string.h>
42#include <machine/bootinfo.h>
43#include <machine/elf.h>
44#include <stand.h>
45#define FREEBSD_ELF
46#include <link.h>
47
48#include <err.h>
49
50#include "ef.h"
51
52static void ef_print_phdr(Elf_Phdr *);
53static u_long ef_get_offset(elf_file_t, Elf_Off);
54static int ef_parse_dynamic(elf_file_t);
55
56void
57ef_print_phdr(Elf_Phdr *phdr)
58{
59
60	if ((phdr->p_flags & PF_W) == 0) {
61		printf("text=0x%lx ", (long)phdr->p_filesz);
62	} else {
63		printf("data=0x%lx", (long)phdr->p_filesz);
64		if (phdr->p_filesz < phdr->p_memsz)
65			printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
66		printf(" ");
67	}
68}
69
70u_long
71ef_get_offset(elf_file_t ef, Elf_Off off)
72{
73	Elf_Phdr *ph;
74	int i;
75
76	for (i = 0; i < ef->ef_nsegs; i++) {
77		ph = ef->ef_segs[i];
78		if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
79			return ph->p_offset + (off - ph->p_vaddr);
80		}
81	}
82	return 0;
83}
84
85/*
86 * next three functions copied from link_elf.c
87 */
88static unsigned long
89elf_hash(const char *name)
90{
91	const unsigned char *p = (const unsigned char *) name;
92	unsigned long h = 0;
93	unsigned long g;
94
95	while (*p != '\0') {
96		h = (h << 4) + *p++;
97		if ((g = h & 0xf0000000) != 0)
98			h ^= g >> 24;
99		h &= ~g;
100	}
101	return h;
102}
103
104int
105ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
106{
107	unsigned long symnum;
108	Elf_Sym* symp;
109	char *strp;
110	unsigned long hash;
111
112	/* First, search hashed global symbols */
113	hash = elf_hash(name);
114	symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
115
116	while (symnum != STN_UNDEF) {
117		if (symnum >= ef->ef_nchains) {
118			warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
119			    ef->ef_name);
120			return ENOENT;
121		}
122
123		symp = ef->ef_symtab + symnum;
124		if (symp->st_name == 0) {
125			warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
126			    ef->ef_name);
127			return ENOENT;
128		}
129
130		strp = ef->ef_strtab + symp->st_name;
131
132		if (strcmp(name, strp) == 0) {
133			if (symp->st_shndx != SHN_UNDEF ||
134			    (symp->st_value != 0 &&
135				ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
136				*sym = symp;
137				return 0;
138			} else
139				return ENOENT;
140		}
141
142		symnum = ef->ef_chains[symnum];
143	}
144
145	return ENOENT;
146}
147
148int
149ef_parse_dynamic(elf_file_t ef)
150{
151	Elf_Dyn *dp;
152	Elf_Off hashhdr[2];
153/*	int plttype = DT_REL;*/
154	int error;
155
156	for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
157		switch (dp->d_tag) {
158		case DT_HASH:
159			error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
160			    sizeof(hashhdr),  hashhdr);
161			if (error) {
162				warnx("can't read hash header (%lx)",
163				    ef_get_offset(ef, dp->d_un.d_ptr));
164				return error;
165			}
166			ef->ef_nbuckets = hashhdr[0];
167			ef->ef_nchains = hashhdr[1];
168			error = ef_read_entry(ef, -1,
169			    (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Off),
170			    (void**)&ef->ef_hashtab);
171			if (error) {
172				warnx("can't read hash table");
173				return error;
174			}
175			ef->ef_buckets = ef->ef_hashtab;
176			ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
177			break;
178		case DT_STRTAB:
179			ef->ef_stroff = dp->d_un.d_ptr;
180			break;
181		case DT_STRSZ:
182			ef->ef_strsz = dp->d_un.d_val;
183			break;
184		case DT_SYMTAB:
185			ef->ef_symoff = dp->d_un.d_ptr;
186			break;
187		case DT_SYMENT:
188			if (dp->d_un.d_val != sizeof(Elf_Sym))
189				return EFTYPE;
190			break;
191		}
192	}
193	if (ef->ef_symoff == 0) {
194		warnx("%s: no .dynsym section found\n", ef->ef_name);
195		return EFTYPE;
196	}
197	if (ef->ef_stroff == 0) {
198		warnx("%s: no .dynstr section found\n", ef->ef_name);
199		return EFTYPE;
200	}
201	if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
202	    ef->ef_nchains * sizeof(Elf_Sym),
203		(void**)&ef->ef_symtab) != 0) {
204		if (ef->ef_verbose)
205			warnx("%s: can't load .dynsym section (0x%lx)",
206			    ef->ef_name, (long)ef->ef_symoff);
207		return EIO;
208	}
209	if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
210		(void**)&ef->ef_strtab) != 0) {
211		warnx("can't load .dynstr section");
212		return EIO;
213	}
214	return 0;
215}
216
217int
218ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
219{
220	ssize_t r;
221
222	if (offset != (Elf_Off)-1) {
223		if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
224			return EIO;
225	}
226
227	r = read(ef->ef_fd, dest, len);
228	if (r != -1 && (size_t)r == len)
229		return 0;
230	else
231		return EIO;
232}
233
234int
235ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
236{
237	int error;
238
239	*ptr = malloc(len);
240	if (*ptr == NULL)
241		return ENOMEM;
242	error = ef_read(ef, offset, len, *ptr);
243	if (error)
244		free(*ptr);
245	return error;
246}
247
248int
249ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
250{
251	u_long ofs = ef_get_offset(ef, offset);
252
253	if (ofs == 0) {
254		if (ef->ef_verbose)
255			warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
256			    ef->ef_name, (long)offset, ofs);
257		return EFAULT;
258	}
259	return ef_read(ef, ofs, len, dest);
260}
261
262int
263ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
264{
265	int error;
266
267	*ptr = malloc(len);
268	if (*ptr == NULL)
269		return ENOMEM;
270	error = ef_seg_read(ef, offset, len, *ptr);
271	if (error)
272		free(*ptr);
273	return error;
274}
275
276int
277ef_open(const char *filename, elf_file_t ef, int verbose)
278{
279	Elf_Ehdr *hdr;
280	int fd;
281	int error;
282	int phlen, res;
283	int nsegs;
284	Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit;
285
286	bzero(ef, sizeof(*ef));
287	if (filename == NULL)
288		return EFTYPE;
289	ef->ef_verbose = verbose;
290	if ((fd = open(filename, O_RDONLY)) == -1)
291		return errno;
292	ef->ef_fd = fd;
293	ef->ef_name = strdup(filename);
294	hdr = (Elf_Ehdr *)&ef->ef_hdr;
295	do {
296		res = read(fd, hdr, sizeof(*hdr));
297		error = EFTYPE;
298		if (res != sizeof(*hdr))
299			break;
300		if (!IS_ELF(*hdr))
301			break;
302		if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
303		    hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
304		    hdr->e_ident[EI_VERSION] != EV_CURRENT ||
305		    hdr->e_version != EV_CURRENT ||
306		    hdr->e_machine != ELF_TARG_MACH ||
307		    hdr->e_phentsize != sizeof(Elf_Phdr))
308			break;
309		phlen = hdr->e_phnum * sizeof(Elf_Phdr);
310		if (ef_read_entry(ef, hdr->e_phoff, phlen,
311		    (void**)&ef->ef_ph) != 0)
312			break;
313		phdr = ef->ef_ph;
314		phlimit = phdr + hdr->e_phnum;
315		nsegs = 0;
316		phdyn = NULL;
317		phphdr = NULL;
318		while (phdr < phlimit) {
319			if (verbose > 1)
320				ef_print_phdr(phdr);
321			switch (phdr->p_type) {
322			case PT_LOAD:
323				if (nsegs == 2) {
324					warnx("%s: too many sections",
325					    filename);
326					break;
327				}
328				ef->ef_segs[nsegs++] = phdr;
329				break;
330			case PT_PHDR:
331				phphdr = phdr;
332				break;
333			case PT_DYNAMIC:
334				phdyn = phdr;
335				break;
336			}
337			phdr++;
338		}
339		if (verbose > 1)
340			printf("\n");
341		ef->ef_nsegs = nsegs;
342		if (phdyn == NULL) {
343			warnx("file isn't dynamically-linked");
344			break;
345		}
346		if (ef_read_entry(ef, phdyn->p_offset,
347			phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
348			printf("ef_read_entry failed\n");
349			break;
350		}
351		error = ef_parse_dynamic(ef);
352		if (error)
353			break;
354		if (hdr->e_type == ET_DYN) {
355			ef->ef_type = EFT_KLD;
356/*			pad = (u_int)dest & PAGE_MASK;
357			if (pad)
358				dest += PAGE_SIZE - pad;*/
359			error = 0;
360		} else if (hdr->e_type == ET_EXEC) {
361/*			dest = hdr->e_entry;
362			if (dest == 0)
363				break;*/
364			ef->ef_type = EFT_KERNEL;
365			error = 0;
366		} else
367			break;
368	} while(0);
369	if (error) {
370		ef_close(ef);
371		if (ef->ef_verbose)
372			warnc(error, "elf_open(%s)", filename);
373	}
374	return error;
375}
376
377int
378ef_close(elf_file_t ef)
379{
380	close(ef->ef_fd);
381/*	if (ef->ef_fpage)
382		free(ef->ef_fpage);*/
383	if (ef->ef_name)
384		free(ef->ef_name);
385	return 0;
386}
387