diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index ee862f650329..b95adf82fe9b 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -1,98 +1,98 @@ # Use the following command to build local debug version of dynamic # linker: # make DEBUG_FLAGS=-g WITHOUT_TESTS=yes all RTLD_ELF_DIR:= ${.PARSEDIR} .include PACKAGE= clibs MK_PIE= no # Always position independent using local rules MK_SSP= no .if !defined(NEED_COMPAT) CONFS= libmap.conf .endif PROG?= ld-elf.so.1 .if (${PROG:M*ld-elf32*} != "") TAGS+= lib32 .endif SRCS= \ crtbrand.S \ rtld_start.S \ reloc.c \ rtld.c \ rtld_lock.c \ rtld_malloc.c \ rtld_printf.c \ map_object.c \ xmalloc.c \ debug.c \ libmap.c MAN?= rtld.1 ACFLAGS+= -DLOCORE -CFLAGS+= -Wall -DFREEBSD_ELF -DIN_RTLD -ffreestanding +CFLAGS+= -Wall -DIN_RTLD -ffreestanding CFLAGS+= -I${SRCTOP}/lib/csu/common .if exists(${RTLD_ELF_DIR}/${MACHINE_ARCH:S/powerpc64le/powerpc64/}) RTLD_ARCH= ${MACHINE_ARCH:S/powerpc64le/powerpc64/} .else RTLD_ARCH= ${MACHINE_CPUARCH} .endif CFLAGS+= -I${RTLD_ELF_DIR}/${RTLD_ARCH} -I${RTLD_ELF_DIR} NO_WCAST_ALIGN= yes INSTALLFLAGS= -C -b PRECIOUSPROG= BINDIR= /libexec SYMLINKS= ../..${BINDIR}/${PROG} ${LIBEXECDIR}/${PROG} MLINKS?= rtld.1 ld-elf.so.1.1 \ rtld.1 ld.so.1 CFLAGS+= -fpic -DPIC $(DEBUG) LDFLAGS+= -shared -Wl,-Bsymbolic -Wl,-z,defs -nostdlib -e ${RTLD_ENTRY} # Pull in the dependencies that we use from libc .include "rtld-libc/Makefile.inc" .if ${MK_TOOLCHAIN} == "no" LDFLAGS+= -L${LIBCDIR} .endif VERSION_DEF= ${LIBCSRCDIR}/Versions.def SYMBOL_MAPS= ${RTLD_ELF_DIR}/Symbol.map VERSION_MAP= Version.map LDFLAGS+= -Wl,--version-script=${VERSION_MAP} .if exists(${RTLD_ELF_DIR}/${RTLD_ARCH}/Symbol.map) SYMBOL_MAPS+= ${RTLD_ELF_DIR}/${RTLD_ARCH}/Symbol.map .endif .sinclude "${RTLD_ELF_DIR}/${RTLD_ARCH}/Makefile.inc" RTLD_ENTRY?= .rtld_start # Since moving rtld-elf to /libexec, we need to create a symlink. # Fixup the existing binary that's there so we can symlink over it. beforeinstall: .if exists(${DESTDIR}/usr/libexec/${PROG}) && ${MK_STAGING} == "no" -chflags -h noschg ${DESTDIR}/usr/libexec/${PROG} .endif .PATH: ${RTLD_ELF_DIR}/${RTLD_ARCH} ${SRCTOP}/lib/csu/common .if ${.CURDIR} == ${RTLD_ELF_DIR} HAS_TESTS= SUBDIR.${MK_TESTS}+= tests .endif # Some of the required math functions (div & mod) are implemented in # libcompiler_rt on some architectures. LIBADD+= compiler_rt .include ${PROG_FULL}: ${VERSION_MAP} .include .if ${COMPILER_TYPE} == "gcc" # GCC warns about redeclarations even though they have __exported # and are therefore not identical to the ones from the system headers. CFLAGS+= -Wno-redundant-decls .endif diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c index 9f3b015fef7e..bd9ab9e7ec5b 100644 --- a/stand/common/load_elf.c +++ b/stand/common/load_elf.c @@ -1,1325 +1,1324 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * 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. * * 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. */ #include #include #include #include #include #include #include #include #include #include -#define FREEBSD_ELF #include #include "bootstrap.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) #if defined(__i386__) && __ELF_WORD_SIZE == 64 #undef ELF_TARG_CLASS #undef ELF_TARG_MACH #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_MACH EM_X86_64 #endif typedef struct elf_file { Elf_Phdr *ph; Elf_Ehdr *ehdr; Elf_Sym *symtab; Elf_Hashelt *hashtab; Elf_Hashelt nbuckets; Elf_Hashelt nchains; Elf_Hashelt *buckets; Elf_Hashelt *chains; Elf_Rel *rel; size_t relsz; Elf_Rela *rela; size_t relasz; char *strtab; size_t strsz; int fd; caddr_t firstpage; size_t firstlen; int kernel; uint64_t off; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; #endif } *elf_file_t; #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(ef) (ef)->vctx #else #define VECTX_HANDLE(ef) (ef)->fd #endif static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym *sym, unsigned char type); static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end); static symaddr_fn __elfN(symaddr); static char *fake_modname(const char *name); const char *__elfN(kerneltype) = "elf kernel"; const char *__elfN(moduletype) = "elf module"; uint64_t __elfN(relocation_offset) = 0; #ifdef __powerpc__ extern void elf_wrong_field_size(void); #define CONVERT_FIELD(b, f, e) \ switch (sizeof((b)->f)) { \ case 2: \ (b)->f = e ## 16toh((b)->f); \ break; \ case 4: \ (b)->f = e ## 32toh((b)->f); \ break; \ case 8: \ (b)->f = e ## 64toh((b)->f); \ break; \ default: \ /* Force a link time error. */ \ elf_wrong_field_size(); \ break; \ } #define CONVERT_SWITCH(h, d, f) \ switch ((h)->e_ident[EI_DATA]) { \ case ELFDATA2MSB: \ f(d, be); \ break; \ case ELFDATA2LSB: \ f(d, le); \ break; \ default: \ return (EINVAL); \ } static int elf_header_convert(Elf_Ehdr *ehdr) { /* * Fixup ELF header endianness. * * The Xhdr structure was loaded using block read call to optimize file * accesses. It might happen, that the endianness of the system memory * is different that endianness of the ELF header. Swap fields here to * guarantee that Xhdr always contain valid data regardless of * architecture. */ #define HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, e_type, e); \ CONVERT_FIELD(b, e_machine, e); \ CONVERT_FIELD(b, e_version, e); \ CONVERT_FIELD(b, e_entry, e); \ CONVERT_FIELD(b, e_phoff, e); \ CONVERT_FIELD(b, e_shoff, e); \ CONVERT_FIELD(b, e_flags, e); \ CONVERT_FIELD(b, e_ehsize, e); \ CONVERT_FIELD(b, e_phentsize, e); \ CONVERT_FIELD(b, e_phnum, e); \ CONVERT_FIELD(b, e_shentsize, e); \ CONVERT_FIELD(b, e_shnum, e); \ CONVERT_FIELD(b, e_shstrndx, e) CONVERT_SWITCH(ehdr, ehdr, HEADER_FIELDS); #undef HEADER_FIELDS return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { #define PROGRAM_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, p_type, e); \ CONVERT_FIELD(b, p_flags, e); \ CONVERT_FIELD(b, p_offset, e); \ CONVERT_FIELD(b, p_vaddr, e); \ CONVERT_FIELD(b, p_paddr, e); \ CONVERT_FIELD(b, p_filesz, e); \ CONVERT_FIELD(b, p_memsz, e); \ CONVERT_FIELD(b, p_align, e) CONVERT_SWITCH(ehdr, phdr, PROGRAM_HEADER_FIELDS); #undef PROGRAM_HEADER_FIELDS return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { #define SECTION_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, sh_name, e); \ CONVERT_FIELD(b, sh_type, e); \ CONVERT_FIELD(b, sh_link, e); \ CONVERT_FIELD(b, sh_info, e); \ CONVERT_FIELD(b, sh_flags, e); \ CONVERT_FIELD(b, sh_addr, e); \ CONVERT_FIELD(b, sh_offset, e); \ CONVERT_FIELD(b, sh_size, e); \ CONVERT_FIELD(b, sh_addralign, e); \ CONVERT_FIELD(b, sh_entsize, e) CONVERT_SWITCH(ehdr, shdr, SECTION_HEADER_FIELDS); #undef SECTION_HEADER_FIELDS return (0); } #undef CONVERT_SWITCH #undef CONVERT_FIELD #else static int elf_header_convert(Elf_Ehdr *ehdr) { return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { return (0); } #endif #ifdef __amd64__ static bool is_kernphys_relocatable(elf_file_t ef) { Elf_Sym sym; return (__elfN(lookup_symbol)(ef, "kernphys", &sym, STT_OBJECT) == 0); } #endif #ifdef __i386__ static bool is_tg_kernel_support(struct preloaded_file *fp, elf_file_t ef) { Elf_Sym sym; Elf_Addr p_start, p_end, v, p; char vd_name[16]; int error; if (__elfN(lookup_symbol)(ef, "__start_set_vt_drv_set", &sym, STT_NOTYPE) != 0) return (false); p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_vt_drv_set", &sym, STT_NOTYPE) != 0) return (false); p_end = sym.st_value + ef->off; /* * Walk through vt_drv_set, each vt driver structure starts with * static 16 chars for driver name. If we have "vbefb", return true. */ for (p = p_start; p < p_end; p += sizeof(Elf_Addr)) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) v += ef->off; else if (error != 0) return (false); COPYOUT(v, &vd_name, sizeof(vd_name)); if (strncmp(vd_name, "vbefb", sizeof(vd_name)) == 0) return (true); } return (false); } #endif static int __elfN(load_elf_header)(char *filename, elf_file_t ef) { ssize_t bytes_read; Elf_Ehdr *ehdr; int err; /* * Open the image, read and validate the ELF header */ if (filename == NULL) /* can't handle nameless */ return (EFTYPE); if ((ef->fd = open(filename, O_RDONLY)) == -1) return (errno); ef->firstpage = malloc(PAGE_SIZE); if (ef->firstpage == NULL) { close(ef->fd); return (ENOMEM); } preload(ef->fd); #ifdef LOADER_VERIEXEC_VECTX { int verror; ef->vctx = vectx_open(ef->fd, filename, 0L, NULL, &verror, __func__); if (verror) { printf("Unverified %s: %s\n", filename, ve_error_get()); close(ef->fd); free(ef->vctx); return (EAUTH); } } #endif bytes_read = VECTX_READ(VECTX_HANDLE(ef), ef->firstpage, PAGE_SIZE); ef->firstlen = (size_t)bytes_read; if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto error; } ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; /* Is it ELF? */ if (!IS_ELF(*ehdr)) { err = EFTYPE; goto error; } if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || ehdr->e_ident[EI_VERSION] != EV_CURRENT) /* Version ? */ { err = EFTYPE; goto error; } err = elf_header_convert(ehdr); if (err) goto error; if (ehdr->e_version != EV_CURRENT || ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ err = EFTYPE; goto error; } #if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) if (verify_file(ef->fd, filename, bytes_read, VE_MUST, __func__) < 0) { err = EAUTH; goto error; } #endif return (0); error: if (ef->firstpage != NULL) { free(ef->firstpage); ef->firstpage = NULL; } if (ef->fd != -1) { #ifdef LOADER_VERIEXEC_VECTX free(ef->vctx); #endif close(ef->fd); ef->fd = -1; } return (err); } /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { return (__elfN(loadfile_raw)(filename, dest, result, 0)); } int __elfN(loadfile_raw)(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *ehdr; int err; fp = NULL; bzero(&ef, sizeof(struct elf_file)); ef.fd = -1; err = __elfN(load_elf_header)(filename, &ef); if (err != 0) return (err); ehdr = ef.ehdr; /* * Check to see what sort of module we are. */ kfp = file_findfile(NULL, __elfN(kerneltype)); #ifdef __powerpc__ /* * Kernels can be ET_DYN, so just assume the first loaded object is the * kernel. This assumption will be checked later. */ if (kfp == NULL) ef.kernel = 1; #endif if (ef.kernel || ehdr->e_type == ET_EXEC) { /* Looks like a kernel */ if (kfp != NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n"); err = EPERM; goto oerr; } /* * Calculate destination address based on kernel entrypoint. * * For ARM, the destination address is independent of any values * in the elf header (an ARM kernel can be loaded at any 2MB * boundary), so we leave dest set to the value calculated by * archsw.arch_loadaddr() and passed in to this function. */ #ifndef __arm__ if (ehdr->e_type == ET_EXEC) dest = (ehdr->e_entry & ~PAGE_MASK); #endif if ((ehdr->e_entry & ~PAGE_MASK) == 0) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); err = EPERM; goto oerr; } ef.kernel = 1; } else if (ehdr->e_type == ET_DYN) { /* Looks like a kld module */ if (multiboot != 0) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module as multiboot\n"); err = EPERM; goto oerr; } if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); err = EPERM; goto oerr; } if (strcmp(__elfN(kerneltype), kfp->f_type)) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); err = EPERM; goto oerr; } /* Looks OK, got ahead */ ef.kernel = 0; } else { err = EFTYPE; goto oerr; } if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_ELF, ehdr, dest); else dest = roundup(dest, PAGE_SIZE); /* * Ok, we think we should handle this. */ fp = file_alloc(); if (fp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: cannot allocate module info\n"); err = EPERM; goto out; } if (ef.kernel == 1 && multiboot == 0) setenv("kernelname", filename, 1); fp->f_name = strdup(filename); if (multiboot == 0) fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); else fp->f_type = strdup("elf multiboot kernel"); if (module_verbose >= MODULE_VERBOSE_FULL) { if (ef.kernel) printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry); } else if (module_verbose > MODULE_VERBOSE_SILENT) printf("%s ", filename); fp->f_size = __elfN(loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; #ifdef __amd64__ fp->f_kernphys_relocatable = multiboot || is_kernphys_relocatable(&ef); #endif #ifdef __i386__ fp->f_tg_kernel_support = is_tg_kernel_support(fp, &ef); #endif goto out; ioerr: err = EIO; oerr: file_discard(fp); out: if (ef.firstpage) free(ef.firstpage); if (ef.fd != -1) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif close(ef.fd); } return (err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off) { int i; u_int j; Elf_Ehdr *ehdr; Elf_Phdr *phdr, *php; Elf_Shdr *shdr; char *shstr; int ret; vm_offset_t firstaddr; vm_offset_t lastaddr; size_t chunk; ssize_t result; Elf_Addr ssym, esym; Elf_Dyn *dp; Elf_Addr adp; Elf_Addr ctors; int ndp; int symstrindex; int symtabindex; Elf_Size size; u_int fpcopy; Elf_Sym sym; Elf_Addr p_start, p_end; dp = NULL; shdr = NULL; ret = 0; firstaddr = lastaddr = 0; ehdr = ef->ehdr; #ifdef __powerpc__ if (ef->kernel) { #else if (ehdr->e_type == ET_EXEC) { #endif #if defined(__i386__) || defined(__amd64__) #if __ELF_WORD_SIZE == 64 /* x86_64 relocates after locore */ off = - (off & 0xffffffffff000000ull); #else /* i386 relocates after locore */ off = - (off & 0xff000000u); #endif #elif defined(__powerpc__) /* * On the purely virtual memory machines like e500, the kernel * is linked against its final VA range, which is most often * not available at the loader stage, but only after kernel * initializes and completes its VM settings. In such cases we * cannot use p_vaddr field directly to load ELF segments, but * put them at some 'load-time' locations. */ if (off & 0xf0000000u) { off = -(off & 0xf0000000u); /* * XXX the physical load address should not be * hardcoded. Note that the Book-E kernel assumes that * it's loaded at a 16MB boundary for now... */ off += 0x01000000; } ehdr->e_entry += off; if (module_verbose >= MODULE_VERBOSE_FULL) printf("Converted entry 0x%jx\n", (uintmax_t)ehdr->e_entry); #elif defined(__arm__) && !defined(EFI) /* * The elf headers in arm kernels specify virtual addresses in * all header fields, even the ones that should be physical * addresses. We assume the entry point is in the first page, * and masking the page offset will leave us with the virtual * address the kernel was linked at. We subtract that from the * load offset, making 'off' into the value which, when added * to a virtual address in an elf header, translates it to a * physical address. We do the va->pa conversion on the entry * point address in the header now, so that later we can launch * the kernel by just jumping to that address. * * When booting from UEFI the copyin and copyout functions * handle adjusting the location relative to the first virtual * address. Because of this there is no need to adjust the * offset or entry point address as these will both be handled * by the efi code. */ off -= ehdr->e_entry & ~PAGE_MASK; ehdr->e_entry += off; if (module_verbose >= MODULE_VERBOSE_FULL) printf("ehdr->e_entry 0x%jx, va<->pa off %llx\n", (uintmax_t)ehdr->e_entry, off); #else off = 0; /* other archs use direct mapped kernels */ #endif } ef->off = off; if (ef->kernel) __elfN(relocation_offset) = off; if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n"); goto out; } phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { if (elf_program_header_convert(ehdr, phdr)) continue; /* We want to load PT_LOAD segments only.. */ if (phdr[i].p_type != PT_LOAD) continue; if (module_verbose >= MODULE_VERBOSE_FULL) { printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", (long)phdr[i].p_filesz, (long)phdr[i].p_offset, (long)(phdr[i].p_vaddr + off), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); } else if (module_verbose > MODULE_VERBOSE_SILENT) { if ((phdr[i].p_flags & PF_W) == 0) { printf("text=0x%lx ", (long)phdr[i].p_filesz); } else { printf("data=0x%lx", (long)phdr[i].p_filesz); if (phdr[i].p_filesz < phdr[i].p_memsz) printf("+0x%lx", (long)(phdr[i].p_memsz - phdr[i].p_filesz)); printf(" "); } } fpcopy = 0; if (ef->firstlen > phdr[i].p_offset) { fpcopy = ef->firstlen - phdr[i].p_offset; archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, phdr[i].p_vaddr + off, fpcopy); } if (phdr[i].p_filesz > fpcopy) { if (kern_pread(VECTX_HANDLE(ef), phdr[i].p_vaddr + off + fpcopy, phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: read failed\n"); goto out; } } /* clear space from oversized segments; eg: bss */ if (phdr[i].p_filesz < phdr[i].p_memsz) { if (module_verbose >= MODULE_VERBOSE_FULL) { printf(" (bss: 0x%lx-0x%lx)", (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz -1)); } kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, phdr[i].p_memsz - phdr[i].p_filesz); } if (module_verbose >= MODULE_VERBOSE_FULL) printf("\n"); if (archsw.arch_loadseg != NULL) archsw.arch_loadseg(ehdr, phdr + i, off); if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) firstaddr = phdr[i].p_vaddr + off; if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; } lastaddr = roundup(lastaddr, sizeof(long)); /* * Get the section headers. We need this for finding the .ctors * section as well as for loading any symbols. Both may be hard * to do if reading from a .gz file as it involves seeking. I * think the rule is going to have to be that you must strip a * file to remove symbols before gzipping it. */ chunk = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize; if (chunk == 0 || ehdr->e_shoff == 0) goto nosyms; shdr = alloc_pread(VECTX_HANDLE(ef), ehdr->e_shoff, chunk); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: failed to read section headers"); goto nosyms; } for (i = 0; i < ehdr->e_shnum; i++) elf_section_header_convert(ehdr, &shdr[i]); file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr); /* * Read the section string table and look for the .ctors section. * We need to tell the kernel where it is so that it can call the * ctors. */ chunk = shdr[ehdr->e_shstrndx].sh_size; if (chunk) { shstr = alloc_pread(VECTX_HANDLE(ef), shdr[ehdr->e_shstrndx].sh_offset, chunk); if (shstr) { for (i = 0; i < ehdr->e_shnum; i++) { if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0) continue; ctors = shdr[i].sh_addr; file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors), &ctors); size = shdr[i].sh_size; file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size), &size); break; } free(shstr); } } /* * Now load any symbols. */ symtabindex = -1; symstrindex = -1; for (i = 0; i < ehdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_SYMTAB) continue; for (j = 0; j < ehdr->e_phnum; j++) { if (phdr[j].p_type != PT_LOAD) continue; if (shdr[i].sh_offset >= phdr[j].p_offset && (shdr[i].sh_offset + shdr[i].sh_size <= phdr[j].p_offset + phdr[j].p_filesz)) { shdr[i].sh_offset = 0; shdr[i].sh_size = 0; break; } } if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) continue; /* alread loaded in a PT_LOAD above */ /* Save it for loading below */ symtabindex = i; symstrindex = shdr[i].sh_link; } if (symtabindex < 0 || symstrindex < 0) goto nosyms; /* Ok, committed to a load. */ if (module_verbose >= MODULE_VERBOSE_FULL) printf("syms=["); ssym = lastaddr; for (i = symtabindex; i >= 0; i = symstrindex) { char *secname; switch(shdr[i].sh_type) { case SHT_SYMTAB: /* Symbol table */ secname = "symtab"; break; case SHT_STRTAB: /* String table */ secname = "strtab"; break; default: secname = "WHOA!!"; break; } size = shdr[i].sh_size; archsw.arch_copyin(&size, lastaddr, sizeof(size)); lastaddr += sizeof(size); if (module_verbose >= MODULE_VERBOSE_FULL) { printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname, (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset, (uintmax_t)lastaddr, (uintmax_t)(lastaddr + shdr[i].sh_size)); } else if (module_verbose > MODULE_VERBOSE_SILENT) { if (i == symstrindex) printf("+"); printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); } if (VECTX_LSEEK(VECTX_HANDLE(ef), (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); lastaddr = ssym; ssym = 0; goto nosyms; } result = archsw.arch_readin(VECTX_HANDLE(ef), lastaddr, shdr[i].sh_size); if (result < 0 || (size_t)result != shdr[i].sh_size) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! " "(%ju != %ju)", (uintmax_t)result, (uintmax_t)shdr[i].sh_size); lastaddr = ssym; ssym = 0; goto nosyms; } /* Reset offsets relative to ssym */ lastaddr += shdr[i].sh_size; lastaddr = roundup(lastaddr, sizeof(size)); if (i == symtabindex) symtabindex = -1; else if (i == symstrindex) symstrindex = -1; } esym = lastaddr; if (module_verbose >= MODULE_VERBOSE_FULL) printf("]"); file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); nosyms: if (module_verbose > MODULE_VERBOSE_SILENT) printf("\n"); ret = lastaddr - firstaddr; fp->f_addr = firstaddr; php = NULL; for (i = 0; i < ehdr->e_phnum; i++) { if (phdr[i].p_type == PT_DYNAMIC) { php = phdr + i; adp = php->p_vaddr; file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp); break; } } if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ goto out; ndp = php->p_filesz / sizeof(Elf_Dyn); if (ndp == 0) goto out; dp = malloc(php->p_filesz); if (dp == NULL) goto out; archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); ef->strsz = 0; for (i = 0; i < ndp; i++) { if (dp[i].d_tag == 0) break; switch (dp[i].d_tag) { case DT_HASH: ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRTAB: ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRSZ: ef->strsz = dp[i].d_un.d_val; break; case DT_SYMTAB: ef->symtab = (Elf_Sym *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_REL: ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELSZ: ef->relsz = dp[i].d_un.d_val; break; case DT_RELA: ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELASZ: ef->relasz = dp[i].d_un.d_val; break; default: break; } } if (ef->hashtab == NULL || ef->symtab == NULL || ef->strtab == NULL || ef->strsz == 0) goto out; COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); ef->buckets = ef->hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; if (__elfN(lookup_symbol)(ef, "__start_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; p_end = sym.st_value + ef->off; if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0) goto out; if (ef->kernel) /* kernel must not depend on anything */ goto out; out: if (dp) free(dp); if (shdr) free(shdr); return ret; } static char invalid_name[] = "bad"; char * fake_modname(const char *name) { const char *sp, *ep; char *fp; size_t len; sp = strrchr(name, '/'); if (sp) sp++; else sp = name; ep = strrchr(sp, '.'); if (ep == NULL) { ep = sp + strlen(sp); } if (ep == sp) { sp = invalid_name; ep = invalid_name + sizeof(invalid_name) - 1; } len = ep - sp; fp = malloc(len + 1); if (fp == NULL) return NULL; memcpy(fp, sp, len); fp[len] = '\0'; return fp; } #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint64_t md_data; /* specific data */ uint64_t md_cval; /* common string label */ }; #endif #if defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint32_t md_data; /* specific data */ uint32_t md_cval; /* common string label */ }; #endif int __elfN(load_modmetadata)(struct preloaded_file *fp, uint64_t dest) { struct elf_file ef; int err, i, j; Elf_Shdr *sh_meta, *shdr = NULL; Elf_Shdr *sh_data[2]; char *shstrtab = NULL; size_t size; Elf_Addr p_start, p_end; bzero(&ef, sizeof(struct elf_file)); ef.fd = -1; err = __elfN(load_elf_header)(fp->f_name, &ef); if (err != 0) goto out; if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) { ef.kernel = 1; } else if (ef.ehdr->e_type != ET_DYN) { err = EFTYPE; goto out; } size = (size_t)ef.ehdr->e_shnum * (size_t)ef.ehdr->e_shentsize; shdr = alloc_pread(VECTX_HANDLE(&ef), ef.ehdr->e_shoff, size); if (shdr == NULL) { err = ENOMEM; goto out; } /* Load shstrtab. */ shstrtab = alloc_pread(VECTX_HANDLE(&ef), shdr[ef.ehdr->e_shstrndx].sh_offset, shdr[ef.ehdr->e_shstrndx].sh_size); if (shstrtab == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load shstrtab\n"); err = EFTYPE; goto out; } /* Find set_modmetadata_set and data sections. */ sh_data[0] = sh_data[1] = sh_meta = NULL; for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { if (strcmp(&shstrtab[shdr[i].sh_name], "set_modmetadata_set") == 0) { sh_meta = &shdr[i]; } if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { sh_data[j++] = &shdr[i]; } } if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); err = EFTYPE; goto out; } /* Load set_modmetadata_set into memory */ err = kern_pread(VECTX_HANDLE(&ef), dest, sh_meta->sh_size, sh_meta->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); goto out; } p_start = dest; p_end = dest + sh_meta->sh_size; dest += sh_meta->sh_size; /* Load data sections into memory. */ err = kern_pread(VECTX_HANDLE(&ef), dest, sh_data[0]->sh_size, sh_data[0]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } /* * We have to increment the dest, so that the offset is the same into * both the .rodata and .data sections. */ ef.off = -(sh_data[0]->sh_addr - dest); dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); err = kern_pread(VECTX_HANDLE(&ef), dest, sh_data[1]->sh_size, sh_data[1]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to parse metadata: %d\n", err); goto out; } out: if (shstrtab != NULL) free(shstrtab); if (shdr != NULL) free(shdr); if (ef.firstpage != NULL) free(ef.firstpage); if (ef.fd != -1) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif close(ef.fd); } return (err); } int __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end) { struct mod_metadata md; #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 md32; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p; modcnt = 0; p = p_start; while (p < p_end) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) v += ef->off; else if (error != 0) return (error); #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error == EOPNOTSUPP) { md64.md_cval += ef->off; md64.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 COPYOUT(v, &md32, sizeof(md32)); error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof(md32)); if (error == EOPNOTSUPP) { md32.md_cval += ef->off; md32.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md32.md_version; md.md_type = md32.md_type; md.md_cval = (const char *)(uintptr_t)md32.md_cval; md.md_data = (void *)(uintptr_t)md32.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error == EOPNOTSUPP) { md.md_cval += ef->off; md.md_data = (void *)((uintptr_t)md.md_data + (uintptr_t)ef->off); } else if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: if (ef->kernel) /* kernel must not depend on anything */ break; s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; } } if (modcnt == 0) { s = fake_modname(fp->f_name); file_addmodule(fp, s, 1, NULL); free(s); } return 0; } 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; } static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; int __elfN(lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym *symp, unsigned char type) { Elf_Hashelt symnum; Elf_Sym sym; char *strp; unsigned long hash; if (ef->nbuckets == 0) { printf(__elfN(bad_symtable)); return ENOENT; } hash = elf_hash(name); COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); while (symnum != STN_UNDEF) { if (symnum >= ef->nchains) { printf(__elfN(bad_symtable)); return ENOENT; } COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); if (sym.st_name == 0) { printf(__elfN(bad_symtable)); return ENOENT; } strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); if (strcmp(name, strp) == 0) { free(strp); if (sym.st_shndx != SHN_UNDEF && sym.st_value != 0 && ELF_ST_TYPE(sym.st_info) == type) { *symp = sym; return 0; } return ENOENT; } free(strp); COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); } return ENOENT; } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. * * Returns EOPNOTSUPP if no relocation method is supplied. */ static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { size_t n; Elf_Rela a; Elf_Rel r; int error; /* * The kernel is already relocated, but we still want to apply * offset adjustments. */ if (ef->kernel) return (EOPNOTSUPP); for (n = 0; n < ef->relsz / sizeof(r); n++) { COPYOUT(ef->rel + n, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL, ef->off, p, val, len); if (error != 0) return (error); } for (n = 0; n < ef->relasz / sizeof(a); n++) { COPYOUT(ef->rela + n, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA, ef->off, p, val, len); if (error != 0) return (error); } return (0); } static Elf_Addr __elfN(symaddr)(struct elf_file *ef, Elf_Size symidx) { /* Symbol lookup by index not required here. */ return (0); } diff --git a/stand/common/load_elf_obj.c b/stand/common/load_elf_obj.c index 86e0fb2b5d22..6906e08f5f36 100644 --- a/stand/common/load_elf_obj.c +++ b/stand/common/load_elf_obj.c @@ -1,582 +1,581 @@ /*- * Copyright (c) 2004 Ian Dowse * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * 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. * * 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. */ #include #include #include #include #include #include #include #include #include -#define FREEBSD_ELF #include #include "bootstrap.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) #if defined(__i386__) && __ELF_WORD_SIZE == 64 #undef ELF_TARG_CLASS #undef ELF_TARG_MACH #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_MACH EM_X86_64 #endif typedef struct elf_file { Elf_Ehdr hdr; Elf_Shdr *e_shdr; int symtabindex; /* Index of symbol table */ int shstrindex; /* Index of section name string table */ int fd; vm_offset_t off; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; #endif } *elf_file_t; #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(ef) (ef)->vctx #else #define VECTX_HANDLE(ef) (ef)->fd #endif static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(obj_lookup_set)(struct preloaded_file *mp, elf_file_t ef, const char *name, Elf_Addr *startp, Elf_Addr *stopp, int *countp); static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(obj_parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx); const char *__elfN(obj_kerneltype) = "elf kernel"; const char *__elfN(obj_moduletype) = "elf obj module"; /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(obj_loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *hdr; int err; ssize_t bytes_read; fp = NULL; bzero(&ef, sizeof(struct elf_file)); /* * Open the image, read and validate the ELF header */ if (filename == NULL) /* can't handle nameless */ return(EFTYPE); if ((ef.fd = open(filename, O_RDONLY)) == -1) return(errno); #ifdef LOADER_VERIEXEC_VECTX { int verror; ef.vctx = vectx_open(ef.fd, filename, 0L, NULL, &verror, __func__); if (verror) { printf("Unverified %s: %s\n", filename, ve_error_get()); close(ef.fd); free(ef.vctx); return (EAUTH); } } #endif hdr = &ef.hdr; bytes_read = VECTX_READ(VECTX_HANDLE(&ef), hdr, sizeof(*hdr)); if (bytes_read != sizeof(*hdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto oerr; } /* Is it ELF? */ if (!IS_ELF(*hdr)) { err = EFTYPE; goto oerr; } if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ hdr->e_ident[EI_DATA] != ELF_TARG_DATA || hdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH || /* Machine ? */ hdr->e_type != ET_REL) { err = EFTYPE; goto oerr; } if (hdr->e_shnum * hdr->e_shentsize == 0 || hdr->e_shoff == 0 || hdr->e_shentsize != sizeof(Elf_Shdr)) { err = EFTYPE; goto oerr; } #if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) if (verify_file(ef.fd, filename, bytes_read, VE_MUST, __func__) < 0) { err = EAUTH; goto oerr; } #endif kfp = file_findfile(NULL, __elfN(obj_kerneltype)); if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: can't load module before kernel\n"); err = EPERM; goto oerr; } if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); else dest = roundup(dest, PAGE_SIZE); /* * Ok, we think we should handle this. */ fp = file_alloc(); if (fp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: cannot allocate module info\n"); err = EPERM; goto out; } fp->f_name = strdup(filename); fp->f_type = strdup(__elfN(obj_moduletype)); if (module_verbose > MODULE_VERBOSE_SILENT) printf("%s ", filename); fp->f_size = __elfN(obj_loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*hdr), hdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; goto out; ioerr: err = EIO; oerr: file_discard(fp); out: #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif close(ef.fd); if (ef.e_shdr != NULL) free(ef.e_shdr); return(err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off) { Elf_Ehdr *hdr; Elf_Shdr *shdr, *cshdr, *lshdr; vm_offset_t firstaddr, lastaddr; int i, nsym, res, ret, shdrbytes, symstrindex; ret = 0; firstaddr = lastaddr = (vm_offset_t)off; hdr = &ef->hdr; ef->off = (vm_offset_t)off; /* Read in the section headers. */ shdrbytes = hdr->e_shnum * hdr->e_shentsize; shdr = alloc_pread(VECTX_HANDLE(ef), (off_t)hdr->e_shoff, shdrbytes); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read section headers failed\n"); goto out; } ef->e_shdr = shdr; /* * Decide where to load everything, but don't read it yet. * We store the load address as a non-zero sh_addr value. * Start with the code/data and bss. */ for (i = 0; i < hdr->e_shnum; i++) shdr[i].sh_addr = 0; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_size == 0) continue; switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: #if defined(__i386__) || defined(__amd64__) case SHT_X86_64_UNWIND: #endif case SHT_INIT_ARRAY: case SHT_FINI_ARRAY: if ((shdr[i].sh_flags & SHF_ALLOC) == 0) break; lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Symbols. */ nsym = 0; for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_SYMTAB: nsym++; ef->symtabindex = i; break; } } if (nsym != 1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no valid symbol table\n"); goto out; } lastaddr = roundup(lastaddr, shdr[ef->symtabindex].sh_addralign); shdr[ef->symtabindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->symtabindex].sh_size; symstrindex = shdr[ef->symtabindex].sh_link; if (symstrindex < 0 || symstrindex >= hdr->e_shnum || shdr[symstrindex].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has invalid symbol strings\n"); goto out; } lastaddr = roundup(lastaddr, shdr[symstrindex].sh_addralign); shdr[symstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[symstrindex].sh_size; /* Section names. */ if (hdr->e_shstrndx == 0 || hdr->e_shstrndx >= hdr->e_shnum || shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no section names\n"); goto out; } ef->shstrindex = hdr->e_shstrndx; lastaddr = roundup(lastaddr, shdr[ef->shstrindex].sh_addralign); shdr[ef->shstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->shstrindex].sh_size; /* Relocation tables. */ for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_REL: case SHT_RELA: if ((shdr[shdr[i].sh_info].sh_flags & SHF_ALLOC) == 0) break; lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Clear the whole area, including bss regions. */ kern_bzero(firstaddr, lastaddr - firstaddr); /* Figure section with the lowest file offset we haven't loaded yet. */ for (cshdr = NULL; /* none */; /* none */) { /* * Find next section to load. The complexity of this loop is * O(n^2), but with the number of sections being typically * small, we do not care. */ lshdr = cshdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_addr == 0 || shdr[i].sh_type == SHT_NOBITS) continue; /* Skip sections that were loaded already. */ if (lshdr != NULL && lshdr->sh_offset >= shdr[i].sh_offset) continue; /* Find section with smallest offset. */ if (cshdr == lshdr || cshdr->sh_offset > shdr[i].sh_offset) cshdr = &shdr[i]; } if (cshdr == lshdr) break; if (kern_pread(VECTX_HANDLE(ef), (vm_offset_t)cshdr->sh_addr, cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read failed\n"); goto out; } } file_addmetadata(fp, MODINFOMD_SHDR, shdrbytes, shdr); res = __elfN(obj_parse_modmetadata)(fp, ef); if (res != 0) goto out; ret = lastaddr - firstaddr; fp->f_addr = firstaddr; if (module_verbose > MODULE_VERBOSE_SILENT) printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr); out: if (module_verbose > MODULE_VERBOSE_SILENT) printf("\n"); return ret; } #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint64_t md_data; /* specific data */ uint64_t md_cval; /* common string label */ }; #endif int __elfN(obj_parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) { struct mod_metadata md; #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p, p_stop; if (__elfN(obj_lookup_set)(fp, ef, "modmetadata_set", &p, &p_stop, &modcnt) != 0) return 0; modcnt = 0; while (p < p_stop) { COPYOUT(p, &v, sizeof(v)); error = __elfN(obj_reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error != 0) return (error); #if defined(__i386__) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; case MDT_MODULE: case MDT_PNP_INFO: break; default: printf("unknown type %d\n", md.md_type); break; } } return 0; } static int __elfN(obj_lookup_set)(struct preloaded_file *fp, elf_file_t ef, const char* name, Elf_Addr *startp, Elf_Addr *stopp, int *countp) { Elf_Ehdr *hdr; Elf_Shdr *shdr; char *p; vm_offset_t shstrtab; int i; hdr = &ef->hdr; shdr = ef->e_shdr; shstrtab = shdr[ef->shstrindex].sh_addr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_PROGBITS) continue; if (shdr[i].sh_name == 0) continue; p = strdupout(shstrtab + shdr[i].sh_name); if (strncmp(p, "set_", 4) == 0 && strcmp(p + 4, name) == 0) { *startp = shdr[i].sh_addr; *stopp = shdr[i].sh_addr + shdr[i].sh_size; *countp = (*stopp - *startp) / sizeof(Elf_Addr); free(p); return (0); } free(p); } return (ESRCH); } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. */ static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { Elf_Ehdr *hdr; Elf_Shdr *shdr; Elf_Addr off = p; Elf_Addr base; Elf_Rela a, *abase; Elf_Rel r, *rbase; int error, i, j, nrel, nrela; hdr = &ef->hdr; shdr = ef->e_shdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_RELA && shdr[i].sh_type != SHT_REL) continue; base = shdr[shdr[i].sh_info].sh_addr; if (base == 0 || shdr[i].sh_addr == 0) continue; if (off < base || off + len > base + shdr[shdr[i].sh_info].sh_size) continue; switch (shdr[i].sh_type) { case SHT_RELA: abase = (Elf_Rela *)(intptr_t)shdr[i].sh_addr; nrela = shdr[i].sh_size / sizeof(Elf_Rela); for (j = 0; j < nrela; j++) { COPYOUT(abase + j, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &a, ELF_RELOC_RELA, base, off, val, len); if (error != 0) return (error); } break; case SHT_REL: rbase = (Elf_Rel *)(intptr_t)shdr[i].sh_addr; nrel = shdr[i].sh_size / sizeof(Elf_Rel); for (j = 0; j < nrel; j++) { COPYOUT(rbase + j, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &r, ELF_RELOC_REL, base, off, val, len); if (error != 0) return (error); } break; } } return (0); } /* Look up the address of a specified symbol. */ static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx) { Elf_Sym sym; Elf_Addr base; if (symidx >= ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym)) return (0); COPYOUT(ef->e_shdr[ef->symtabindex].sh_addr + symidx * sizeof(Elf_Sym), &sym, sizeof(sym)); if (sym.st_shndx == SHN_UNDEF || sym.st_shndx >= ef->hdr.e_shnum) return (0); base = ef->e_shdr[sym.st_shndx].sh_addr; if (base == 0) return (0); return (base + sym.st_value); } diff --git a/stand/common/reloc_elf.c b/stand/common/reloc_elf.c index 47435dd78f6c..02e030016e17 100644 --- a/stand/common/reloc_elf.c +++ b/stand/common/reloc_elf.c @@ -1,219 +1,218 @@ /*- * Copyright (c) 2003 Jake Burkholder. * Copyright 1996-1998 John D. Polstra. * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * 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. * * 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. */ #include #include #include #include -#define FREEBSD_ELF #include #include "bootstrap.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) /* * Apply a single intra-module relocation to the data. `relbase' is the * target relocation base for the section (i.e. it corresponds to where * r_offset == 0). `dataaddr' is the relocated address corresponding to * the start of the data, and `len' is the number of bytes. */ int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata, int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len) { #if (defined(__aarch64__) || defined(__amd64__) || defined(__i386__)) && \ __ELF_WORD_SIZE == 64 Elf64_Addr *where, val; Elf_Addr addend, addr; Elf_Size rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; switch (reltype) { case ELF_RELOC_REL: rel = (const Elf_Rel *)reldata; where = (Elf_Addr *)((char *)data + relbase + rel->r_offset - dataaddr); addend = 0; rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); addend = 0; break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)reldata; where = (Elf_Addr *)((char *)data + relbase + rela->r_offset - dataaddr); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: return (EINVAL); } if ((char *)where < (char *)data || (char *)where >= (char *)data + len) return (0); if (reltype == ELF_RELOC_REL) addend = *where; #if defined(__aarch64__) #define RELOC_RELATIVE R_AARCH64_RELATIVE #define RELOC_IRELATIVE R_AARCH64_IRELATIVE #elif defined(__amd64__) || defined(__i386__) /* XXX, definitions not available on i386. */ #define R_X86_64_64 1 #define R_X86_64_RELATIVE 8 #define R_X86_64_IRELATIVE 37 #define RELOC_RELATIVE R_X86_64_RELATIVE #define RELOC_IRELATIVE R_X86_64_IRELATIVE #endif switch (rtype) { case RELOC_RELATIVE: addr = (Elf_Addr)addend + relbase; val = addr; memcpy(where, &val, sizeof(val)); break; case RELOC_IRELATIVE: /* leave it to kernel */ break; #if defined(__amd64__) || defined(__i386__) case R_X86_64_64: /* S + A */ addr = symaddr(ef, symidx); if (addr == 0) return (ESRCH); val = addr + addend; *where = val; break; #endif default: printf("\nunhandled relocation type %u\n", (u_int)rtype); return (EFTYPE); } return (0); #elif defined(__i386__) && __ELF_WORD_SIZE == 32 Elf_Addr addend, addr, *where, val; Elf_Size rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; switch (reltype) { case ELF_RELOC_REL: rel = (const Elf_Rel *)reldata; where = (Elf_Addr *)((char *)data + relbase + rel->r_offset - dataaddr); addend = 0; rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); addend = 0; break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)reldata; where = (Elf_Addr *)((char *)data + relbase + rela->r_offset - dataaddr); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: return (EINVAL); } if ((char *)where < (char *)data || (char *)where >= (char *)data + len) return (0); if (reltype == ELF_RELOC_REL) addend = *where; /* XXX, definitions not available on amd64. */ #define R_386_32 1 /* Add symbol value. */ #define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */ #define R_386_RELATIVE 8 /* Add load address of shared object. */ #define R_386_IRELATIVE 42 switch (rtype) { case R_386_RELATIVE: addr = addend + relbase; *where = addr; break; case R_386_32: /* S + A */ addr = symaddr(ef, symidx); if (addr == 0) return (ESRCH); val = addr + addend; *where = val; break; case R_386_IRELATIVE: /* leave it to kernel */ break; default: printf("\nunhandled relocation type %u\n", (u_int)rtype); return (EFTYPE); } return (0); #elif defined(__powerpc__) || defined(__riscv) Elf_Size w; const Elf_Rela *rela; switch (reltype) { case ELF_RELOC_RELA: rela = reldata; if (relbase + rela->r_offset >= dataaddr && relbase + rela->r_offset < dataaddr + len) { switch (ELF_R_TYPE(rela->r_info)) { #if defined(__powerpc__) case R_PPC_RELATIVE: #elif defined(__riscv) case R_RISCV_RELATIVE: #endif w = relbase + rela->r_addend; bcopy(&w, (u_char *)data + (relbase + rela->r_offset - dataaddr), sizeof(w)); break; default: printf("\nunhandled relocation type %u\n", (u_int)ELF_R_TYPE(rela->r_info)); return (EFTYPE); } } break; } return (0); #else return (EOPNOTSUPP); #endif } diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c index 5d052cc90a6c..72e023e30783 100644 --- a/usr.sbin/kldxref/ef.c +++ b/usr.sbin/kldxref/ef.c @@ -1,673 +1,672 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * 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. */ #include #include #include #include #include #include #include #include #include #include -#define FREEBSD_ELF #include "ef.h" #define MAXSEGS 16 struct ef_file { char *ef_name; struct elf_file *ef_efile; Elf_Phdr *ef_ph; int ef_fd; int ef_type; Elf_Ehdr ef_hdr; void *ef_fpage; /* First block of the file */ int ef_fplen; /* length of first block */ Elf_Dyn *ef_dyn; /* Symbol table etc. */ Elf_Hashelt ef_nbuckets; Elf_Hashelt ef_nchains; Elf_Hashelt *ef_buckets; Elf_Hashelt *ef_chains; Elf_Hashelt *ef_hashtab; Elf_Off ef_stroff; caddr_t ef_strtab; int ef_strsz; Elf_Off ef_symoff; Elf_Sym *ef_symtab; int ef_nsegs; Elf_Phdr *ef_segs[MAXSEGS]; int ef_verbose; Elf_Rel *ef_rel; /* relocation table */ int ef_relsz; /* number of entries */ Elf_Rela *ef_rela; /* relocation table */ int ef_relasz; /* number of entries */ }; static void ef_print_phdr(Elf_Phdr *); static Elf_Off ef_get_offset(elf_file_t, Elf_Off); static int ef_parse_dynamic(elf_file_t); static int ef_get_type(elf_file_t ef); static int ef_close(elf_file_t ef); static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest); static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr); static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest); static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest); static int ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest); static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr); static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void **ptr); static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx); static int ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, long *countp); static int ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym); static struct elf_file_ops ef_file_ops = { .get_type = ef_get_type, .close = ef_close, .read = ef_read, .read_entry = ef_read_entry, .seg_read = ef_seg_read, .seg_read_rel = ef_seg_read_rel, .seg_read_string = ef_seg_read_string, .seg_read_entry = ef_seg_read_entry, .seg_read_entry_rel = ef_seg_read_entry_rel, .symaddr = ef_symaddr, .lookup_set = ef_lookup_set, .lookup_symbol = ef_lookup_symbol }; static void ef_print_phdr(Elf_Phdr *phdr) { if ((phdr->p_flags & PF_W) == 0) { printf("text=0x%jx ", (uintmax_t)phdr->p_filesz); } else { printf("data=0x%jx", (uintmax_t)phdr->p_filesz); if (phdr->p_filesz < phdr->p_memsz) printf("+0x%jx", (uintmax_t)(phdr->p_memsz - phdr->p_filesz)); printf(" "); } } static Elf_Off 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); } static int ef_get_type(elf_file_t ef) { return (ef->ef_type); } /* * next three functions copied from link_elf.c */ static unsigned long elf_hash(const char *name) { unsigned long h, g; const unsigned char *p; h = 0; p = (const unsigned char *)name; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return (h); } static int ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym) { unsigned long hash, symnum; Elf_Sym *symp; char *strp; /* 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); } static int ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, long *countp) { Elf_Sym *sym; char *setsym; int error, len; len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ setsym = malloc(len); if (setsym == NULL) return (errno); /* get address of first entry */ snprintf(setsym, len, "%s%s", "__start_set_", name); error = ef_lookup_symbol(ef, setsym, &sym); if (error != 0) goto out; *startp = sym->st_value; /* get address of last entry */ snprintf(setsym, len, "%s%s", "__stop_set_", name); error = ef_lookup_symbol(ef, setsym, &sym); if (error != 0) goto out; *stopp = sym->st_value; /* and the number of entries */ *countp = (*stopp - *startp) / sizeof(void *); out: free(setsym); return (error); } static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx) { const Elf_Sym *sym; if (symidx >= ef->ef_nchains) return (0); sym = ef->ef_symtab + symidx; if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && sym->st_shndx != SHN_UNDEF && sym->st_value != 0) return (sym->st_value); return (0); } static int ef_parse_dynamic(elf_file_t ef) { Elf_Dyn *dp; Elf_Hashelt hashhdr[2]; int error; Elf_Off rel_off; Elf_Off rela_off; int rel_sz; int rela_sz; int rel_entry; int rela_entry; rel_off = rela_off = 0; rel_sz = rela_sz = 0; rel_entry = rela_entry = 0; 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 != 0) { warnx("can't read hash header (%jx)", (uintmax_t)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_Hashelt), (void **)&ef->ef_hashtab); if (error != 0) { 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; case DT_REL: if (rel_off != 0) warnx("second DT_REL entry ignored"); rel_off = dp->d_un.d_ptr; break; case DT_RELSZ: if (rel_sz != 0) warnx("second DT_RELSZ entry ignored"); rel_sz = dp->d_un.d_val; break; case DT_RELENT: if (rel_entry != 0) warnx("second DT_RELENT entry ignored"); rel_entry = dp->d_un.d_val; break; case DT_RELA: if (rela_off != 0) warnx("second DT_RELA entry ignored"); rela_off = dp->d_un.d_ptr; break; case DT_RELASZ: if (rela_sz != 0) warnx("second DT_RELASZ entry ignored"); rela_sz = dp->d_un.d_val; break; case DT_RELAENT: if (rela_entry != 0) warnx("second DT_RELAENT entry ignored"); rela_entry = dp->d_un.d_val; 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%jx)", ef->ef_name, (uintmax_t)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); } if (rel_off != 0) { if (rel_entry == 0) { warnx("%s: no DT_RELENT for DT_REL", ef->ef_name); return (EFTYPE); } if (rel_entry != sizeof(Elf_Rel)) { warnx("%s: inconsistent DT_RELENT value", ef->ef_name); return (EFTYPE); } if (rel_sz % rel_entry != 0) { warnx("%s: inconsistent values for DT_RELSZ and " "DT_RELENT", ef->ef_name); return (EFTYPE); } if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz, (void **)&ef->ef_rel) != 0) { warnx("%s: cannot load DT_REL section", ef->ef_name); return (EIO); } ef->ef_relsz = rel_sz / rel_entry; if (ef->ef_verbose) warnx("%s: %d REL entries", ef->ef_name, ef->ef_relsz); } if (rela_off != 0) { if (rela_entry == 0) { warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name); return (EFTYPE); } if (rela_entry != sizeof(Elf_Rela)) { warnx("%s: inconsistent DT_RELAENT value", ef->ef_name); return (EFTYPE); } if (rela_sz % rela_entry != 0) { warnx("%s: inconsistent values for DT_RELASZ and " "DT_RELAENT", ef->ef_name); return (EFTYPE); } if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz, (void **)&ef->ef_rela) != 0) { warnx("%s: cannot load DT_RELA section", ef->ef_name); return (EIO); } ef->ef_relasz = rela_sz / rela_entry; if (ef->ef_verbose) warnx("%s: %d RELA entries", ef->ef_name, ef->ef_relasz); } return (0); } static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) { ssize_t r; if (offset != (Elf_Off)-1) { if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) return (EIO); } r = read(ef->ef_fd, dest, len); if (r != -1 && (size_t)r == len) return (0); else return (EIO); } static 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 (errno); error = ef_read(ef, offset, len, *ptr); if (error != 0) free(*ptr); return (error); } static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) { Elf_Off ofs; ofs = ef_get_offset(ef, offset); if (ofs == 0) { if (ef->ef_verbose) warnx("ef_seg_read(%s): zero offset (%jx:%ju)", ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); return (EFAULT); } return (ef_read(ef, ofs, len, dest)); } static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) { Elf_Off ofs; const Elf_Rela *a; const Elf_Rel *r; int error; ofs = ef_get_offset(ef, offset); if (ofs == 0) { if (ef->ef_verbose) warnx("ef_seg_read_rel(%s): zero offset (%jx:%ju)", ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); return (EFAULT); } if ((error = ef_read(ef, ofs, len, dest)) != 0) return (error); for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) { error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len, dest); if (error != 0) return (error); } for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) { error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len, dest); if (error != 0) return (error); } return (0); } static int ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest) { Elf_Off ofs; ssize_t r; ofs = ef_get_offset(ef, offset); if (ofs == 0 || ofs == (Elf_Off)-1) { if (ef->ef_verbose) warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)", ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); return (EFAULT); } r = pread(ef->ef_fd, dest, len, ofs); if (r < 0) return (errno); if (strnlen(dest, len) == len) return (EFAULT); return (0); } static 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 (errno); error = ef_seg_read(ef, offset, len, *ptr); if (error != 0) free(*ptr); return (error); } static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) { int error; *ptr = malloc(len); if (*ptr == NULL) return (errno); error = ef_seg_read_rel(ef, offset, len, *ptr); if (error != 0) free(*ptr); return (error); } int ef_open(const char *filename, struct elf_file *efile, int verbose) { elf_file_t ef; Elf_Ehdr *hdr; int fd; int error; int phlen, res; int nsegs; Elf_Phdr *phdr, *phdyn, *phlimit; if (filename == NULL) return (EINVAL); if ((fd = open(filename, O_RDONLY)) == -1) return (errno); ef = malloc(sizeof(*ef)); if (ef == NULL) { close(fd); return (errno); } efile->ef_ef = ef; efile->ef_ops = &ef_file_ops; bzero(ef, sizeof(*ef)); ef->ef_verbose = verbose; ef->ef_fd = fd; ef->ef_name = strdup(filename); ef->ef_efile = efile; 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; while (phdr < phlimit) { if (verbose > 1) ef_print_phdr(phdr); switch (phdr->p_type) { case PT_LOAD: if (nsegs < MAXSEGS) ef->ef_segs[nsegs] = phdr; nsegs++; break; case PT_PHDR: break; case PT_DYNAMIC: phdyn = phdr; break; } phdr++; } if (verbose > 1) printf("\n"); if (phdyn == NULL) { warnx("Skipping %s: not dynamically-linked", filename); break; } else if (nsegs > MAXSEGS) { warnx("%s: too many segments", filename); break; } ef->ef_nsegs = nsegs; 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 != 0) break; if (hdr->e_type == ET_DYN) { ef->ef_type = EFT_KLD; error = 0; } else if (hdr->e_type == ET_EXEC) { ef->ef_type = EFT_KERNEL; error = 0; } else break; } while(0); if (error != 0) ef_close(ef); return (error); } static int ef_close(elf_file_t ef) { close(ef->ef_fd); if (ef->ef_name) free(ef->ef_name); ef->ef_efile->ef_ops = NULL; ef->ef_efile->ef_ef = NULL; free(ef); return (0); } diff --git a/usr.sbin/kldxref/ef_obj.c b/usr.sbin/kldxref/ef_obj.c index 953e54dc0d28..027408876a5e 100644 --- a/usr.sbin/kldxref/ef_obj.c +++ b/usr.sbin/kldxref/ef_obj.c @@ -1,632 +1,631 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000, Boris Popov * Copyright (c) 1998-2000 Doug Rabson * Copyright (c) 2004 Peter Wemm * 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. */ #include #include #include #include #include #include #include #include #include #include -#define FREEBSD_ELF #include "ef.h" typedef struct { void *addr; Elf_Off size; int flags; int sec; /* Original section */ char *name; } Elf_progent; typedef struct { Elf_Rel *rel; int nrel; int sec; } Elf_relent; typedef struct { Elf_Rela *rela; int nrela; int sec; } Elf_relaent; struct ef_file { char *ef_name; int ef_fd; Elf_Ehdr ef_hdr; struct elf_file *ef_efile; caddr_t address; Elf_Off size; Elf_Shdr *e_shdr; Elf_progent *progtab; int nprogtab; Elf_relaent *relatab; int nrela; Elf_relent *reltab; int nrel; Elf_Sym *ddbsymtab; /* The symbol table we are using */ long ddbsymcnt; /* Number of symbols */ caddr_t ddbstrtab; /* String table */ long ddbstrcnt; /* number of bytes in string table */ caddr_t shstrtab; /* Section name string table */ long shstrcnt; /* number of bytes in string table */ int ef_verbose; }; static int ef_obj_get_type(elf_file_t ef); static int ef_obj_close(elf_file_t ef); static int ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest); static int ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr); static int ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest); static int ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest); static int ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest); static int ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr); static int ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void **ptr); static Elf_Addr ef_obj_symaddr(elf_file_t ef, Elf_Size symidx); static int ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, long *countp); static int ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym); static struct elf_file_ops ef_obj_file_ops = { .get_type = ef_obj_get_type, .close = ef_obj_close, .read = ef_obj_read, .read_entry = ef_obj_read_entry, .seg_read = ef_obj_seg_read, .seg_read_rel = ef_obj_seg_read_rel, .seg_read_string = ef_obj_seg_read_string, .seg_read_entry = ef_obj_seg_read_entry, .seg_read_entry_rel = ef_obj_seg_read_entry_rel, .symaddr = ef_obj_symaddr, .lookup_set = ef_obj_lookup_set, .lookup_symbol = ef_obj_lookup_symbol }; static int ef_obj_get_type(elf_file_t __unused ef) { return (EFT_KLD); } static int ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) { Elf_Sym *symp; const char *strp; int i; for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { strp = ef->ddbstrtab + symp->st_name; if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) { *sym = symp; return (0); } } return (ENOENT); } static int ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, long *countp) { int i; for (i = 0; i < ef->nprogtab; i++) { if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) && strcmp(ef->progtab[i].name + 4, name) == 0) { *startp = (char *)ef->progtab[i].addr - ef->address; *stopp = (char *)ef->progtab[i].addr + ef->progtab[i].size - ef->address; *countp = (*stopp - *startp) / sizeof(void *); return (0); } } return (ESRCH); } static Elf_Addr ef_obj_symaddr(elf_file_t ef, Elf_Size symidx) { const Elf_Sym *sym; if (symidx >= (size_t) ef->ddbsymcnt) return (0); sym = ef->ddbsymtab + symidx; if (sym->st_shndx != SHN_UNDEF) return (sym->st_value - (Elf_Addr)ef->address); return (0); } static int ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) { ssize_t r; if (offset != (Elf_Off)-1) { if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) return (EIO); } r = read(ef->ef_fd, dest, len); if (r != -1 && (size_t)r == len) return (0); else return (EIO); } static int ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) { int error; *ptr = malloc(len); if (*ptr == NULL) return (errno); error = ef_obj_read(ef, offset, len, *ptr); if (error != 0) free(*ptr); return (error); } static int ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) { if (offset + len > ef->size) { if (ef->ef_verbose) warnx("ef_obj_seg_read(%s): bad offset/len (%lx:%ld)", ef->ef_name, (long)offset, (long)len); return (EFAULT); } bcopy(ef->address + offset, dest, len); return (0); } static int ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) { char *memaddr; Elf_Rel *r; Elf_Rela *a; Elf_Off secbase, dataoff; int error, i, sec; if (offset + len > ef->size) { if (ef->ef_verbose) warnx("ef_obj_seg_read_rel(%s): bad offset/len (%lx:%ld)", ef->ef_name, (long)offset, (long)len); return (EFAULT); } bcopy(ef->address + offset, dest, len); /* Find out which section contains the data. */ memaddr = ef->address + offset; sec = -1; secbase = dataoff = 0; for (i = 0; i < ef->nprogtab; i++) { if (ef->progtab[i].addr == NULL) continue; if (memaddr < (char *)ef->progtab[i].addr || memaddr + len > (char *)ef->progtab[i].addr + ef->progtab[i].size) continue; sec = ef->progtab[i].sec; /* We relocate to address 0. */ secbase = (char *)ef->progtab[i].addr - ef->address; dataoff = memaddr - ef->address; break; } if (sec == -1) return (EFAULT); /* Now do the relocations. */ for (i = 0; i < ef->nrel; i++) { if (ef->reltab[i].sec != sec) continue; for (r = ef->reltab[i].rel; r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) { error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, secbase, dataoff, len, dest); if (error != 0) return (error); } } for (i = 0; i < ef->nrela; i++) { if (ef->relatab[i].sec != sec) continue; for (a = ef->relatab[i].rela; a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) { error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, secbase, dataoff, len, dest); if (error != 0) return (error); } } return (0); } static int ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest) { if (offset >= ef->size) { if (ef->ef_verbose) warnx("ef_obj_seg_read_string(%s): bad offset (%lx)", ef->ef_name, (long)offset); return (EFAULT); } if (ef->size - offset < len) len = ef->size - offset; if (strnlen(ef->address + offset, len) == len) return (EFAULT); memcpy(dest, ef->address + offset, len); return (0); } static int ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) { int error; *ptr = malloc(len); if (*ptr == NULL) return (errno); error = ef_obj_seg_read(ef, offset, len, *ptr); if (error != 0) free(*ptr); return (error); } static int ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) { int error; *ptr = malloc(len); if (*ptr == NULL) return (errno); error = ef_obj_seg_read_rel(ef, offset, len, *ptr); if (error != 0) free(*ptr); return (error); } int ef_obj_open(const char *filename, struct elf_file *efile, int verbose) { elf_file_t ef; Elf_Ehdr *hdr; Elf_Shdr *shdr; Elf_Sym *es; char *mapbase; void *vtmp; size_t mapsize, alignmask, max_addralign; int error, fd, pb, ra, res, rl; int i, j, nbytes, nsym, shstrindex, symstrindex, symtabindex; if (filename == NULL) return (EINVAL); if ((fd = open(filename, O_RDONLY)) == -1) return (errno); ef = calloc(1, sizeof(*ef)); if (ef == NULL) { close(fd); return (errno); } efile->ef_ef = ef; efile->ef_ops = &ef_obj_file_ops; ef->ef_verbose = verbose; ef->ef_fd = fd; ef->ef_name = strdup(filename); ef->ef_efile = efile; hdr = (Elf_Ehdr *)&ef->ef_hdr; res = read(fd, hdr, sizeof(*hdr)); error = EFTYPE; if (res != sizeof(*hdr)) goto out; if (!IS_ELF(*hdr)) goto out; 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_type != ET_REL) goto out; nbytes = hdr->e_shnum * hdr->e_shentsize; if (nbytes == 0 || hdr->e_shoff == 0 || hdr->e_shentsize != sizeof(Elf_Shdr)) goto out; if (ef_obj_read_entry(ef, hdr->e_shoff, nbytes, &vtmp) != 0) { printf("ef_read_entry failed\n"); goto out; } ef->e_shdr = shdr = vtmp; /* Scan the section header for information and table sizing. */ nsym = 0; symtabindex = -1; symstrindex = -1; for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: ef->nprogtab++; break; case SHT_SYMTAB: nsym++; symtabindex = i; symstrindex = shdr[i].sh_link; break; case SHT_REL: ef->nrel++; break; case SHT_RELA: ef->nrela++; break; case SHT_STRTAB: break; } } if (ef->nprogtab == 0) { warnx("%s: file has no contents", filename); goto out; } if (nsym != 1) { warnx("%s: file has no valid symbol table", filename); goto out; } if (symstrindex < 0 || symstrindex > hdr->e_shnum || shdr[symstrindex].sh_type != SHT_STRTAB) { warnx("%s: file has invalid symbol strings", filename); goto out; } /* Allocate space for tracking the load chunks */ if (ef->nprogtab != 0) ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab)); if (ef->nrel != 0) ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab)); if (ef->nrela != 0) ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab)); if ((ef->nprogtab != 0 && ef->progtab == NULL) || (ef->nrel != 0 && ef->reltab == NULL) || (ef->nrela != 0 && ef->relatab == NULL)) { printf("malloc failed\n"); error = ENOMEM; goto out; } ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym); if (ef_obj_read_entry(ef, shdr[symtabindex].sh_offset, shdr[symtabindex].sh_size, (void**)&ef->ddbsymtab) != 0) { printf("ef_read_entry failed\n"); goto out; } ef->ddbstrcnt = shdr[symstrindex].sh_size; if (ef_obj_read_entry(ef, shdr[symstrindex].sh_offset, shdr[symstrindex].sh_size, (void**)&ef->ddbstrtab) != 0) { printf("ef_read_entry failed\n"); goto out; } /* Do we have a string table for the section names? */ shstrindex = -1; if (hdr->e_shstrndx != 0 && shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) { shstrindex = hdr->e_shstrndx; ef->shstrcnt = shdr[shstrindex].sh_size; if (ef_obj_read_entry(ef, shdr[shstrindex].sh_offset, shdr[shstrindex].sh_size, (void**)&ef->shstrtab) != 0) { printf("ef_read_entry failed\n"); goto out; } } /* Size up code/data(progbits) and bss(nobits). */ alignmask = 0; max_addralign = 0; mapsize = 0; for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: alignmask = shdr[i].sh_addralign - 1; if (shdr[i].sh_addralign > max_addralign) max_addralign = shdr[i].sh_addralign; mapsize += alignmask; mapsize &= ~alignmask; mapsize += shdr[i].sh_size; break; } } /* We know how much space we need for the text/data/bss/etc. */ ef->size = mapsize; if (posix_memalign((void **)&ef->address, max_addralign, mapsize)) { printf("posix_memalign failed\n"); goto out; } mapbase = ef->address; /* * Now load code/data(progbits), zero bss(nobits), allocate * space for and load relocs */ pb = 0; rl = 0; ra = 0; alignmask = 0; for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: alignmask = shdr[i].sh_addralign - 1; mapbase += alignmask; mapbase = (char *)((uintptr_t)mapbase & ~alignmask); ef->progtab[pb].addr = (void *)(uintptr_t)mapbase; if (shdr[i].sh_type == SHT_PROGBITS) { ef->progtab[pb].name = "<>"; if (ef_obj_read(ef, shdr[i].sh_offset, shdr[i].sh_size, ef->progtab[pb].addr) != 0) { printf("failed to read progbits\n"); goto out; } } else { ef->progtab[pb].name = "<>"; bzero(ef->progtab[pb].addr, shdr[i].sh_size); } ef->progtab[pb].size = shdr[i].sh_size; ef->progtab[pb].sec = i; if (ef->shstrtab && shdr[i].sh_name != 0) ef->progtab[pb].name = ef->shstrtab + shdr[i].sh_name; /* Update all symbol values with the offset. */ for (j = 0; j < ef->ddbsymcnt; j++) { es = &ef->ddbsymtab[j]; if (es->st_shndx != i) continue; es->st_value += (Elf_Addr)ef->progtab[pb].addr; } mapbase += shdr[i].sh_size; pb++; break; case SHT_REL: ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel); ef->reltab[rl].sec = shdr[i].sh_info; if (ef_obj_read_entry(ef, shdr[i].sh_offset, shdr[i].sh_size, (void**)&ef->reltab[rl].rel) != 0) { printf("ef_read_entry failed\n"); goto out; } rl++; break; case SHT_RELA: ef->relatab[ra].nrela = shdr[i].sh_size / sizeof(Elf_Rela); ef->relatab[ra].sec = shdr[i].sh_info; if (ef_obj_read_entry(ef, shdr[i].sh_offset, shdr[i].sh_size, (void**)&ef->relatab[ra].rela) != 0) { printf("ef_read_entry failed\n"); goto out; } ra++; break; } } error = 0; out: if (error != 0) ef_obj_close(ef); return (error); } static int ef_obj_close(elf_file_t ef) { int i; close(ef->ef_fd); if (ef->ef_name) free(ef->ef_name); if (ef->e_shdr != NULL) free(ef->e_shdr); if (ef->size != 0) free(ef->address); if (ef->nprogtab != 0) free(ef->progtab); if (ef->nrel != 0) { for (i = 0; i < ef->nrel; i++) if (ef->reltab[i].rel != NULL) free(ef->reltab[i].rel); free(ef->reltab); } if (ef->nrela != 0) { for (i = 0; i < ef->nrela; i++) if (ef->relatab[i].rela != NULL) free(ef->relatab[i].rela); free(ef->relatab); } if (ef->ddbsymtab != NULL) free(ef->ddbsymtab); if (ef->ddbstrtab != NULL) free(ef->ddbstrtab); if (ef->shstrtab != NULL) free(ef->shstrtab); ef->ef_efile->ef_ops = NULL; ef->ef_efile->ef_ef = NULL; free(ef); return (0); } diff --git a/usr.sbin/kldxref/kldxref.c b/usr.sbin/kldxref/kldxref.c index 154cf90a597f..f91571a4ef11 100644 --- a/usr.sbin/kldxref/kldxref.c +++ b/usr.sbin/kldxref/kldxref.c @@ -1,766 +1,765 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * 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. */ #include #include #include #include #include #include #include #include #include #include -#define FREEBSD_ELF #include #include #include #include #include #include #include #include #include #include #include "ef.h" #define MAXRECSIZE (64 << 10) /* 64k */ #define check(val) if ((error = (val)) != 0) break static bool dflag; /* do not create a hint file, only write on stdout */ static int verbose; static FILE *fxref; /* current hints file */ static const char *xref_file = "linker.hints"; /* * A record is stored in the static buffer recbuf before going to disk. */ static char recbuf[MAXRECSIZE]; static int recpos; /* current write position */ static int reccnt; /* total record written to this file so far */ static void intalign(void) { recpos = roundup2(recpos, sizeof(int)); } static void record_start(void) { recpos = 0; memset(recbuf, 0, MAXRECSIZE); } static int record_end(void) { if (recpos == 0) return (0); reccnt++; intalign(); fwrite(&recpos, sizeof(recpos), 1, fxref); return (fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0); } static int record_buf(const void *buf, size_t size) { if (MAXRECSIZE - recpos < size) errx(1, "record buffer overflow"); memcpy(recbuf + recpos, buf, size); recpos += size; return (0); } /* * An int is stored in host order and aligned */ static int record_int(int val) { intalign(); return (record_buf(&val, sizeof(val))); } /* * A string is stored as 1-byte length plus data, no padding */ static int record_string(const char *str) { int error; size_t len; u_char val; if (dflag) return (0); val = len = strlen(str); if (len > 255) errx(1, "string %s too long", str); error = record_buf(&val, sizeof(val)); if (error != 0) return (error); return (record_buf(str, len)); } /* From sys/isa/pnp.c */ static char * pnp_eisaformat(uint32_t id) { uint8_t *data; static char idbuf[8]; const char hextoascii[] = "0123456789abcdef"; id = htole32(id); data = (uint8_t *)&id; idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); idbuf[2] = '@' + (data[1] & 0x1f); idbuf[3] = hextoascii[(data[2] >> 4)]; idbuf[4] = hextoascii[(data[2] & 0xf)]; idbuf[5] = hextoascii[(data[3] >> 4)]; idbuf[6] = hextoascii[(data[3] & 0xf)]; idbuf[7] = 0; return (idbuf); } struct pnp_elt { int pe_kind; /* What kind of entry */ #define TYPE_SZ_MASK 0x0f #define TYPE_FLAGGED 0x10 /* all f's is a wildcard */ #define TYPE_INT 0x20 /* Is a number */ #define TYPE_PAIRED 0x40 #define TYPE_LE 0x80 /* Matches <= this value */ #define TYPE_GE 0x100 /* Matches >= this value */ #define TYPE_MASK 0x200 /* Specifies a mask to follow */ #define TYPE_U8 (1 | TYPE_INT) #define TYPE_V8 (1 | TYPE_INT | TYPE_FLAGGED) #define TYPE_G16 (2 | TYPE_INT | TYPE_GE) #define TYPE_L16 (2 | TYPE_INT | TYPE_LE) #define TYPE_M16 (2 | TYPE_INT | TYPE_MASK) #define TYPE_U16 (2 | TYPE_INT) #define TYPE_V16 (2 | TYPE_INT | TYPE_FLAGGED) #define TYPE_U32 (4 | TYPE_INT) #define TYPE_V32 (4 | TYPE_INT | TYPE_FLAGGED) #define TYPE_W32 (4 | TYPE_INT | TYPE_PAIRED) #define TYPE_D 7 #define TYPE_Z 8 #define TYPE_P 9 #define TYPE_E 10 #define TYPE_T 11 int pe_offset; /* Offset within the element */ char * pe_key; /* pnp key name */ TAILQ_ENTRY(pnp_elt) next; /* Link */ }; typedef TAILQ_HEAD(pnp_head, pnp_elt) pnp_list; /* * this function finds the data from the pnp table, as described by the * the description and creates a new output (new_desc). This output table * is a form that's easier for the agent that's automatically loading the * modules. * * The format output is the simplified string from this routine in the * same basic format as the pnp string, as documented in sys/module.h. * First a string describing the format is output, the a count of the * number of records, then each record. The format string also describes * the length of each entry (though it isn't a fixed length when strings * are present). * * type Output Meaning * I uint32_t Integer equality comparison * J uint32_t Pair of uint16_t fields converted to native * byte order. The two fields both must match. * G uint32_t Greater than or equal to * L uint32_t Less than or equal to * M uint32_t Mask of which fields to test. Fields that * take up space increment the count. This * field must be first, and resets the count. * D string Description of the device this pnp info is for * Z string pnp string must match this * T nothing T fields set pnp values that must be true for * the entire table. * Values are packed the same way that other values are packed in this file. * Strings and int32_t's start on a 32-bit boundary and are padded with 0 * bytes. Objects that are smaller than uint32_t are converted, without * sign extension to uint32_t to simplify parsing downstream. */ static int parse_pnp_list(const char *desc, char **new_desc, pnp_list *list) { const char *walker, *ep; const char *colon, *semi; struct pnp_elt *elt; char type[8], key[32]; int off; size_t new_desc_size; FILE *fp; walker = desc; ep = desc + strlen(desc); off = 0; fp = open_memstream(new_desc, &new_desc_size); if (fp == NULL) err(1, "Could not open new memory stream"); if (verbose > 1) printf("Converting %s into a list\n", desc); while (walker < ep) { colon = strchr(walker, ':'); semi = strchr(walker, ';'); if (semi != NULL && semi < colon) goto err; if (colon - walker > sizeof(type)) goto err; strncpy(type, walker, colon - walker); type[colon - walker] = '\0'; if (semi != NULL) { if (semi - colon >= sizeof(key)) goto err; strncpy(key, colon + 1, semi - colon - 1); key[semi - colon - 1] = '\0'; walker = semi + 1; /* Fail safe if we have spaces after ; */ while (walker < ep && isspace(*walker)) walker++; } else { if (strlen(colon + 1) >= sizeof(key)) goto err; strcpy(key, colon + 1); walker = ep; } if (verbose > 1) printf("Found type %s for name %s\n", type, key); /* Skip pointer place holders */ if (strcmp(type, "P") == 0) { off += sizeof(void *); continue; } /* * Add a node of the appropriate type */ elt = malloc(sizeof(struct pnp_elt) + strlen(key) + 1); TAILQ_INSERT_TAIL(list, elt, next); elt->pe_key = (char *)(elt + 1); elt->pe_offset = off; if (strcmp(type, "U8") == 0) elt->pe_kind = TYPE_U8; else if (strcmp(type, "V8") == 0) elt->pe_kind = TYPE_V8; else if (strcmp(type, "G16") == 0) elt->pe_kind = TYPE_G16; else if (strcmp(type, "L16") == 0) elt->pe_kind = TYPE_L16; else if (strcmp(type, "M16") == 0) elt->pe_kind = TYPE_M16; else if (strcmp(type, "U16") == 0) elt->pe_kind = TYPE_U16; else if (strcmp(type, "V16") == 0) elt->pe_kind = TYPE_V16; else if (strcmp(type, "U32") == 0) elt->pe_kind = TYPE_U32; else if (strcmp(type, "V32") == 0) elt->pe_kind = TYPE_V32; else if (strcmp(type, "W32") == 0) elt->pe_kind = TYPE_W32; else if (strcmp(type, "D") == 0) /* description char * */ elt->pe_kind = TYPE_D; else if (strcmp(type, "Z") == 0) /* char * to match */ elt->pe_kind = TYPE_Z; else if (strcmp(type, "P") == 0) /* Pointer -- ignored */ elt->pe_kind = TYPE_P; else if (strcmp(type, "E") == 0) /* EISA PNP ID, as uint32_t */ elt->pe_kind = TYPE_E; else if (strcmp(type, "T") == 0) elt->pe_kind = TYPE_T; else goto err; /* * Maybe the rounding here needs to be more nuanced and/or somehow * architecture specific. Fortunately, most tables in the system * have sane ordering of types. */ if (elt->pe_kind & TYPE_INT) { elt->pe_offset = roundup2(elt->pe_offset, elt->pe_kind & TYPE_SZ_MASK); off = elt->pe_offset + (elt->pe_kind & TYPE_SZ_MASK); } else if (elt->pe_kind == TYPE_E) { /* Type E stored as Int, displays as string */ elt->pe_offset = roundup2(elt->pe_offset, sizeof(uint32_t)); off = elt->pe_offset + sizeof(uint32_t); } else if (elt->pe_kind == TYPE_T) { /* doesn't actually consume space in the table */ off = elt->pe_offset; } else { elt->pe_offset = roundup2(elt->pe_offset, sizeof(void *)); off = elt->pe_offset + sizeof(void *); } if (elt->pe_kind & TYPE_PAIRED) { char *word, *ctx, newtype; for (word = strtok_r(key, "/", &ctx); word; word = strtok_r(NULL, "/", &ctx)) { newtype = elt->pe_kind & TYPE_FLAGGED ? 'J' : 'I'; fprintf(fp, "%c:%s;", newtype, word); } } else { char newtype; if (elt->pe_kind & TYPE_FLAGGED) newtype = 'J'; else if (elt->pe_kind & TYPE_GE) newtype = 'G'; else if (elt->pe_kind & TYPE_LE) newtype = 'L'; else if (elt->pe_kind & TYPE_MASK) newtype = 'M'; else if (elt->pe_kind & TYPE_INT) newtype = 'I'; else if (elt->pe_kind == TYPE_D) newtype = 'D'; else if (elt->pe_kind == TYPE_Z || elt->pe_kind == TYPE_E) newtype = 'Z'; else if (elt->pe_kind == TYPE_T) newtype = 'T'; else errx(1, "Impossible type %x\n", elt->pe_kind); fprintf(fp, "%c:%s;", newtype, key); } } if (ferror(fp) != 0) { fclose(fp); errx(1, "Exhausted space converting description %s", desc); } if (fclose(fp) != 0) errx(1, "Failed to close memory stream"); return (0); err: errx(1, "Parse error of description string %s", desc); } static int parse_entry(struct mod_metadata *md, const char *cval, struct elf_file *ef, const char *kldname) { struct mod_depend mdp; struct mod_version mdv; struct mod_pnp_match_info pnp; char descr[1024]; Elf_Off data; int error, i; size_t len; char *walker; void *table; data = (Elf_Off)md->md_data; error = 0; record_start(); switch (md->md_type) { case MDT_DEPEND: if (!dflag) break; check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); printf(" depends on %s.%d (%d,%d)\n", cval, mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); break; case MDT_VERSION: check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); if (dflag) { printf(" interface %s.%d\n", cval, mdv.mv_version); } else { record_int(MDT_VERSION); record_string(cval); record_int(mdv.mv_version); record_string(kldname); } break; case MDT_MODULE: if (dflag) { printf(" module %s\n", cval); } else { record_int(MDT_MODULE); record_string(cval); record_string(kldname); } break; case MDT_PNP_INFO: check(EF_SEG_READ_REL(ef, data, sizeof(pnp), &pnp)); check(EF_SEG_READ_STRING(ef, (Elf_Off)pnp.descr, sizeof(descr), descr)); descr[sizeof(descr) - 1] = '\0'; if (dflag) { printf(" pnp info for bus %s format %s %d entries of %d bytes\n", cval, descr, pnp.num_entry, pnp.entry_len); } else { pnp_list list; struct pnp_elt *elt, *elt_tmp; char *new_descr; if (verbose > 1) printf(" pnp info for bus %s format %s %d entries of %d bytes\n", cval, descr, pnp.num_entry, pnp.entry_len); /* * Parse descr to weed out the chaff and to create a list * of offsets to output. */ TAILQ_INIT(&list); parse_pnp_list(descr, &new_descr, &list); record_int(MDT_PNP_INFO); record_string(cval); record_string(new_descr); record_int(pnp.num_entry); len = pnp.num_entry * pnp.entry_len; walker = table = malloc(len); check(EF_SEG_READ_REL(ef, (Elf_Off)pnp.table, len, table)); /* * Walk the list and output things. We've collapsed all the * variant forms of the table down to just ints and strings. */ for (i = 0; i < pnp.num_entry; i++) { TAILQ_FOREACH(elt, &list, next) { uint8_t v1; uint16_t v2; uint32_t v4; int value; char buffer[1024]; if (elt->pe_kind == TYPE_W32) { memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); value = v4 & 0xffff; record_int(value); if (verbose > 1) printf("W32:%#x", value); value = (v4 >> 16) & 0xffff; record_int(value); if (verbose > 1) printf(":%#x;", value); } else if (elt->pe_kind & TYPE_INT) { switch (elt->pe_kind & TYPE_SZ_MASK) { case 1: memcpy(&v1, walker + elt->pe_offset, sizeof(v1)); if ((elt->pe_kind & TYPE_FLAGGED) && v1 == 0xff) value = -1; else value = v1; break; case 2: memcpy(&v2, walker + elt->pe_offset, sizeof(v2)); if ((elt->pe_kind & TYPE_FLAGGED) && v2 == 0xffff) value = -1; else value = v2; break; case 4: memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); if ((elt->pe_kind & TYPE_FLAGGED) && v4 == 0xffffffff) value = -1; else value = v4; break; default: errx(1, "Invalid size somehow %#x", elt->pe_kind); } if (verbose > 1) printf("I:%#x;", value); record_int(value); } else if (elt->pe_kind == TYPE_T) { /* Do nothing */ } else { /* E, Z or D -- P already filtered */ if (elt->pe_kind == TYPE_E) { memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); strcpy(buffer, pnp_eisaformat(v4)); } else { char *ptr; ptr = *(char **)(walker + elt->pe_offset); buffer[0] = '\0'; if (ptr != NULL) { EF_SEG_READ_STRING(ef, (Elf_Off)ptr, sizeof(buffer), buffer); buffer[sizeof(buffer) - 1] = '\0'; } } if (verbose > 1) printf("%c:%s;", elt->pe_kind == TYPE_E ? 'E' : (elt->pe_kind == TYPE_Z ? 'Z' : 'D'), buffer); record_string(buffer); } } if (verbose > 1) printf("\n"); walker += pnp.entry_len; } /* Now free it */ TAILQ_FOREACH_SAFE(elt, &list, next, elt_tmp) { TAILQ_REMOVE(&list, elt, next); free(elt); } free(table); } break; default: warnx("unknown metadata record %d in file %s", md->md_type, kldname); } if (!error) record_end(); return (error); } static int read_kld(char *filename, char *kldname) { struct mod_metadata md; struct elf_file ef; void **p; int error, eftype; long start, finish, entries, i; char cval[MAXMODNAME + 1]; if (verbose || dflag) printf("%s\n", filename); error = ef_open(filename, &ef, verbose); if (error != 0) { error = ef_obj_open(filename, &ef, verbose); if (error != 0) { if (verbose) warnc(error, "elf_open(%s)", filename); return (error); } } eftype = EF_GET_TYPE(&ef); if (eftype != EFT_KLD && eftype != EFT_KERNEL) { EF_CLOSE(&ef); return (0); } do { check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, &entries)); check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, (void *)&p)); /* * Do a first pass to find MDT_MODULE. It is required to be * ordered first in the output linker.hints stream because it * serves as an implicit record boundary between distinct klds * in the stream. Other MDTs only make sense in the context of * a specific MDT_MODULE. * * Some compilers (e.g., GCC 6.4.0 xtoolchain) or binutils * (e.g., GNU binutils 2.32 objcopy/ld.bfd) can reorder * MODULE_METADATA set entries relative to the source ordering. * This is permitted by the C standard; memory layout of * file-scope objects is left implementation-defined. There is * no requirement that source code ordering is retained. * * Handle that here by taking two passes to ensure MDT_MODULE * records are emitted to linker.hints before other MDT records * in the same kld. */ for (i = 0; i < entries; i++) { check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), &md)); check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval)); if (md.md_type == MDT_MODULE) { parse_entry(&md, cval, &ef, kldname); break; } } if (error != 0) { warnc(error, "error while reading %s", filename); break; } /* * Second pass for all !MDT_MODULE entries. */ for (i = 0; i < entries; i++) { check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), &md)); check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval)); if (md.md_type != MDT_MODULE) parse_entry(&md, cval, &ef, kldname); } if (error != 0) warnc(error, "error while reading %s", filename); free(p); } while(0); EF_CLOSE(&ef); return (error); } /* * Create a temp file in directory root, make sure we don't * overflow the buffer for the destination name */ static FILE * maketempfile(char *dest, const char *root) { char *p; int n, fd; p = strrchr(root, '/'); n = p != NULL ? p - root + 1 : 0; if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= MAXPATHLEN) { errno = ENAMETOOLONG; return (NULL); } fd = mkstemp(dest); if (fd < 0) return (NULL); fchmod(fd, 0644); /* nothing secret in the file */ return (fdopen(fd, "w+")); } static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; static void usage(void) { fprintf(stderr, "%s\n", "usage: kldxref [-Rdv] [-f hintsfile] path ..." ); exit(1); } static int compare(const FTSENT *const *a, const FTSENT *const *b) { if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) return (1); if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) return (-1); return (strcmp((*a)->fts_name, (*b)->fts_name)); } int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; int opt, fts_options, ival; struct stat sb; fts_options = FTS_PHYSICAL; while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { switch (opt) { case 'd': /* no hint file, only print on stdout */ dflag = true; break; case 'f': /* use this name instead of linker.hints */ xref_file = optarg; break; case 'v': verbose++; break; case 'R': /* recurse on directories */ fts_options |= FTS_COMFOLLOW; break; default: usage(); /* NOTREACHED */ } } if (argc - optind < 1) usage(); argc -= optind; argv += optind; if (stat(argv[0], &sb) != 0) err(1, "%s", argv[0]); if ((sb.st_mode & S_IFDIR) == 0 && !dflag) { errno = ENOTDIR; err(1, "%s", argv[0]); } ftsp = fts_open(argv, fts_options, compare); if (ftsp == NULL) exit(1); for (;;) { p = fts_read(ftsp); if ((p == NULL || p->fts_info == FTS_D) && fxref) { /* close and rename the current hint file */ fclose(fxref); fxref = NULL; if (reccnt != 0) { rename(tempname, xrefname); } else { /* didn't find any entry, ignore this file */ unlink(tempname); unlink(xrefname); } } if (p == NULL) break; if (p->fts_info == FTS_D && !dflag) { /* visiting a new directory, create a new hint file */ snprintf(xrefname, sizeof(xrefname), "%s/%s", ftsp->fts_path, xref_file); fxref = maketempfile(tempname, ftsp->fts_path); if (fxref == NULL) err(1, "can't create %s", tempname); ival = 1; fwrite(&ival, sizeof(ival), 1, fxref); reccnt = 0; } /* skip non-files and separate debug files */ if (p->fts_info != FTS_F) continue; if (p->fts_namelen >= 6 && strcmp(p->fts_name + p->fts_namelen - 6, ".debug") == 0) continue; if (p->fts_namelen >= 8 && strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0) continue; read_kld(p->fts_path, p->fts_name); } fts_close(ftsp); return (0); }