ef.c revision 100272
183322Speter/*
283322Speter * Copyright (c) 2000, Boris Popov
383322Speter * All rights reserved.
483322Speter *
583322Speter * Redistribution and use in source and binary forms, with or without
683322Speter * modification, are permitted provided that the following conditions
783322Speter * are met:
883322Speter * 1. Redistributions of source code must retain the above copyright
983322Speter *    notice, this list of conditions and the following disclaimer.
1083322Speter * 2. Redistributions in binary form must reproduce the above copyright
1183322Speter *    notice, this list of conditions and the following disclaimer in the
1283322Speter *    documentation and/or other materials provided with the distribution.
1383322Speter * 3. All advertising materials mentioning features or use of this software
1483322Speter *    must display the following acknowledgement:
1583322Speter *    This product includes software developed by Boris Popov.
1683322Speter * 4. Neither the name of the author nor the names of any co-contributors
1783322Speter *    may be used to endorse or promote products derived from this software
1883322Speter *    without specific prior written permission.
1983322Speter *
2083322Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2183322Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2283322Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2383322Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2483322Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2583322Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2683322Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2783322Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2883322Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2983322Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3083322Speter * SUCH DAMAGE.
3183322Speter *
3283322Speter * $FreeBSD: head/usr.sbin/kldxref/ef.c 100272 2002-07-17 23:41:58Z peter $
3383322Speter */
3483322Speter
3583322Speter#include <sys/param.h>
3683322Speter#include <sys/linker.h>
3783322Speter#include <string.h>
38100272Speter#include <stdio.h>
39100272Speter#include <stdlib.h>
40100272Speter#include <unistd.h>
41100272Speter#include <errno.h>
42100272Speter#include <fcntl.h>
4383322Speter#include <machine/elf.h>
4483322Speter#define FREEBSD_ELF
4583322Speter#include <link.h>
4683322Speter
4783322Speter#include <err.h>
4883322Speter
4983322Speter#include "ef.h"
5083322Speter
5187551Smikehstatic void ef_print_phdr(Elf_Phdr *);
5287551Smikehstatic u_long ef_get_offset(elf_file_t, Elf_Off);
5387551Smikehstatic int ef_parse_dynamic(elf_file_t);
5487551Smikeh
5583322Spetervoid
5683322Speteref_print_phdr(Elf_Phdr *phdr)
5783322Speter{
5883322Speter
5983322Speter	if ((phdr->p_flags & PF_W) == 0) {
6083322Speter		printf("text=0x%lx ", (long)phdr->p_filesz);
6183322Speter	} else {
6283322Speter		printf("data=0x%lx", (long)phdr->p_filesz);
6383322Speter		if (phdr->p_filesz < phdr->p_memsz)
6483322Speter			printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
6583322Speter		printf(" ");
6683322Speter	}
6783322Speter}
6883322Speter
6983322Speteru_long
7083322Speteref_get_offset(elf_file_t ef, Elf_Off off)
7183322Speter{
7283322Speter	Elf_Phdr *ph;
7383322Speter	int i;
7483322Speter
7583322Speter	for (i = 0; i < ef->ef_nsegs; i++) {
7683322Speter		ph = ef->ef_segs[i];
7783322Speter		if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
7883322Speter			return ph->p_offset + (off - ph->p_vaddr);
7983322Speter		}
8083322Speter	}
8183322Speter	return 0;
8283322Speter}
8383322Speter
8483322Speter/*
8583322Speter * next three functions copied from link_elf.c
8683322Speter */
8783322Speterstatic unsigned long
8883322Speterelf_hash(const char *name)
8983322Speter{
9083322Speter	const unsigned char *p = (const unsigned char *) name;
9183322Speter	unsigned long h = 0;
9283322Speter	unsigned long g;
9383322Speter
9483322Speter	while (*p != '\0') {
9583322Speter		h = (h << 4) + *p++;
9683322Speter		if ((g = h & 0xf0000000) != 0)
9783322Speter			h ^= g >> 24;
9883322Speter		h &= ~g;
9983322Speter	}
10083322Speter	return h;
10183322Speter}
10283322Speter
10383322Speterint
10483322Speteref_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
10583322Speter{
10683322Speter	unsigned long symnum;
10783322Speter	Elf_Sym* symp;
10883322Speter	char *strp;
10983322Speter	unsigned long hash;
11083322Speter
11183322Speter	/* First, search hashed global symbols */
11283322Speter	hash = elf_hash(name);
11383322Speter	symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
11483322Speter
11583322Speter	while (symnum != STN_UNDEF) {
11683322Speter		if (symnum >= ef->ef_nchains) {
11783322Speter			warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
11883322Speter			    ef->ef_name);
11983322Speter			return ENOENT;
12083322Speter		}
12183322Speter
12283322Speter		symp = ef->ef_symtab + symnum;
12383322Speter		if (symp->st_name == 0) {
12483322Speter			warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
12583322Speter			    ef->ef_name);
12683322Speter			return ENOENT;
12783322Speter		}
12883322Speter
12983322Speter		strp = ef->ef_strtab + symp->st_name;
13083322Speter
13183322Speter		if (strcmp(name, strp) == 0) {
13283322Speter			if (symp->st_shndx != SHN_UNDEF ||
13383322Speter			    (symp->st_value != 0 &&
13483322Speter				ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
13583322Speter				*sym = symp;
13683322Speter				return 0;
13783322Speter			} else
13883322Speter				return ENOENT;
13983322Speter		}
14083322Speter
14183322Speter		symnum = ef->ef_chains[symnum];
14283322Speter	}
14383322Speter
14483322Speter	return ENOENT;
14583322Speter}
14683322Speter
14783322Speterint
14883322Speteref_parse_dynamic(elf_file_t ef)
14983322Speter{
15083322Speter	Elf_Dyn *dp;
15194414Speter	Elf_Hashelt hashhdr[2];
15283322Speter/*	int plttype = DT_REL;*/
15383322Speter	int error;
15483322Speter
15583322Speter	for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
15683322Speter		switch (dp->d_tag) {
15783322Speter		case DT_HASH:
15883322Speter			error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
15983322Speter			    sizeof(hashhdr),  hashhdr);
16083322Speter			if (error) {
16183322Speter				warnx("can't read hash header (%lx)",
16283322Speter				    ef_get_offset(ef, dp->d_un.d_ptr));
16383322Speter				return error;
16483322Speter			}
16583322Speter			ef->ef_nbuckets = hashhdr[0];
16683322Speter			ef->ef_nchains = hashhdr[1];
16783322Speter			error = ef_read_entry(ef, -1,
16894414Speter			    (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt),
16983322Speter			    (void**)&ef->ef_hashtab);
17083322Speter			if (error) {
17183322Speter				warnx("can't read hash table");
17283322Speter				return error;
17383322Speter			}
17483322Speter			ef->ef_buckets = ef->ef_hashtab;
17583322Speter			ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
17683322Speter			break;
17783322Speter		case DT_STRTAB:
17883322Speter			ef->ef_stroff = dp->d_un.d_ptr;
17983322Speter			break;
18083322Speter		case DT_STRSZ:
18183322Speter			ef->ef_strsz = dp->d_un.d_val;
18283322Speter			break;
18383322Speter		case DT_SYMTAB:
18483322Speter			ef->ef_symoff = dp->d_un.d_ptr;
18583322Speter			break;
18683322Speter		case DT_SYMENT:
18783322Speter			if (dp->d_un.d_val != sizeof(Elf_Sym))
18883322Speter				return EFTYPE;
18983322Speter			break;
19083322Speter		}
19183322Speter	}
19283322Speter	if (ef->ef_symoff == 0) {
19383322Speter		warnx("%s: no .dynsym section found\n", ef->ef_name);
19483322Speter		return EFTYPE;
19583322Speter	}
19683322Speter	if (ef->ef_stroff == 0) {
19783322Speter		warnx("%s: no .dynstr section found\n", ef->ef_name);
19883322Speter		return EFTYPE;
19983322Speter	}
20083322Speter	if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
20183322Speter	    ef->ef_nchains * sizeof(Elf_Sym),
20283322Speter		(void**)&ef->ef_symtab) != 0) {
20383322Speter		if (ef->ef_verbose)
20483322Speter			warnx("%s: can't load .dynsym section (0x%lx)",
20583322Speter			    ef->ef_name, (long)ef->ef_symoff);
20683322Speter		return EIO;
20783322Speter	}
20883322Speter	if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
20983322Speter		(void**)&ef->ef_strtab) != 0) {
21083322Speter		warnx("can't load .dynstr section");
21183322Speter		return EIO;
21283322Speter	}
21383322Speter	return 0;
21483322Speter}
21583322Speter
21683322Speterint
21783322Speteref_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
21883322Speter{
21987551Smikeh	ssize_t r;
22083322Speter
22187551Smikeh	if (offset != (Elf_Off)-1) {
22283322Speter		if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
22383322Speter			return EIO;
22483322Speter	}
22587551Smikeh
22687551Smikeh	r = read(ef->ef_fd, dest, len);
22787551Smikeh	if (r != -1 && (size_t)r == len)
22887551Smikeh		return 0;
22987551Smikeh	else
23087551Smikeh		return EIO;
23183322Speter}
23283322Speter
23383322Speterint
23483322Speteref_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
23583322Speter{
23683322Speter	int error;
23783322Speter
23883322Speter	*ptr = malloc(len);
23983322Speter	if (*ptr == NULL)
24083322Speter		return ENOMEM;
24183322Speter	error = ef_read(ef, offset, len, *ptr);
24283322Speter	if (error)
24383322Speter		free(*ptr);
24483322Speter	return error;
24583322Speter}
24683322Speter
24783322Speterint
24883322Speteref_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
24983322Speter{
25083322Speter	u_long ofs = ef_get_offset(ef, offset);
25183322Speter
25283322Speter	if (ofs == 0) {
25383322Speter		if (ef->ef_verbose)
25483322Speter			warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
25583322Speter			    ef->ef_name, (long)offset, ofs);
25683322Speter		return EFAULT;
25783322Speter	}
25883322Speter	return ef_read(ef, ofs, len, dest);
25983322Speter}
26083322Speter
26183322Speterint
26283322Speteref_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
26383322Speter{
26483322Speter	int error;
26583322Speter
26683322Speter	*ptr = malloc(len);
26783322Speter	if (*ptr == NULL)
26883322Speter		return ENOMEM;
26983322Speter	error = ef_seg_read(ef, offset, len, *ptr);
27083322Speter	if (error)
27183322Speter		free(*ptr);
27283322Speter	return error;
27383322Speter}
27483322Speter
27583322Speterint
27683322Speteref_open(const char *filename, elf_file_t ef, int verbose)
27783322Speter{
27883322Speter	Elf_Ehdr *hdr;
27983322Speter	int fd;
28083322Speter	int error;
28183322Speter	int phlen, res;
28283322Speter	int nsegs;
28383322Speter	Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit;
28483322Speter
28583322Speter	bzero(ef, sizeof(*ef));
28683322Speter	if (filename == NULL)
28783322Speter		return EFTYPE;
28883322Speter	ef->ef_verbose = verbose;
28983322Speter	if ((fd = open(filename, O_RDONLY)) == -1)
29083322Speter		return errno;
29183322Speter	ef->ef_fd = fd;
29283322Speter	ef->ef_name = strdup(filename);
29383322Speter	hdr = (Elf_Ehdr *)&ef->ef_hdr;
29483322Speter	do {
29583322Speter		res = read(fd, hdr, sizeof(*hdr));
29683322Speter		error = EFTYPE;
29783322Speter		if (res != sizeof(*hdr))
29883322Speter			break;
29983322Speter		if (!IS_ELF(*hdr))
30083322Speter			break;
30183322Speter		if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
30283322Speter		    hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
30383322Speter		    hdr->e_ident[EI_VERSION] != EV_CURRENT ||
30483322Speter		    hdr->e_version != EV_CURRENT ||
30583322Speter		    hdr->e_machine != ELF_TARG_MACH ||
30683322Speter		    hdr->e_phentsize != sizeof(Elf_Phdr))
30783322Speter			break;
30883322Speter		phlen = hdr->e_phnum * sizeof(Elf_Phdr);
30983322Speter		if (ef_read_entry(ef, hdr->e_phoff, phlen,
31083322Speter		    (void**)&ef->ef_ph) != 0)
31183322Speter			break;
31283322Speter		phdr = ef->ef_ph;
31383322Speter		phlimit = phdr + hdr->e_phnum;
31483322Speter		nsegs = 0;
31583322Speter		phdyn = NULL;
31683322Speter		phphdr = NULL;
31783322Speter		while (phdr < phlimit) {
31883322Speter			if (verbose > 1)
31983322Speter				ef_print_phdr(phdr);
32083322Speter			switch (phdr->p_type) {
32183322Speter			case PT_LOAD:
32283322Speter				if (nsegs == 2) {
32383322Speter					warnx("%s: too many sections",
32483322Speter					    filename);
32583322Speter					break;
32683322Speter				}
32783322Speter				ef->ef_segs[nsegs++] = phdr;
32883322Speter				break;
32983322Speter			case PT_PHDR:
33083322Speter				phphdr = phdr;
33183322Speter				break;
33283322Speter			case PT_DYNAMIC:
33383322Speter				phdyn = phdr;
33483322Speter				break;
33583322Speter			}
33683322Speter			phdr++;
33783322Speter		}
33883322Speter		if (verbose > 1)
33983322Speter			printf("\n");
34083322Speter		ef->ef_nsegs = nsegs;
34183322Speter		if (phdyn == NULL) {
34283322Speter			warnx("file isn't dynamically-linked");
34383322Speter			break;
34483322Speter		}
34583322Speter		if (ef_read_entry(ef, phdyn->p_offset,
34683322Speter			phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
34783322Speter			printf("ef_read_entry failed\n");
34883322Speter			break;
34983322Speter		}
35083322Speter		error = ef_parse_dynamic(ef);
35183322Speter		if (error)
35283322Speter			break;
35383322Speter		if (hdr->e_type == ET_DYN) {
35483322Speter			ef->ef_type = EFT_KLD;
35583322Speter/*			pad = (u_int)dest & PAGE_MASK;
35683322Speter			if (pad)
35783322Speter				dest += PAGE_SIZE - pad;*/
35883322Speter			error = 0;
35983322Speter		} else if (hdr->e_type == ET_EXEC) {
36083322Speter/*			dest = hdr->e_entry;
36183322Speter			if (dest == 0)
36283322Speter				break;*/
36383322Speter			ef->ef_type = EFT_KERNEL;
36483322Speter			error = 0;
36583322Speter		} else
36683322Speter			break;
36783322Speter	} while(0);
36883322Speter	if (error) {
36983322Speter		ef_close(ef);
37083322Speter		if (ef->ef_verbose)
37183322Speter			warnc(error, "elf_open(%s)", filename);
37283322Speter	}
37383322Speter	return error;
37483322Speter}
37583322Speter
37683322Speterint
37783322Speteref_close(elf_file_t ef)
37883322Speter{
37983322Speter	close(ef->ef_fd);
38083322Speter/*	if (ef->ef_fpage)
38183322Speter		free(ef->ef_fpage);*/
38283322Speter	if (ef->ef_name)
38383322Speter		free(ef->ef_name);
38483322Speter	return 0;
38583322Speter}
386