diff options
author | peter <peter@FreeBSD.org> | 2001-09-11 01:13:15 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2001-09-11 01:13:15 +0000 |
commit | 2975fbf1b355f2dc0a55d5112534cfdc48d0e9df (patch) | |
tree | 86821a17ef3a1d1bb08bc8b0b0c46d5d91382f47 /usr.sbin/kldxref/ef.c | |
parent | 808991de7cecc9e774ffe5031e29362207e75564 (diff) | |
download | FreeBSD-src-2975fbf1b355f2dc0a55d5112534cfdc48d0e9df.zip FreeBSD-src-2975fbf1b355f2dc0a55d5112534cfdc48d0e9df.tar.gz |
Add kldxref(8), for maintaining the linker.hints file for translating
module->pathname.ko. It supports only ELF for now.
Submitted by: bp (with some minor tweaks)
Diffstat (limited to 'usr.sbin/kldxref/ef.c')
-rw-r--r-- | usr.sbin/kldxref/ef.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c new file mode 100644 index 0000000..9d3a478 --- /dev/null +++ b/usr.sbin/kldxref/ef.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/exec.h> +#include <sys/queue.h> +#include <sys/kernel.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <string.h> +#include <machine/bootinfo.h> +#include <machine/elf.h> +#include <stand.h> +#define FREEBSD_ELF +#include <link.h> + +#include <err.h> + +#include "ef.h" + +void +ef_print_phdr(Elf_Phdr *phdr) +{ + + if ((phdr->p_flags & PF_W) == 0) { + printf("text=0x%lx ", (long)phdr->p_filesz); + } else { + printf("data=0x%lx", (long)phdr->p_filesz); + if (phdr->p_filesz < phdr->p_memsz) + printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz)); + printf(" "); + } +} + +u_long +ef_get_offset(elf_file_t ef, Elf_Off off) +{ + Elf_Phdr *ph; + int i; + + for (i = 0; i < ef->ef_nsegs; i++) { + ph = ef->ef_segs[i]; + if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) { + return ph->p_offset + (off - ph->p_vaddr); + } + } + return 0; +} + +/* + * next three functions copied from link_elf.c + */ +static unsigned long +elf_hash(const char *name) +{ + const unsigned char *p = (const unsigned char *) name; + unsigned long h = 0; + unsigned long g; + + while (*p != '\0') { + h = (h << 4) + *p++; + if ((g = h & 0xf0000000) != 0) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +int +ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) +{ + unsigned long symnum; + Elf_Sym* symp; + char *strp; + unsigned long hash; + + /* First, search hashed global symbols */ + hash = elf_hash(name); + symnum = ef->ef_buckets[hash % ef->ef_nbuckets]; + + while (symnum != STN_UNDEF) { + if (symnum >= ef->ef_nchains) { + warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", + ef->ef_name); + return ENOENT; + } + + symp = ef->ef_symtab + symnum; + if (symp->st_name == 0) { + warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", + ef->ef_name); + return ENOENT; + } + + strp = ef->ef_strtab + symp->st_name; + + if (strcmp(name, strp) == 0) { + if (symp->st_shndx != SHN_UNDEF || + (symp->st_value != 0 && + ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { + *sym = symp; + return 0; + } else + return ENOENT; + } + + symnum = ef->ef_chains[symnum]; + } + + return ENOENT; +} + +int +ef_parse_dynamic(elf_file_t ef) +{ + Elf_Dyn *dp; + Elf_Off hashhdr[2]; +/* int plttype = DT_REL;*/ + int error; + + for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_HASH: + error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr), + sizeof(hashhdr), hashhdr); + if (error) { + warnx("can't read hash header (%lx)", + ef_get_offset(ef, dp->d_un.d_ptr)); + return error; + } + ef->ef_nbuckets = hashhdr[0]; + ef->ef_nchains = hashhdr[1]; + error = ef_read_entry(ef, -1, + (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Off), + (void**)&ef->ef_hashtab); + if (error) { + warnx("can't read hash table"); + return error; + } + ef->ef_buckets = ef->ef_hashtab; + ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; + break; + case DT_STRTAB: + ef->ef_stroff = dp->d_un.d_ptr; + break; + case DT_STRSZ: + ef->ef_strsz = dp->d_un.d_val; + break; + case DT_SYMTAB: + ef->ef_symoff = dp->d_un.d_ptr; + break; + case DT_SYMENT: + if (dp->d_un.d_val != sizeof(Elf_Sym)) + return EFTYPE; + break; + } + } + if (ef->ef_symoff == 0) { + warnx("%s: no .dynsym section found\n", ef->ef_name); + return EFTYPE; + } + if (ef->ef_stroff == 0) { + warnx("%s: no .dynstr section found\n", ef->ef_name); + return EFTYPE; + } + if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff), + ef->ef_nchains * sizeof(Elf_Sym), + (void**)&ef->ef_symtab) != 0) { + if (ef->ef_verbose) + warnx("%s: can't load .dynsym section (0x%lx)", + ef->ef_name, (long)ef->ef_symoff); + return EIO; + } + if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz, + (void**)&ef->ef_strtab) != 0) { + warnx("can't load .dynstr section"); + return EIO; + } + return 0; +} + +int +ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) +{ + + if (offset != -1) { + if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) + return EIO; + } + return read(ef->ef_fd, dest, len) == len ? 0 : EIO; +} + +int +ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) +{ + int error; + + *ptr = malloc(len); + if (*ptr == NULL) + return ENOMEM; + error = ef_read(ef, offset, len, *ptr); + if (error) + free(*ptr); + return error; +} + +int +ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) +{ + u_long ofs = ef_get_offset(ef, offset); + + if (ofs == 0) { + if (ef->ef_verbose) + warnx("ef_seg_read(%s): zero offset (%lx:%ld)", + ef->ef_name, (long)offset, ofs); + return EFAULT; + } + return ef_read(ef, ofs, len, dest); +} + +int +ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) +{ + int error; + + *ptr = malloc(len); + if (*ptr == NULL) + return ENOMEM; + error = ef_seg_read(ef, offset, len, *ptr); + if (error) + free(*ptr); + return error; +} + +int +ef_open(const char *filename, elf_file_t ef, int verbose) +{ + Elf_Ehdr *hdr; + int fd; + int error; + int phlen, res; + int nsegs; + Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit; + + bzero(ef, sizeof(*ef)); + if (filename == NULL) + return EFTYPE; + ef->ef_verbose = verbose; + if ((fd = open(filename, O_RDONLY)) == -1) + return errno; + ef->ef_fd = fd; + ef->ef_name = strdup(filename); + hdr = (Elf_Ehdr *)&ef->ef_hdr; + do { + res = read(fd, hdr, sizeof(*hdr)); + error = EFTYPE; + if (res != sizeof(*hdr)) + break; + if (!IS_ELF(*hdr)) + break; + if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || + hdr->e_ident[EI_DATA] != ELF_TARG_DATA || + hdr->e_ident[EI_VERSION] != EV_CURRENT || + hdr->e_version != EV_CURRENT || + hdr->e_machine != ELF_TARG_MACH || + hdr->e_phentsize != sizeof(Elf_Phdr)) + break; + phlen = hdr->e_phnum * sizeof(Elf_Phdr); + if (ef_read_entry(ef, hdr->e_phoff, phlen, + (void**)&ef->ef_ph) != 0) + break; + phdr = ef->ef_ph; + phlimit = phdr + hdr->e_phnum; + nsegs = 0; + phdyn = NULL; + phphdr = NULL; + while (phdr < phlimit) { + if (verbose > 1) + ef_print_phdr(phdr); + switch (phdr->p_type) { + case PT_LOAD: + if (nsegs == 2) { + warnx("%s: too many sections", + filename); + break; + } + ef->ef_segs[nsegs++] = phdr; + break; + case PT_PHDR: + phphdr = phdr; + break; + case PT_DYNAMIC: + phdyn = phdr; + break; + } + phdr++; + } + if (verbose > 1) + printf("\n"); + ef->ef_nsegs = nsegs; + if (phdyn == NULL) { + warnx("file isn't dynamically-linked"); + break; + } + if (ef_read_entry(ef, phdyn->p_offset, + phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) { + printf("ef_read_entry failed\n"); + break; + } + error = ef_parse_dynamic(ef); + if (error) + break; + if (hdr->e_type == ET_DYN) { + ef->ef_type = EFT_KLD; +/* pad = (u_int)dest & PAGE_MASK; + if (pad) + dest += PAGE_SIZE - pad;*/ + error = 0; + } else if (hdr->e_type == ET_EXEC) { +/* dest = hdr->e_entry; + if (dest == 0) + break;*/ + ef->ef_type = EFT_KERNEL; + error = 0; + } else + break; + } while(0); + if (error) { + ef_close(ef); + if (ef->ef_verbose) + warnc(error, "elf_open(%s)", filename); + } + return error; +} + +int +ef_close(elf_file_t ef) +{ + close(ef->ef_fd); +/* if (ef->ef_fpage) + free(ef->ef_fpage);*/ + if (ef->ef_name) + free(ef->ef_name); + return 0; +} |