2004-08-29 00:48:42 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2004 Ian Dowse <iedowse@freebsd.org>
|
|
|
|
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
|
|
|
|
* Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
|
|
|
|
* 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 <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/exec.h>
|
|
|
|
#include <sys/linker.h>
|
|
|
|
#include <sys/module.h>
|
2017-12-05 21:38:04 +00:00
|
|
|
#include <stdint.h>
|
2004-08-29 00:48:42 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <machine/elf.h>
|
|
|
|
#include <stand.h>
|
|
|
|
#define FREEBSD_ELF
|
2017-12-05 21:38:04 +00:00
|
|
|
#include <sys/link_elf.h>
|
2004-08-29 00:48:42 +00:00
|
|
|
|
|
|
|
#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;
|
|
|
|
} *elf_file_t;
|
|
|
|
|
|
|
|
static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef,
|
2018-03-13 16:33:00 +00:00
|
|
|
uint64_t loadaddr);
|
2004-08-29 00:48:42 +00:00
|
|
|
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);
|
2005-12-18 04:52:37 +00:00
|
|
|
static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx);
|
2004-08-29 00:48:42 +00:00
|
|
|
|
|
|
|
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
|
2018-03-13 16:33:00 +00:00
|
|
|
__elfN(obj_loadfile)(char *filename, uint64_t dest,
|
2004-08-29 00:48:42 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
hdr = &ef.hdr;
|
|
|
|
bytes_read = read(ef.fd, 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;
|
|
|
|
}
|
|
|
|
|
2019-02-26 06:22:10 +00:00
|
|
|
#ifdef LOADER_VERIEXEC
|
|
|
|
if (verify_file(ef.fd, filename, bytes_read, VE_MUST) < 0) {
|
|
|
|
err = EAUTH;
|
|
|
|
goto oerr;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
loader: implement multiboot support for Xen Dom0
Implement a subset of the multiboot specification in order to boot Xen
and a FreeBSD Dom0 from the FreeBSD bootloader. This multiboot
implementation is tailored to boot Xen and FreeBSD Dom0, and it will
most surely fail to boot any other multiboot compilant kernel.
In order to detect and boot the Xen microkernel, two new file formats
are added to the bootloader, multiboot and multiboot_obj. Multiboot
support must be tested before regular ELF support, since Xen is a
multiboot kernel that also uses ELF. After a multiboot kernel is
detected, all the other loaded kernels/modules are parsed by the
multiboot_obj format.
The layout of the loaded objects in memory is the following; first the
Xen kernel is loaded as a 32bit ELF into memory (Xen will switch to
long mode by itself), after that the FreeBSD kernel is loaded as a RAW
file (Xen will parse and load it using it's internal ELF loader), and
finally the metadata and the modules are loaded using the native
FreeBSD way. After everything is loaded we jump into Xen's entry point
using a small trampoline. The order of the multiboot modules passed to
Xen is the following, the first module is the RAW FreeBSD kernel, and
the second module is the metadata and the FreeBSD modules.
Since Xen will relocate the memory position of the second
multiboot module (the one that contains the metadata and native
FreeBSD modules), we need to stash the original modulep address inside
of the metadata itself in order to recalculate its position once
booted. This also means the metadata must come before the loaded
modules, so after loading the FreeBSD kernel a portion of memory is
reserved in order to place the metadata before booting.
In order to tell the loader to boot Xen and then the FreeBSD kernel the
following has to be added to the /boot/loader.conf file:
xen_cmdline="dom0_mem=1024M dom0_max_vcpus=2 dom0pvh=1 console=com1,vga"
xen_kernel="/boot/xen"
The first argument contains the command line that will be passed to the Xen
kernel, while the second argument is the path to the Xen kernel itself. This
can also be done manually from the loader command line, by for example
typing the following set of commands:
OK unload
OK load /boot/xen dom0_mem=1024M dom0_max_vcpus=2 dom0pvh=1 console=com1,vga
OK load kernel
OK load zfs
OK load if_tap
OK load ...
OK boot
Sponsored by: Citrix Systems R&D
Reviewed by: jhb
Differential Revision: https://reviews.freebsd.org/D517
For the Forth bits:
Submitted by: Julien Grall <julien.grall AT citrix.com>
2015-01-15 16:27:20 +00:00
|
|
|
kfp = file_findfile(NULL, __elfN(obj_kerneltype));
|
2004-08-29 00:48:42 +00:00
|
|
|
if (kfp == NULL) {
|
|
|
|
printf("elf" __XSTRING(__ELF_WORD_SIZE)
|
|
|
|
"_obj_loadfile: can't load module before kernel\n");
|
|
|
|
err = EPERM;
|
|
|
|
goto oerr;
|
|
|
|
}
|
|
|
|
|
2011-04-03 22:31:51 +00:00
|
|
|
if (archsw.arch_loadaddr != NULL)
|
|
|
|
dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest);
|
|
|
|
else
|
|
|
|
dest = roundup(dest, PAGE_SIZE);
|
2004-08-29 00:48:42 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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));
|
|
|
|
|
|
|
|
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:
|
|
|
|
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
|
2018-03-13 16:33:00 +00:00
|
|
|
__elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off)
|
2004-08-29 00:48:42 +00:00
|
|
|
{
|
|
|
|
Elf_Ehdr *hdr;
|
2011-06-19 13:35:41 +00:00
|
|
|
Elf_Shdr *shdr, *cshdr, *lshdr;
|
2004-08-29 00:48:42 +00:00
|
|
|
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(ef->fd, (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++) {
|
2010-07-23 17:07:51 +00:00
|
|
|
if (shdr[i].sh_size == 0)
|
|
|
|
continue;
|
2004-08-29 00:48:42 +00:00
|
|
|
switch (shdr[i].sh_type) {
|
|
|
|
case SHT_PROGBITS:
|
|
|
|
case SHT_NOBITS:
|
2016-03-06 15:57:43 +00:00
|
|
|
#if defined(__i386__) || defined(__amd64__)
|
2016-04-08 10:23:48 +00:00
|
|
|
case SHT_X86_64_UNWIND:
|
2016-03-06 15:57:43 +00:00
|
|
|
#endif
|
Require the SHF_ALLOC flag for program sections from kernel object modules.
ELF object files can contain program sections which are not supposed
to be loaded into memory (e.g. .comment). Normally the static linker
uses these flags to decide which sections are allocated to loadable
program segments in ELF binaries and shared objects (including kernels
on all architectures and kernel modules on architectures other than
amd64).
Mapping ELF object files (such as amd64 kernel modules) into memory
directly is a bit of a grey area. ELF object files are intended to be
used as inputs to the static linker. As a result, there is not a
standardized definition for what the memory layout of an ELF object
should be (none of the section headers have valid virtual memory
addresses for example).
The kernel and loader were not checking the SHF_ALLOC flag but loading
any program sections with certain types such as SHT_PROGBITS. As a
result, the kernel and loader would load into RAM some sections that
weren't marked with SHF_ALLOC such as .comment that are not loaded
into RAM for kernel modules on other architectures (which are
implemented as ELF shared objects). Aside from possibly requiring
slightly more RAM to hold a kernel module this does not affect runtime
correctness as the kernel relocates symbols based on the layout it
uses.
Debuggers such as gdb and lldb do not extract symbol tables from a
running process or kernel. Instead, they replicate the memory layout
of ELF executables and shared objects and use that to construct their
own symbol tables. For executables and shared objects this works
fine. For ELF objects the current logic in kgdb (and probably lldb
based on a simple reading) assumes that only sections with SHF_ALLOC
are memory resident when constructing a memory layout. If the
debugger constructs a different memory layout than the kernel, then it
will compute different addresses for symbols causing symbols in the
debugger to appear to have the wrong values (though the kernel itself
is working fine). The current port of mdb does not check SHF_ALLOC as
it replicates the kernel's logic in its existing kernel support.
The bfd linker sorts the sections in ELF object files such that all of
the allocated sections (sections with SHF_ALLOCATED) are placed first
followed by unallocated sections. As a result, when kgdb composed a
memory layout using only the allocated sections, this layout happened
to match the layout used by the kernel and loader. The lld linker
does not sort the sections in ELF object files and mixed allocated and
unallocated sections. This resulted in kgdb composing a different
memory layout than the kernel and loader.
We could either patch kgdb (and possibly in the future lldb) to use
custom handling when generating memory layouts for kernel modules that
are ELF objects, or we could change the kernel and loader to check
SHF_ALLOCATED. I chose the latter as I feel we shouldn't be loading
things into RAM that the module won't use. This should mostly be a
NOP when linking with bfd but will allow the existing kgdb to work
with amd64 kernel modules linked with lld.
Note that we only require SHF_ALLOC for "program" sections for types
like SHT_PROGBITS and SHT_NOBITS. Other section types such as symbol
tables, string tables, and relocations must also be loaded and are not
marked with SHF_ALLOC.
Reported by: np
Reviewed by: kib, emaste
MFC after: 1 month
Sponsored by: Chelsio Communications
Differential Revision: https://reviews.freebsd.org/D13926
2018-01-17 22:51:59 +00:00
|
|
|
if ((shdr[i].sh_flags & SHF_ALLOC) == 0)
|
|
|
|
break;
|
2004-08-29 00:48:42 +00:00
|
|
|
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;
|
|
|
|
shdr[i].sh_addr = (Elf_Addr)lastaddr;
|
|
|
|
lastaddr += shdr[i].sh_size;
|
|
|
|
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:
|
2018-02-05 23:35:33 +00:00
|
|
|
if ((shdr[shdr[i].sh_info].sh_flags & SHF_ALLOC) == 0)
|
|
|
|
break;
|
2004-08-29 00:48:42 +00:00
|
|
|
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);
|
|
|
|
|
2011-06-19 13:35:41 +00:00
|
|
|
/* 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(ef->fd, (vm_offset_t)cshdr->sh_addr,
|
|
|
|
cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) {
|
2004-08-29 00:48:42 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr);
|
|
|
|
|
|
|
|
out:
|
|
|
|
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_* */
|
2018-03-13 16:33:00 +00:00
|
|
|
uint64_t md_data; /* specific data */
|
|
|
|
uint64_t md_cval; /* common string label */
|
2004-08-29 00:48:42 +00:00
|
|
|
};
|
|
|
|
#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)
|
2012-06-20 21:06:51 +00:00
|
|
|
return 0;
|
2004-08-29 00:48:42 +00:00
|
|
|
|
|
|
|
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:
|
2015-01-15 00:46:30 +00:00
|
|
|
case MDT_PNP_INFO:
|
2004-08-29 00:48:42 +00:00
|
|
|
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
|
2005-12-18 04:52:37 +00:00
|
|
|
__elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx)
|
2004-08-29 00:48:42 +00:00
|
|
|
{
|
|
|
|
Elf_Sym sym;
|
|
|
|
Elf_Addr base;
|
|
|
|
|
2016-01-12 02:17:39 +00:00
|
|
|
if (symidx >= ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym))
|
2004-08-29 00:48:42 +00:00
|
|
|
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);
|
|
|
|
}
|