load_elf.c revision 199767f8919635c4928607450d9e0abb932109ce
/*-
* 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
*
* 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.
*/
__FBSDID("$FreeBSD$");
#include <string.h>
#include <stand.h>
#define FREEBSD_ELF
#include <link.h>
#include "bootstrap.h"
#define ELF_TARG_CLASS ELFCLASS64
#define ELF_TARG_MACH EM_X86_64
#endif
typedef struct elf_file {
char *strtab;
int fd;
int kernel;
} *elf_file_t;
static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym);
static char *fake_modname(const char *name);
static int
{
int err;
/*
* Open the image, read and validate the ELF header
*/
return (EFTYPE);
return (errno);
return (ENOMEM);
}
goto error;
}
/* Is it ELF? */
goto error;
}
goto error;
}
return (0);
}
}
return (err);
}
/*
* Attempt to load the file (file) as an ELF module. It will be stored at
* (dest), and a pointer to a module structure describing the loaded object
* will be saved in (result).
*/
int
{
}
int
{
int err;
if (err != 0)
return (err);
/*
* Check to see what sort of module we are.
*/
#ifdef __powerpc__
/*
* Kernels can be ET_DYN, so just assume the first loaded object is the
* kernel. This assumption will be checked later.
*/
#endif
/* Looks like a kernel */
goto oerr;
}
/*
* Calculate destination address based on kernel entrypoint.
*
* For ARM, the destination address is independent of any values in the
* elf header (an ARM kernel can be loaded at any 2MB boundary), so we
* leave dest set to the value calculated by archsw.arch_loadaddr() and
* passed in to this function.
*/
#ifndef __arm__
#endif
goto oerr;
}
/* Looks like a kld module */
if (multiboot != 0) {
goto oerr;
}
goto oerr;
}
printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type);
goto oerr;
}
/* Looks OK, got ahead */
} else {
goto oerr;
}
else
/*
* Ok, we think we should handle this.
*/
fp = file_alloc();
goto out;
}
if (multiboot == 0)
else
#ifdef ELF_VERBOSE
#else
#endif
goto ioerr;
/* save exec header as metadata */
/* Load OK, return module pointer */
err = 0;
goto out;
oerr:
out:
return(err);
}
/*
* With the file (fd) open on the image, and (ehdr) containing
* the Elf header, load the image at (off)
*/
static int
{
int i;
u_int j;
char *shstr;
int ret;
int ndp;
int symstrindex;
int symtabindex;
ret = 0;
#if __ELF_WORD_SIZE == 64
#else
#endif
#elif defined(__powerpc__)
/*
* On the purely virtual memory machines like e500, the kernel is
* linked against its final VA range, which is most often not
* available at the loader stage, but only after kernel initializes
* and completes its VM settings. In such cases we cannot use p_vaddr
* field directly to load ELF segments, but put them at some
* 'load-time' locations.
*/
if (off & 0xf0000000u) {
/*
* 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;
#ifdef ELF_VERBOSE
#endif
} else
off = 0;
/*
* 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.
*/
#ifdef ELF_VERBOSE
#endif
#else
off = 0; /* other archs use direct mapped kernels */
#endif
}
/* use entry address from header */
}
goto out;
}
/* We want to load PT_LOAD segments only.. */
continue;
#ifdef ELF_VERBOSE
printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
} else {
printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
}
#else
} else {
printf(" ");
}
#endif
fpcopy = 0;
} else {
}
}
"_loadimage: read failed\n");
goto out;
}
} else {
"_loadimage: read failed\n");
goto out;
}
}
}
/* clear space from oversized segments; eg: bss */
#ifdef ELF_VERBOSE
printf(" (bss: 0x%lx-0x%lx)",
} else {
printf(" (bss: 0x%lx-0x%lx)",
}
#endif
} else {
}
}
#ifdef ELF_VERBOSE
printf("\n");
#endif
if (lastaddr == 0 ||
} else {
if (lastaddr == 0 ||
}
}
/*
* 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.
*/
goto nosyms;
"_loadimage: failed to read section headers");
goto nosyms;
}
/*
* 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.
*/
if (chunk) {
if (shstr) {
continue;
&ctors);
&size);
break;
}
}
}
/*
* Now load any symbols.
*/
symtabindex = -1;
symstrindex = -1;
continue;
continue;
break;
}
}
continue; /* alread loaded in a PT_LOAD above */
/* Save it for loading below */
symtabindex = i;
}
if (symtabindex < 0 || symstrindex < 0)
goto nosyms;
/* Ok, committed to a load. */
#ifndef ELF_VERBOSE
printf("syms=[");
#endif
for (i = symtabindex; i >= 0; i = symstrindex) {
#ifdef ELF_VERBOSE
char *secname;
case SHT_SYMTAB: /* Symbol table */
secname = "symtab";
break;
case SHT_STRTAB: /* String table */
secname = "strtab";
break;
default:
secname = "WHOA!!";
break;
}
#endif
#ifdef ELF_VERBOSE
#else
if (i == symstrindex)
printf("+");
#endif
ssym = 0;
goto nosyms;
}
printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! (%ju != %ju)", (uintmax_t)result,
ssym = 0;
goto nosyms;
}
/* Reset offsets relative to ssym */
if (i == symtabindex)
symtabindex = -1;
else if (i == symstrindex)
symstrindex = -1;
}
#ifndef ELF_VERBOSE
printf("]");
#endif
printf("\n");
break;
}
}
goto out;
if (ndp == 0)
goto out;
goto out;
else
for (i = 0; i < ndp; i++) {
break;
case DT_HASH:
break;
case DT_STRTAB:
break;
case DT_STRSZ:
break;
case DT_SYMTAB:
break;
case DT_REL:
break;
case DT_RELSZ:
break;
case DT_RELA:
break;
case DT_RELASZ:
break;
default:
break;
}
}
goto out;
return 0;
return ENOENT;
goto out;
goto out;
out:
if (dp)
if (shdr)
return ret;
}
static char invalid_name[] = "bad";
char *
fake_modname(const char *name)
{
char *fp;
if (sp)
sp++;
else
if (ep) {
sp = invalid_name;
}
} else
return NULL;
return fp;
}
struct mod_metadata64 {
int md_version; /* structure version MDTV_* */
int md_type; /* type of entry MDT_* */
};
#endif
struct mod_metadata32 {
int md_version; /* structure version MDTV_* */
int md_type; /* type of entry MDT_* */
};
#endif
int
{
int err, i, j;
if (err != 0)
goto out;
goto out;
}
goto out;
}
/* Load shstrtab. */
"load_modmetadata: unable to load shstrtab\n");
goto out;
}
/* Find set_modmetadata_set and data sections. */
"set_modmetadata_set") == 0) {
}
}
}
"load_modmetadata: unable to find set_modmetadata_set or data sections\n");
goto out;
}
/* Load set_modmetadata_set into memory */
if (err != 0) {
"load_modmetadata: unable to load set_modmetadata_set: %d\n", err);
goto out;
}
/* Load data sections into memory. */
if (err != 0) {
"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.
*/
if (err != 0) {
"load_modmetadata: unable to load data: %d\n", err);
goto out;
}
if (err != 0) {
"load_modmetadata: unable to parse metadata: %d\n", err);
goto out;
}
out:
return (err);
}
int
{
struct mod_metadata md;
struct mod_metadata64 md64;
struct mod_metadata32 md32;
#endif
struct mod_depend *mdepend;
struct mod_version mver;
char *s;
Elf_Addr v, p;
modcnt = 0;
p = p_start;
while (p < p_end) {
COPYOUT(p, &v, sizeof(v));
if (error == EOPNOTSUPP)
else if (error != 0)
return (error);
if (error == EOPNOTSUPP) {
} else if (error != 0)
return (error);
if (error == EOPNOTSUPP) {
} else if (error != 0)
return (error);
#else
if (error == EOPNOTSUPP) {
} else if (error != 0)
return (error);
#endif
p += sizeof(Elf_Addr);
case MDT_DEPEND:
break;
return ENOMEM;
free(s);
break;
case MDT_VERSION:
free(s);
modcnt++;
break;
}
}
if (modcnt == 0) {
free(s);
}
return 0;
}
static unsigned long
{
const unsigned char *p = (const unsigned char *) name;
unsigned long h = 0;
unsigned long g;
while (*p != '\0') {
h = (h << 4) + *p++;
if ((g = h & 0xf0000000) != 0)
h ^= g >> 24;
h &= ~g;
}
return h;
}
static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n";
int
{
char *strp;
unsigned long hash;
return ENOENT;
}
return ENOENT;
}
return 0;
}
return ENOENT;
}
}
return ENOENT;
}
/*
* Apply any intra-module relocations to the value. p is the load address
* the image in-place, because this is done by kern_linker later on.
*
* Returns EOPNOTSUPP if no relocation method is supplied.
*/
static int
{
size_t n;
Elf_Rela a;
Elf_Rel r;
int error;
(void)mp;
/*
* The kernel is already relocated, but we still want to apply
* offset adjustments.
*/
return (EOPNOTSUPP);
if (error != 0)
return (error);
}
if (error != 0)
return (error);
}
return (0);
}
static Elf_Addr
{
/* Symbol lookup by index not required here. */
return (0);
}