1998-09-30 19:38:26 +00:00
|
|
|
/*-
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2003-08-25 23:30:41 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
1998-09-30 19:38:26 +00:00
|
|
|
#include <sys/param.h>
|
2018-01-29 09:24:28 +00:00
|
|
|
#include <sys/endian.h>
|
1998-09-30 19:38:26 +00:00
|
|
|
#include <sys/exec.h>
|
1998-10-09 23:18:43 +00:00
|
|
|
#include <sys/linker.h>
|
2000-05-01 17:41:25 +00:00
|
|
|
#include <sys/module.h>
|
2006-11-02 17:52:43 +00:00
|
|
|
#include <sys/stdint.h>
|
1998-09-30 19:38:26 +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>
|
1998-09-30 19:38:26 +00:00
|
|
|
|
|
|
|
#include "bootstrap.h"
|
|
|
|
|
2000-05-01 17:41:25 +00:00
|
|
|
#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l)
|
|
|
|
|
2003-05-01 03:56:30 +00:00
|
|
|
#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
|
2000-05-01 17:41:25 +00:00
|
|
|
|
|
|
|
typedef struct elf_file {
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
2018-03-13 16:33:00 +00:00
|
|
|
uint64_t off;
|
2000-05-01 17:41:25 +00:00
|
|
|
} *elf_file_t;
|
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef,
|
2018-03-13 16:33:00 +00:00
|
|
|
uint64_t loadaddr);
|
2018-02-21 19:42:54 +00:00
|
|
|
static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef,
|
|
|
|
const char* name, Elf_Sym* sym);
|
2004-08-28 23:03:05 +00:00
|
|
|
static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
|
|
|
|
Elf_Addr p, void *val, size_t len);
|
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
|
|
|
static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef,
|
2015-01-17 08:09:07 +00:00
|
|
|
Elf_Addr p_start, Elf_Addr p_end);
|
2004-08-28 23:03:05 +00:00
|
|
|
static symaddr_fn __elfN(symaddr);
|
2000-08-03 09:14:02 +00:00
|
|
|
static char *fake_modname(const char *name);
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2003-05-01 03:56:30 +00:00
|
|
|
const char *__elfN(kerneltype) = "elf kernel";
|
|
|
|
const char *__elfN(moduletype) = "elf module";
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-03-13 16:33:00 +00:00
|
|
|
uint64_t __elfN(relocation_offset) = 0;
|
2008-02-23 18:33:50 +00:00
|
|
|
|
2018-02-09 10:20:16 +00:00
|
|
|
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
|
|
|
|
|
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
|
|
|
static int
|
|
|
|
__elfN(load_elf_header)(char *filename, elf_file_t ef)
|
|
|
|
{
|
|
|
|
ssize_t bytes_read;
|
|
|
|
Elf_Ehdr *ehdr;
|
2018-02-21 19:42:54 +00:00
|
|
|
int err;
|
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
|
|
|
|
|
|
|
/*
|
2018-02-21 19:42:54 +00:00
|
|
|
* Open the image, read and validate the ELF header
|
|
|
|
*/
|
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
|
|
|
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);
|
|
|
|
}
|
|
|
|
bytes_read = read(ef->fd, 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;
|
|
|
|
}
|
2018-01-29 09:24:28 +00:00
|
|
|
|
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
|
|
|
if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */
|
|
|
|
ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
|
2018-01-29 09:24:28 +00:00
|
|
|
ehdr->e_ident[EI_VERSION] != EV_CURRENT) /* Version ? */ {
|
|
|
|
err = EFTYPE;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-02-09 10:20:16 +00:00
|
|
|
err = elf_header_convert(ehdr);
|
|
|
|
if (err)
|
|
|
|
goto error;
|
2018-01-29 09:24:28 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
if (ehdr->e_version != EV_CURRENT || ehdr->e_machine != ELF_TARG_MACH) {
|
|
|
|
/* Machine ? */
|
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
|
|
|
err = EFTYPE;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
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 error;
|
|
|
|
}
|
|
|
|
#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
|
|
|
return (0);
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (ef->firstpage != NULL) {
|
|
|
|
free(ef->firstpage);
|
|
|
|
ef->firstpage = NULL;
|
|
|
|
}
|
|
|
|
if (ef->fd != -1) {
|
|
|
|
close(ef->fd);
|
|
|
|
ef->fd = -1;
|
|
|
|
}
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
1998-09-30 19:38:26 +00:00
|
|
|
/*
|
|
|
|
* 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(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result)
|
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
|
|
|
{
|
|
|
|
return (__elfN(loadfile_raw)(filename, dest, result, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2018-03-13 16:33:00 +00:00
|
|
|
__elfN(loadfile_raw)(char *filename, uint64_t dest,
|
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
|
|
|
struct preloaded_file **result, int multiboot)
|
1998-09-30 19:38:26 +00:00
|
|
|
{
|
2018-02-21 19:42:54 +00:00
|
|
|
struct preloaded_file *fp, *kfp;
|
|
|
|
struct elf_file ef;
|
|
|
|
Elf_Ehdr *ehdr;
|
|
|
|
int err;
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
fp = NULL;
|
|
|
|
bzero(&ef, sizeof(struct elf_file));
|
|
|
|
ef.fd = -1;
|
2008-02-23 18:33:50 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
err = __elfN(load_elf_header)(filename, &ef);
|
|
|
|
if (err != 0)
|
|
|
|
return (err);
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
ehdr = ef.ehdr;
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
/*
|
|
|
|
* Check to see what sort of module we are.
|
|
|
|
*/
|
|
|
|
kfp = file_findfile(NULL, __elfN(kerneltype));
|
2015-01-31 07:22:29 +00:00
|
|
|
#ifdef __powerpc__
|
2018-02-21 19:42:54 +00:00
|
|
|
/*
|
|
|
|
* Kernels can be ET_DYN, so just assume the first loaded object is the
|
|
|
|
* kernel. This assumption will be checked later.
|
2015-01-31 07:22:29 +00:00
|
|
|
*/
|
2018-02-21 19:42:54 +00:00
|
|
|
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.
|
|
|
|
*/
|
An ARM kernel can be loaded at any 2MB boundary, make ubldr aware of that.
Previously, ubldr would use the virtual addresses in the elf headers by
masking off the high bits and assuming the result was a physical address
where the kernel should be loaded. That would sometimes discard
significant bits of the physical address, but the effects of that were
undone by archsw copy code that would find a large block of memory and
apply an offset to the source/dest copy addresses. The result was that
things were loaded at a different physical address than requested by the
higher code layers, but that worked because other adjustments were applied
later (such as when jumping to the entry point). Very confusing, and
somewhat fragile.
Now the archsw copy routines are just simple copies, and instead
archsw.arch_loadaddr is implemented to choose a load address. The new
routine uses some of the code from the old offset-translation routine to
find the largest block of ram, but it excludes ubldr itself from that
range, and also excludes If ubldr splits the largest block of ram in
two, the kernel is loaded into the bottom of whichever resulting block is
larger.
As part of eliminating ubldr itself from the ram ranges, export the heap
start/end addresses in a pair of new global variables.
This change means that the virtual addresses in the arm kernel elf headers
now have no meaning at all, except for the entry point address. There is
an implicit assumption that the entry point is in the first text page, and
that the address in the the header can be turned into an offset by masking
it with PAGE_MASK. In the future we can link all arm kernels at a virtual
address of 0xC0000000 with no need to use any low-order part of the
address to influence where in ram the kernel gets loaded.
2015-05-17 19:59:05 +00:00
|
|
|
#ifndef __arm__
|
2018-02-21 19:42:54 +00:00
|
|
|
if (ehdr->e_type == ET_EXEC)
|
|
|
|
dest = (ehdr->e_entry & ~PAGE_MASK);
|
An ARM kernel can be loaded at any 2MB boundary, make ubldr aware of that.
Previously, ubldr would use the virtual addresses in the elf headers by
masking off the high bits and assuming the result was a physical address
where the kernel should be loaded. That would sometimes discard
significant bits of the physical address, but the effects of that were
undone by archsw copy code that would find a large block of memory and
apply an offset to the source/dest copy addresses. The result was that
things were loaded at a different physical address than requested by the
higher code layers, but that worked because other adjustments were applied
later (such as when jumping to the entry point). Very confusing, and
somewhat fragile.
Now the archsw copy routines are just simple copies, and instead
archsw.arch_loadaddr is implemented to choose a load address. The new
routine uses some of the code from the old offset-translation routine to
find the largest block of ram, but it excludes ubldr itself from that
range, and also excludes If ubldr splits the largest block of ram in
two, the kernel is loaded into the bottom of whichever resulting block is
larger.
As part of eliminating ubldr itself from the ram ranges, export the heap
start/end addresses in a pair of new global variables.
This change means that the virtual addresses in the arm kernel elf headers
now have no meaning at all, except for the entry point address. There is
an implicit assumption that the entry point is in the first text page, and
that the address in the the header can be turned into an offset by masking
it with PAGE_MASK. In the future we can link all arm kernels at a virtual
address of 0xC0000000 with no need to use any low-order part of the
address to influence where in ram the kernel gets loaded.
2015-05-17 19:59:05 +00:00
|
|
|
#endif
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
2015-01-31 07:22:29 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
} 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;
|
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
|
|
|
goto oerr;
|
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
|
|
|
|
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;
|
1998-09-30 19:38:26 +00:00
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
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");
|
1998-09-30 19:38:26 +00:00
|
|
|
|
1998-10-13 09:25:27 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
if (ef.kernel)
|
|
|
|
printf("%s entry at 0x%jx\n", filename,
|
|
|
|
(uintmax_t)ehdr->e_entry);
|
1998-10-14 00:41:17 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
printf("%s ", filename);
|
1998-10-13 09:25:27 +00:00
|
|
|
#endif
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ioerr:
|
|
|
|
err = EIO;
|
|
|
|
oerr:
|
|
|
|
file_discard(fp);
|
|
|
|
out:
|
|
|
|
if (ef.firstpage)
|
|
|
|
free(ef.firstpage);
|
|
|
|
if (ef.fd != -1)
|
|
|
|
close(ef.fd);
|
|
|
|
return (err);
|
1998-09-30 19:38:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* With the file (fd) open on the image, and (ehdr) containing
|
1998-10-09 23:18:43 +00:00
|
|
|
* the Elf header, load the image at (off)
|
1998-09-30 19:38:26 +00:00
|
|
|
*/
|
|
|
|
static int
|
2018-03-13 16:33:00 +00:00
|
|
|
__elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off)
|
1998-09-30 19:38:26 +00:00
|
|
|
{
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
2019-09-06 02:28:03 +00:00
|
|
|
#ifdef __powerpc__
|
|
|
|
if (ef->kernel) {
|
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
if (ehdr->e_type == ET_EXEC) {
|
2019-09-06 02:28:03 +00:00
|
|
|
#endif
|
2011-06-30 16:08:56 +00:00
|
|
|
#if defined(__i386__) || defined(__amd64__)
|
2003-05-01 03:56:30 +00:00
|
|
|
#if __ELF_WORD_SIZE == 64
|
2018-02-21 19:42:54 +00:00
|
|
|
/* x86_64 relocates after locore */
|
|
|
|
off = - (off & 0xffffffffff000000ull);
|
2003-05-01 03:56:30 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
/* i386 relocates after locore */
|
|
|
|
off = - (off & 0xff000000u);
|
2003-05-01 03:56:30 +00:00
|
|
|
#endif
|
2008-02-23 18:33:50 +00:00
|
|
|
#elif defined(__powerpc__)
|
2018-02-21 19:42:54 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2019-09-06 02:28:03 +00:00
|
|
|
}
|
|
|
|
ehdr->e_entry += off;
|
2008-02-23 18:33:50 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2019-09-06 02:28:03 +00:00
|
|
|
printf("Converted entry 0x%jx\n", (uintmax_t)ehdr->e_entry);
|
2008-02-23 18:33:50 +00:00
|
|
|
#endif
|
2016-02-09 09:39:30 +00:00
|
|
|
#elif defined(__arm__) && !defined(EFI)
|
2018-02-21 19:42:54 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2008-10-14 10:11:14 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2019-05-21 00:46:47 +00:00
|
|
|
printf("ehdr->e_entry 0x%jx, va<->pa off %llx\n",
|
2019-05-20 19:21:35 +00:00
|
|
|
(uintmax_t)ehdr->e_entry, off);
|
2008-10-14 10:11:14 +00:00
|
|
|
#endif
|
1998-09-30 19:38:26 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
off = 0; /* other archs use direct mapped kernels */
|
1998-09-30 19:38:26 +00:00
|
|
|
#endif
|
2018-02-21 19:42:54 +00:00
|
|
|
}
|
|
|
|
ef->off = off;
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
if (ef->kernel)
|
|
|
|
__elfN(relocation_offset) = off;
|
2015-01-31 18:42:51 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
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);
|
1998-10-17 03:06:38 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
for (i = 0; i < ehdr->e_phnum; i++) {
|
|
|
|
if (elf_program_header_convert(ehdr, phdr))
|
|
|
|
continue;
|
2018-01-29 09:24:28 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
/* We want to load PT_LOAD segments only.. */
|
|
|
|
if (phdr[i].p_type != PT_LOAD)
|
|
|
|
continue;
|
1998-09-30 19:38:26 +00:00
|
|
|
|
1998-10-12 09:13:50 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
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));
|
1998-10-12 09:13:50 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
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(" ");
|
|
|
|
}
|
1998-10-12 09:13:50 +00:00
|
|
|
#endif
|
2018-02-21 19:42:54 +00:00
|
|
|
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(ef->fd, 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) {
|
1998-10-12 09:13:50 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
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));
|
1998-10-12 09:13:50 +00:00
|
|
|
#endif
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz,
|
|
|
|
phdr[i].p_memsz - phdr[i].p_filesz);
|
|
|
|
}
|
1998-10-12 09:13:50 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
printf("\n");
|
1998-10-12 09:13:50 +00:00
|
|
|
#endif
|
1998-09-30 19:38:26 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
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(ef->fd, 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(ef->fd, 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);
|
|
|
|
}
|
2014-10-20 17:04:03 +00:00
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
1998-10-02 08:04:56 +00:00
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
if (symtabindex < 0 || symstrindex < 0)
|
|
|
|
goto nosyms;
|
|
|
|
|
|
|
|
/* Ok, committed to a load. */
|
1998-10-12 09:13:50 +00:00
|
|
|
#ifndef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
printf("syms=[");
|
1998-10-12 09:13:50 +00:00
|
|
|
#endif
|
2018-02-21 19:42:54 +00:00
|
|
|
ssym = lastaddr;
|
|
|
|
for (i = symtabindex; i >= 0; i = symstrindex) {
|
1998-10-12 09:13:50 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
|
|
|
}
|
1998-10-12 09:13:50 +00:00
|
|
|
#endif
|
2018-02-21 19:42:54 +00:00
|
|
|
size = shdr[i].sh_size;
|
2018-01-29 09:24:28 +00:00
|
|
|
#if defined(__powerpc__)
|
|
|
|
#if __ELF_WORD_SIZE == 64
|
2018-02-21 19:42:54 +00:00
|
|
|
size = htobe64(size);
|
2018-01-29 09:24:28 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
size = htobe32(size);
|
2018-01-29 09:24:28 +00:00
|
|
|
#endif
|
|
|
|
#endif
|
2018-02-03 01:23:48 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
archsw.arch_copyin(&size, lastaddr, sizeof(size));
|
|
|
|
lastaddr += sizeof(size);
|
1998-10-02 08:04:56 +00:00
|
|
|
|
1998-10-12 09:13:50 +00:00
|
|
|
#ifdef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
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));
|
1998-10-12 09:13:50 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
if (i == symstrindex)
|
|
|
|
printf("+");
|
|
|
|
printf("0x%lx+0x%lx", (long)sizeof(size), (long)size);
|
1998-10-12 09:13:50 +00:00
|
|
|
#endif
|
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
if (lseek(ef->fd, (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(ef->fd, 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;
|
1998-10-02 08:04:56 +00:00
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
esym = lastaddr;
|
1998-10-12 09:13:50 +00:00
|
|
|
#ifndef ELF_VERBOSE
|
2018-02-21 19:42:54 +00:00
|
|
|
printf("]");
|
1998-10-12 09:13:50 +00:00
|
|
|
#endif
|
1998-10-02 08:04:56 +00:00
|
|
|
|
2018-01-29 09:24:28 +00:00
|
|
|
#if defined(__powerpc__)
|
|
|
|
/* On PowerPC we always need to provide BE data to the kernel */
|
|
|
|
#if __ELF_WORD_SIZE == 64
|
2018-02-21 19:42:54 +00:00
|
|
|
ssym = htobe64((uint64_t)ssym);
|
|
|
|
esym = htobe64((uint64_t)esym);
|
2018-01-29 09:24:28 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
ssym = htobe32((uint32_t)ssym);
|
|
|
|
esym = htobe32((uint32_t)esym);
|
2018-01-29 09:24:28 +00:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
|
|
|
|
file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym);
|
1998-10-02 08:04:56 +00:00
|
|
|
|
|
|
|
nosyms:
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
|
|
|
}
|
1998-10-09 23:18:43 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */
|
|
|
|
goto out;
|
1998-10-09 23:18:43 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
|
|
|
}
|
1998-10-09 23:18:43 +00:00
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
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
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set",
|
|
|
|
&sym) != 0)
|
|
|
|
return 0;
|
|
|
|
p_start = sym.st_value + ef->off;
|
|
|
|
if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set",
|
|
|
|
&sym) != 0)
|
|
|
|
return ENOENT;
|
|
|
|
p_end = sym.st_value + ef->off;
|
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
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0)
|
|
|
|
goto out;
|
2000-05-01 17:41:25 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
if (ef->kernel) /* kernel must not depend on anything */
|
|
|
|
goto out;
|
1998-10-09 23:18:43 +00:00
|
|
|
|
1998-09-30 19:38:26 +00:00
|
|
|
out:
|
2018-02-21 19:42:54 +00:00
|
|
|
if (dp)
|
|
|
|
free(dp);
|
|
|
|
if (shdr)
|
|
|
|
free(shdr);
|
|
|
|
return ret;
|
1998-09-30 19:38:26 +00:00
|
|
|
}
|
2000-05-01 17:41:25 +00:00
|
|
|
|
|
|
|
static char invalid_name[] = "bad";
|
2001-09-11 01:09:24 +00:00
|
|
|
|
2000-05-01 17:41:25 +00:00
|
|
|
char *
|
2001-06-19 06:48:58 +00:00
|
|
|
fake_modname(const char *name)
|
|
|
|
{
|
2018-02-21 19:42:54 +00:00
|
|
|
const char *sp, *ep;
|
|
|
|
char *fp;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
sp = strrchr(name, '/');
|
|
|
|
if (sp)
|
|
|
|
sp++;
|
|
|
|
else
|
|
|
|
sp = name;
|
2019-03-20 16:24:47 +00:00
|
|
|
|
|
|
|
ep = strrchr(sp, '.');
|
|
|
|
if (ep == NULL) {
|
|
|
|
ep = sp + strlen(sp);
|
|
|
|
}
|
|
|
|
if (ep == sp) {
|
|
|
|
sp = invalid_name;
|
|
|
|
ep = invalid_name + sizeof(invalid_name) - 1;
|
|
|
|
}
|
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
len = ep - sp;
|
|
|
|
fp = malloc(len + 1);
|
|
|
|
if (fp == NULL)
|
|
|
|
return NULL;
|
|
|
|
memcpy(fp, sp, len);
|
|
|
|
fp[len] = '\0';
|
|
|
|
return fp;
|
2000-05-01 17:41:25 +00:00
|
|
|
}
|
|
|
|
|
2012-09-08 20:00:31 +00:00
|
|
|
#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
|
2003-05-12 05:48:09 +00:00
|
|
|
struct mod_metadata64 {
|
2018-02-21 19:42:54 +00:00
|
|
|
int md_version; /* structure version MDTV_* */
|
2003-05-12 05:48:09 +00:00
|
|
|
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 */
|
2003-05-12 05:48:09 +00:00
|
|
|
};
|
|
|
|
#endif
|
2014-11-11 22:03:11 +00:00
|
|
|
#if defined(__amd64__) && __ELF_WORD_SIZE == 32
|
|
|
|
struct mod_metadata32 {
|
2018-02-21 19:42:54 +00:00
|
|
|
int md_version; /* structure version MDTV_* */
|
2014-11-11 22:03:11 +00:00
|
|
|
int md_type; /* type of entry MDT_* */
|
2018-03-13 16:33:00 +00:00
|
|
|
uint32_t md_data; /* specific data */
|
|
|
|
uint32_t md_cval; /* common string label */
|
2014-11-11 22:03:11 +00:00
|
|
|
};
|
|
|
|
#endif
|
2003-05-12 05:48:09 +00:00
|
|
|
|
2000-05-01 17:41:25 +00:00
|
|
|
int
|
2018-03-13 16:33:00 +00:00
|
|
|
__elfN(load_modmetadata)(struct preloaded_file *fp, uint64_t dest)
|
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
|
|
|
{
|
|
|
|
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;
|
2015-01-17 08:09:07 +00:00
|
|
|
Elf_Addr p_start, p_end;
|
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
|
|
|
|
|
|
|
bzero(&ef, sizeof(struct elf_file));
|
|
|
|
ef.fd = -1;
|
|
|
|
|
|
|
|
err = __elfN(load_elf_header)(fp->f_name, &ef);
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
|
2015-01-31 18:42:51 +00:00
|
|
|
if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) {
|
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
|
|
|
ef.kernel = 1;
|
|
|
|
} else if (ef.ehdr->e_type != ET_DYN) {
|
|
|
|
err = EFTYPE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
Fix theoretical integer overflow issues. If the product here is
greater than 2^31-1, then the result will be huge. This is unlikely,
as we don't support that many sections, but out of an abundace of
caution cast to size_t so the multiplication won't overflow
mysteriously when size_t is larger than 32-bits. The resulting code
may be a smidge larger, but this isn't super-space critical code.
CID: 1194216, 1194217, 1194222, 1194223, 1265018, 1265019,1265020,
1265021
Sponsored by: Netflix
2017-11-24 05:00:25 +00:00
|
|
|
size = (size_t)ef.ehdr->e_shnum * (size_t)ef.ehdr->e_shentsize;
|
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
|
|
|
shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size);
|
|
|
|
if (shdr == NULL) {
|
|
|
|
err = ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Load shstrtab. */
|
|
|
|
shstrtab = alloc_pread(ef.fd, 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(ef.fd, 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(ef.fd, 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(ef.fd, 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)
|
|
|
|
close(ef.fd);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef,
|
2015-01-17 08:09:07 +00:00
|
|
|
Elf_Addr p_start, Elf_Addr p_end)
|
2001-06-19 06:48:58 +00:00
|
|
|
{
|
2018-02-21 19:42:54 +00:00
|
|
|
struct mod_metadata md;
|
2012-09-08 20:00:31 +00:00
|
|
|
#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
|
2018-02-21 19:42:54 +00:00
|
|
|
struct mod_metadata64 md64;
|
2014-11-11 22:03:11 +00:00
|
|
|
#elif defined(__amd64__) && __ELF_WORD_SIZE == 32
|
2018-02-21 19:42:54 +00:00
|
|
|
struct mod_metadata32 md32;
|
2003-05-12 05:48:09 +00:00
|
|
|
#endif
|
2018-02-21 19:42:54 +00:00
|
|
|
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);
|
2012-09-08 20:00:31 +00:00
|
|
|
#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
2014-11-11 22:03:11 +00:00
|
|
|
#elif defined(__amd64__) && __ELF_WORD_SIZE == 32
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
2003-05-12 05:48:09 +00:00
|
|
|
#else
|
2018-02-21 19:42:54 +00:00
|
|
|
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);
|
2003-05-12 05:48:09 +00:00
|
|
|
#endif
|
2018-02-21 19:42:54 +00:00
|
|
|
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);
|
2000-05-01 17:41:25 +00:00
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
return 0;
|
2000-05-01 17:41:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long
|
|
|
|
elf_hash(const char *name)
|
|
|
|
{
|
2018-02-21 19:42:54 +00:00
|
|
|
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;
|
2000-05-01 17:41:25 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE)
|
|
|
|
"_lookup_symbol: corrupt symbol table\n";
|
2000-05-01 17:41:25 +00:00
|
|
|
int
|
2018-02-21 19:42:54 +00:00
|
|
|
__elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef,
|
|
|
|
const char* name, Elf_Sym *symp)
|
2000-05-01 17:41:25 +00:00
|
|
|
{
|
2018-02-21 19:42:54 +00:00
|
|
|
Elf_Hashelt symnum;
|
|
|
|
Elf_Sym sym;
|
|
|
|
char *strp;
|
|
|
|
unsigned long hash;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2000-05-01 17:41:25 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
COPYOUT(ef->symtab + symnum, &sym, sizeof(sym));
|
|
|
|
if (sym.st_name == 0) {
|
|
|
|
printf(__elfN(bad_symtable));
|
|
|
|
return ENOENT;
|
|
|
|
}
|
2000-05-01 17:41:25 +00:00
|
|
|
|
2018-02-21 19:42:54 +00:00
|
|
|
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) == STT_FUNC)) {
|
|
|
|
*symp = sym;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
free(strp);
|
|
|
|
COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum));
|
2000-05-01 17:41:25 +00:00
|
|
|
}
|
2018-02-21 19:42:54 +00:00
|
|
|
return ENOENT;
|
2000-05-01 17:41:25 +00:00
|
|
|
}
|
2003-01-21 05:46:46 +00:00
|
|
|
|
|
|
|
/*
|
2004-08-28 23:03:05 +00:00
|
|
|
* Apply any intra-module relocations to the value. p is the load address
|
2003-01-21 05:46:46 +00:00
|
|
|
* 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.
|
2004-08-28 23:03:05 +00:00
|
|
|
*
|
|
|
|
* Returns EOPNOTSUPP if no relocation method is supplied.
|
2003-01-21 05:46:46 +00:00
|
|
|
*/
|
2004-08-28 23:03:05 +00:00
|
|
|
static int
|
2003-05-01 03:56:30 +00:00
|
|
|
__elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
|
2004-08-28 23:03:05 +00:00
|
|
|
Elf_Addr p, void *val, size_t len)
|
2003-01-21 05:46:46 +00:00
|
|
|
{
|
|
|
|
size_t n;
|
2004-08-28 23:03:05 +00:00
|
|
|
Elf_Rela a;
|
|
|
|
Elf_Rel r;
|
|
|
|
int error;
|
2003-01-21 05:46:46 +00:00
|
|
|
|
2004-08-28 23:03:05 +00:00
|
|
|
/*
|
|
|
|
* The kernel is already relocated, but we still want to apply
|
|
|
|
* offset adjustments.
|
|
|
|
*/
|
|
|
|
if (ef->kernel)
|
|
|
|
return (EOPNOTSUPP);
|
2003-01-21 05:46:46 +00:00
|
|
|
|
2004-08-28 23:03:05 +00:00
|
|
|
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);
|
2003-01-21 05:46:46 +00:00
|
|
|
}
|
2004-08-28 23:03:05 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Elf_Addr
|
2005-12-18 04:52:37 +00:00
|
|
|
__elfN(symaddr)(struct elf_file *ef, Elf_Size symidx)
|
2004-08-28 23:03:05 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
/* Symbol lookup by index not required here. */
|
|
|
|
return (0);
|
2003-01-21 05:46:46 +00:00
|
|
|
}
|