1*de11c9b6SToomas Soome /*
222028508SToomas Soome * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
322028508SToomas Soome * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
422028508SToomas Soome * All rights reserved.
522028508SToomas Soome *
622028508SToomas Soome * Redistribution and use in source and binary forms, with or without
722028508SToomas Soome * modification, are permitted provided that the following conditions
822028508SToomas Soome * are met:
922028508SToomas Soome * 1. Redistributions of source code must retain the above copyright
1022028508SToomas Soome * notice, this list of conditions and the following disclaimer.
1122028508SToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
1222028508SToomas Soome * notice, this list of conditions and the following disclaimer in the
1322028508SToomas Soome * documentation and/or other materials provided with the distribution.
1422028508SToomas Soome *
1522028508SToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1622028508SToomas Soome * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1722028508SToomas Soome * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1822028508SToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1922028508SToomas Soome * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2022028508SToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2122028508SToomas Soome * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2222028508SToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2322028508SToomas Soome * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2422028508SToomas Soome * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2522028508SToomas Soome * SUCH DAMAGE.
2622028508SToomas Soome */
2722028508SToomas Soome
2822028508SToomas Soome #include <sys/cdefs.h>
2922028508SToomas Soome
3022028508SToomas Soome #include <sys/param.h>
3122028508SToomas Soome #include <sys/exec.h>
3222028508SToomas Soome #include <sys/linker.h>
3322028508SToomas Soome #include <sys/module.h>
3422028508SToomas Soome #include <sys/stdint.h>
3522028508SToomas Soome #include <string.h>
3622028508SToomas Soome #include <machine/elf.h>
3722028508SToomas Soome #include <stand.h>
3822028508SToomas Soome #define FREEBSD_ELF
3922028508SToomas Soome #include <link.h>
4022028508SToomas Soome
4122028508SToomas Soome #include "bootstrap.h"
4222028508SToomas Soome
4322028508SToomas Soome #define COPYOUT(s, d, l) archsw.arch_copyout((vm_offset_t)(s), d, l)
4422028508SToomas Soome
4522028508SToomas Soome #if defined(__i386__) && __ELF_WORD_SIZE == 64
4622028508SToomas Soome #undef ELF_TARG_CLASS
4722028508SToomas Soome #undef ELF_TARG_MACH
4822028508SToomas Soome #define ELF_TARG_CLASS ELFCLASS64
4922028508SToomas Soome #define ELF_TARG_MACH EM_X86_64
5022028508SToomas Soome #endif
5122028508SToomas Soome
5222028508SToomas Soome typedef struct elf_file {
5322028508SToomas Soome Elf_Phdr *ph;
5422028508SToomas Soome Elf_Ehdr *ehdr;
5522028508SToomas Soome Elf_Sym *symtab;
5622028508SToomas Soome Elf_Hashelt *hashtab;
5722028508SToomas Soome Elf_Hashelt nbuckets;
5822028508SToomas Soome Elf_Hashelt nchains;
5922028508SToomas Soome Elf_Hashelt *buckets;
6022028508SToomas Soome Elf_Hashelt *chains;
6122028508SToomas Soome Elf_Rel *rel;
6222028508SToomas Soome size_t relsz;
6322028508SToomas Soome Elf_Rela *rela;
6422028508SToomas Soome size_t relasz;
6522028508SToomas Soome char *strtab;
6622028508SToomas Soome size_t strsz;
6722028508SToomas Soome int fd;
6822028508SToomas Soome caddr_t firstpage;
6922028508SToomas Soome size_t firstlen;
7022028508SToomas Soome int kernel;
71*de11c9b6SToomas Soome uint64_t off;
7222028508SToomas Soome } *elf_file_t;
7322028508SToomas Soome
74*de11c9b6SToomas Soome static int __elfN(loadimage)(struct preloaded_file *, elf_file_t, uint64_t);
75*de11c9b6SToomas Soome static int __elfN(lookup_symbol)(struct preloaded_file *, elf_file_t,
76*de11c9b6SToomas Soome const char *, Elf_Sym *);
77*de11c9b6SToomas Soome static int __elfN(reloc_ptr)(struct preloaded_file *, elf_file_t,
78*de11c9b6SToomas Soome Elf_Addr, void *, size_t);
79*de11c9b6SToomas Soome static int __elfN(parse_modmetadata)(struct preloaded_file *, elf_file_t,
80*de11c9b6SToomas Soome Elf_Addr, Elf_Addr);
8122028508SToomas Soome static symaddr_fn __elfN(symaddr);
82*de11c9b6SToomas Soome static char *fake_modname(const char *);
8322028508SToomas Soome
8422028508SToomas Soome const char *__elfN(kerneltype) = "elf kernel";
8522028508SToomas Soome const char *__elfN(moduletype) = "elf module";
8622028508SToomas Soome
87*de11c9b6SToomas Soome uint64_t __elfN(relocation_offset) = 0;
8822028508SToomas Soome
8922028508SToomas Soome static int
__elfN(load_elf_header)9022028508SToomas Soome __elfN(load_elf_header)(char *filename, elf_file_t ef)
9122028508SToomas Soome {
9222028508SToomas Soome ssize_t bytes_read;
9322028508SToomas Soome Elf_Ehdr *ehdr;
9422028508SToomas Soome int err;
9522028508SToomas Soome
9622028508SToomas Soome /*
9722028508SToomas Soome * Open the image, read and validate the ELF header
9822028508SToomas Soome */
9922028508SToomas Soome if (filename == NULL) /* can't handle nameless */
10022028508SToomas Soome return (EFTYPE);
10122028508SToomas Soome if ((ef->fd = open(filename, O_RDONLY)) == -1)
10222028508SToomas Soome return (errno);
10322028508SToomas Soome ef->firstpage = malloc(PAGE_SIZE);
10422028508SToomas Soome if (ef->firstpage == NULL) {
10522028508SToomas Soome close(ef->fd);
10622028508SToomas Soome return (ENOMEM);
10722028508SToomas Soome }
10822028508SToomas Soome bytes_read = read(ef->fd, ef->firstpage, PAGE_SIZE);
10922028508SToomas Soome ef->firstlen = (size_t)bytes_read;
11022028508SToomas Soome if (bytes_read < 0 || ef->firstlen <= sizeof (Elf_Ehdr)) {
11122028508SToomas Soome err = EFTYPE; /* could be EIO, but may be small file */
11222028508SToomas Soome goto error;
11322028508SToomas Soome }
11422028508SToomas Soome ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage;
11522028508SToomas Soome
11622028508SToomas Soome /* Is it ELF? */
11722028508SToomas Soome if (!IS_ELF(*ehdr)) {
11822028508SToomas Soome err = EFTYPE;
11922028508SToomas Soome goto error;
12022028508SToomas Soome }
12122028508SToomas Soome if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */
12222028508SToomas Soome ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
12322028508SToomas Soome ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */
12422028508SToomas Soome ehdr->e_version != EV_CURRENT ||
12522028508SToomas Soome ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */
12622028508SToomas Soome err = EFTYPE;
12722028508SToomas Soome goto error;
12822028508SToomas Soome }
12922028508SToomas Soome
13022028508SToomas Soome return (0);
13122028508SToomas Soome
13222028508SToomas Soome error:
13322028508SToomas Soome if (ef->firstpage != NULL) {
13422028508SToomas Soome free(ef->firstpage);
13522028508SToomas Soome ef->firstpage = NULL;
13622028508SToomas Soome }
13722028508SToomas Soome if (ef->fd != -1) {
13822028508SToomas Soome close(ef->fd);
13922028508SToomas Soome ef->fd = -1;
14022028508SToomas Soome }
14122028508SToomas Soome return (err);
14222028508SToomas Soome }
14322028508SToomas Soome
14422028508SToomas Soome /*
14522028508SToomas Soome * Attempt to load the file (file) as an ELF module. It will be stored at
14622028508SToomas Soome * (dest), and a pointer to a module structure describing the loaded object
14722028508SToomas Soome * will be saved in (result).
14822028508SToomas Soome */
14922028508SToomas Soome int
__elfN(loadfile)150*de11c9b6SToomas Soome __elfN(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result)
15122028508SToomas Soome {
15222028508SToomas Soome return (__elfN(loadfile_raw)(filename, dest, result, 0));
15322028508SToomas Soome }
15422028508SToomas Soome
15522028508SToomas Soome int
__elfN(loadfile_raw)156*de11c9b6SToomas Soome __elfN(loadfile_raw)(char *filename, uint64_t dest,
15722028508SToomas Soome struct preloaded_file **result, int multiboot)
15822028508SToomas Soome {
15922028508SToomas Soome struct preloaded_file *fp, *kfp;
16022028508SToomas Soome struct elf_file ef;
16122028508SToomas Soome Elf_Ehdr *ehdr;
16222028508SToomas Soome int err;
16322028508SToomas Soome
16422028508SToomas Soome fp = NULL;
16522028508SToomas Soome bzero(&ef, sizeof (struct elf_file));
16622028508SToomas Soome ef.fd = -1;
16722028508SToomas Soome
16822028508SToomas Soome err = __elfN(load_elf_header)(filename, &ef);
16922028508SToomas Soome if (err != 0)
17022028508SToomas Soome return (err);
17122028508SToomas Soome
17222028508SToomas Soome ehdr = ef.ehdr;
17322028508SToomas Soome
17422028508SToomas Soome /*
17522028508SToomas Soome * Check to see what sort of module we are.
17622028508SToomas Soome */
17722028508SToomas Soome kfp = file_findfile(NULL, __elfN(kerneltype));
17822028508SToomas Soome #ifdef __powerpc__
17922028508SToomas Soome /*
18022028508SToomas Soome * Kernels can be ET_DYN, so just assume the first loaded object is the
18122028508SToomas Soome * kernel. This assumption will be checked later.
18222028508SToomas Soome */
18322028508SToomas Soome if (kfp == NULL)
18422028508SToomas Soome ef.kernel = 1;
18522028508SToomas Soome #endif
18622028508SToomas Soome if (ef.kernel || ehdr->e_type == ET_EXEC) {
18722028508SToomas Soome /* Looks like a kernel */
18822028508SToomas Soome if (kfp != NULL) {
189*de11c9b6SToomas Soome printf("elf" __XSTRING(__ELF_WORD_SIZE)
190*de11c9b6SToomas Soome "_loadfile: kernel already loaded\n");
19122028508SToomas Soome err = EPERM;
19222028508SToomas Soome goto oerr;
19322028508SToomas Soome }
19422028508SToomas Soome /*
19522028508SToomas Soome * Calculate destination address based on kernel entrypoint.
19622028508SToomas Soome *
197*de11c9b6SToomas Soome * For ARM, the destination address is independent of any
198*de11c9b6SToomas Soome * values in the elf header (an ARM kernel can be loaded at
199*de11c9b6SToomas Soome * any 2MB boundary), so we leave dest set to the value
200*de11c9b6SToomas Soome * calculated by archsw.arch_loadaddr() and passed in to
201*de11c9b6SToomas Soome * this function.
20222028508SToomas Soome */
20322028508SToomas Soome #ifndef __arm__
20422028508SToomas Soome if (ehdr->e_type == ET_EXEC)
20522028508SToomas Soome dest = (ehdr->e_entry & ~PAGE_MASK);
20622028508SToomas Soome #endif
20722028508SToomas Soome if ((ehdr->e_entry & ~PAGE_MASK) == 0) {
208*de11c9b6SToomas Soome printf("elf" __XSTRING(__ELF_WORD_SIZE)
209*de11c9b6SToomas Soome "_loadfile: not a kernel (maybe static binary?)\n");
21022028508SToomas Soome err = EPERM;
21122028508SToomas Soome goto oerr;
21222028508SToomas Soome }
21322028508SToomas Soome ef.kernel = 1;
21422028508SToomas Soome
21522028508SToomas Soome } else if (ehdr->e_type == ET_DYN) {
21622028508SToomas Soome /* Looks like a kld module */
21722028508SToomas Soome if (multiboot != 0) {
218*de11c9b6SToomas Soome printf("elf" __XSTRING(__ELF_WORD_SIZE)
219*de11c9b6SToomas Soome "_loadfile: can't load module as multiboot\n");
22022028508SToomas Soome err = EPERM;
22122028508SToomas Soome goto oerr;
22222028508SToomas Soome }
22322028508SToomas Soome if (kfp == NULL) {
224*de11c9b6SToomas Soome printf("elf" __XSTRING(__ELF_WORD_SIZE)
225*de11c9b6SToomas Soome "_loadfile: can't load module before kernel\n");
22622028508SToomas Soome err = EPERM;
22722028508SToomas Soome goto oerr;
22822028508SToomas Soome }
22922028508SToomas Soome if (strcmp(__elfN(kerneltype), kfp->f_type)) {
230*de11c9b6SToomas Soome printf("elf" __XSTRING(__ELF_WORD_SIZE)
231*de11c9b6SToomas Soome "_loadfile: can't load module with "
232*de11c9b6SToomas Soome "kernel type '%s'\n", kfp->f_type);
23322028508SToomas Soome err = EPERM;
23422028508SToomas Soome goto oerr;
23522028508SToomas Soome }
23622028508SToomas Soome /* Looks OK, got ahead */
23722028508SToomas Soome ef.kernel = 0;
23822028508SToomas Soome } else {
23922028508SToomas Soome err = EFTYPE;
24022028508SToomas Soome goto oerr;
24122028508SToomas Soome }
24222028508SToomas Soome
24322028508SToomas Soome if (archsw.arch_loadaddr != NULL)
24422028508SToomas Soome dest = archsw.arch_loadaddr(LOAD_ELF, ehdr, dest);
24522028508SToomas Soome else
24622028508SToomas Soome dest = roundup(dest, PAGE_SIZE);
24722028508SToomas Soome
24822028508SToomas Soome /*
24922028508SToomas Soome * Ok, we think we should handle this.
25022028508SToomas Soome */
25122028508SToomas Soome fp = file_alloc();
25222028508SToomas Soome if (fp == NULL) {
253*de11c9b6SToomas Soome printf("elf" __XSTRING(__ELF_WORD_SIZE)
254*de11c9b6SToomas Soome "_loadfile: cannot allocate module info\n");
25522028508SToomas Soome err = EPERM;
25622028508SToomas Soome goto out;
25722028508SToomas Soome }
25822028508SToomas Soome if (ef.kernel == 1 && multiboot == 0)
25922028508SToomas Soome setenv("kernelname", filename, 1);
26022028508SToomas Soome fp->f_name = strdup(filename);
26122028508SToomas Soome if (multiboot == 0) {
26222028508SToomas Soome fp->f_type = strdup(ef.kernel ?
26322028508SToomas Soome __elfN(kerneltype) : __elfN(moduletype));
26422028508SToomas Soome } else {
26522028508SToomas Soome if (multiboot == 1)
26622028508SToomas Soome fp->f_type = strdup("elf multiboot kernel");
26722028508SToomas Soome else
26822028508SToomas Soome fp->f_type = strdup("elf multiboot2 kernel");
26922028508SToomas Soome }
27022028508SToomas Soome
27122028508SToomas Soome #ifdef ELF_VERBOSE
27222028508SToomas Soome if (ef.kernel)
273*de11c9b6SToomas Soome printf("%s entry at 0x%jx\n", filename,
274*de11c9b6SToomas Soome (uintmax_t)ehdr->e_entry);
27522028508SToomas Soome #else
27622028508SToomas Soome printf("%s ", filename);
27722028508SToomas Soome #endif
27822028508SToomas Soome
27922028508SToomas Soome fp->f_size = __elfN(loadimage)(fp, &ef, dest);
28022028508SToomas Soome if (fp->f_size == 0 || fp->f_addr == 0)
28122028508SToomas Soome goto ioerr;
28222028508SToomas Soome
28322028508SToomas Soome /* save exec header as metadata */
28422028508SToomas Soome file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof (*ehdr), ehdr);
28522028508SToomas Soome
28622028508SToomas Soome /* Load OK, return module pointer */
28722028508SToomas Soome *result = (struct preloaded_file *)fp;
28822028508SToomas Soome err = 0;
28922028508SToomas Soome goto out;
29022028508SToomas Soome
29122028508SToomas Soome ioerr:
29222028508SToomas Soome err = EIO;
29322028508SToomas Soome oerr:
29422028508SToomas Soome file_discard(fp);
29522028508SToomas Soome out:
29622028508SToomas Soome if (ef.firstpage)
29722028508SToomas Soome free(ef.firstpage);
29822028508SToomas Soome if (ef.fd != -1)
29922028508SToomas Soome close(ef.fd);
30022028508SToomas Soome return (err);
30122028508SToomas Soome }
30222028508SToomas Soome
30322028508SToomas Soome /*
30422028508SToomas Soome * With the file (fd) open on the image, and (ehdr) containing
30522028508SToomas Soome * the Elf header, load the image at (off)
30622028508SToomas Soome */
30722028508SToomas Soome static int
__elfN(loadimage)308*de11c9b6SToomas Soome __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off)
30922028508SToomas Soome {
31022028508SToomas Soome int i;
311*de11c9b6SToomas Soome uint_t j;
31222028508SToomas Soome Elf_Ehdr *ehdr;
31322028508SToomas Soome Elf_Phdr *phdr, *php;
31422028508SToomas Soome Elf_Shdr *shdr;
31522028508SToomas Soome char *shstr;
31622028508SToomas Soome int ret;
31722028508SToomas Soome vm_offset_t firstaddr;
31822028508SToomas Soome vm_offset_t lastaddr;
31922028508SToomas Soome size_t chunk;
32022028508SToomas Soome ssize_t result;
32122028508SToomas Soome Elf_Addr ssym, esym;
32222028508SToomas Soome Elf_Dyn *dp;
32322028508SToomas Soome Elf_Addr adp;
32422028508SToomas Soome Elf_Addr ctors;
32522028508SToomas Soome int ndp;
32622028508SToomas Soome int symstrindex;
32722028508SToomas Soome int symtabindex;
32822028508SToomas Soome Elf_Size size;
329*de11c9b6SToomas Soome uint_t fpcopy;
33022028508SToomas Soome Elf_Sym sym;
33122028508SToomas Soome Elf_Addr p_start, p_end;
33222028508SToomas Soome
33322028508SToomas Soome dp = NULL;
33422028508SToomas Soome shdr = NULL;
33522028508SToomas Soome ret = 0;
33622028508SToomas Soome firstaddr = lastaddr = 0;
33722028508SToomas Soome ehdr = ef->ehdr;
33822028508SToomas Soome if (ehdr->e_type == ET_EXEC) {
33922028508SToomas Soome #if defined(__i386__) || defined(__amd64__)
34022028508SToomas Soome #if __ELF_WORD_SIZE == 64
341*de11c9b6SToomas Soome /* x86_64 relocates after locore */
342*de11c9b6SToomas Soome off = - (off & 0xffffffffff000000ull);
34322028508SToomas Soome #else
344*de11c9b6SToomas Soome /* i386 relocates after locore */
345*de11c9b6SToomas Soome off = - (off & 0xff000000u);
34622028508SToomas Soome #endif
34722028508SToomas Soome #elif defined(__powerpc__)
34822028508SToomas Soome /*
349*de11c9b6SToomas Soome * On the purely virtual memory machines like e500, the kernel
350*de11c9b6SToomas Soome * is linked against its final VA range, which is most often
351*de11c9b6SToomas Soome * not available at the loader stage, but only after kernel
352*de11c9b6SToomas Soome * initializes and completes its VM settings. In such cases
353*de11c9b6SToomas Soome * we cannot use p_vaddr field directly to load ELF segments,
354*de11c9b6SToomas Soome * but put them at some 'load-time' locations.
35522028508SToomas Soome */
35622028508SToomas Soome if (off & 0xf0000000u) {
35722028508SToomas Soome off = -(off & 0xf0000000u);
35822028508SToomas Soome /*
359*de11c9b6SToomas Soome * XXX the physical load address should not be
360*de11c9b6SToomas Soome * hardcoded. Note that the Book-E kernel assumes
361*de11c9b6SToomas Soome * that it's loaded at a 16MB boundary for now...
36222028508SToomas Soome */
36322028508SToomas Soome off += 0x01000000;
36422028508SToomas Soome ehdr->e_entry += off;
36522028508SToomas Soome #ifdef ELF_VERBOSE
36622028508SToomas Soome printf("Converted entry 0x%08x\n", ehdr->e_entry);
36722028508SToomas Soome #endif
368*de11c9b6SToomas Soome } else {
36922028508SToomas Soome off = 0;
370*de11c9b6SToomas Soome }
37122028508SToomas Soome #elif defined(__arm__) && !defined(EFI)
37222028508SToomas Soome /*
373*de11c9b6SToomas Soome * The elf headers in arm kernels specify virtual addresses
374*de11c9b6SToomas Soome * in all header fields, even the ones that should be physical
375*de11c9b6SToomas Soome * addresses.
376*de11c9b6SToomas Soome * We assume the entry point is in the first page, and masking
377*de11c9b6SToomas Soome * the page offset will leave us with the virtual address the
378*de11c9b6SToomas Soome * kernel was linked at. We subtract that from the load offset,
379*de11c9b6SToomas Soome * making 'off' into the value which, when added to a virtual
380*de11c9b6SToomas Soome * address in an elf header, translates it to a physical
381*de11c9b6SToomas Soome * address. We do the va->pa conversion on the entry point
382*de11c9b6SToomas Soome * address in the header now, so that later we can
38322028508SToomas Soome * launch the kernel by just jumping to that address.
38422028508SToomas Soome *
385*de11c9b6SToomas Soome * When booting from UEFI the copyin and copyout functions
386*de11c9b6SToomas Soome * handle adjusting the location relative to the first virtual
387*de11c9b6SToomas Soome * address. Because of this there is no need to adjust the
388*de11c9b6SToomas Soome * offset or entry point address as these will both be handled
389*de11c9b6SToomas Soome * by the efi code.
39022028508SToomas Soome */
39122028508SToomas Soome off -= ehdr->e_entry & ~PAGE_MASK;
39222028508SToomas Soome ehdr->e_entry += off;
39322028508SToomas Soome #ifdef ELF_VERBOSE
394*de11c9b6SToomas Soome printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n",
395*de11c9b6SToomas Soome ehdr->e_entry, off);
39622028508SToomas Soome #endif
39722028508SToomas Soome #else
39822028508SToomas Soome off = 0; /* other archs use direct mapped kernels */
39922028508SToomas Soome #endif
40022028508SToomas Soome }
40122028508SToomas Soome ef->off = off;
40222028508SToomas Soome
40322028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
40422028508SToomas Soome /* use entry address from header */
40522028508SToomas Soome fp->f_addr = ehdr->e_entry;
40622028508SToomas Soome }
40722028508SToomas Soome
40822028508SToomas Soome if (ef->kernel)
40922028508SToomas Soome __elfN(relocation_offset) = off;
41022028508SToomas Soome
41122028508SToomas Soome if ((ehdr->e_phoff + ehdr->e_phnum * sizeof (*phdr)) > ef->firstlen) {
412*de11c9b6SToomas Soome printf("elf" __XSTRING(__ELF_WORD_SIZE)
413*de11c9b6SToomas Soome "_loadimage: program header not within first page\n");
41422028508SToomas Soome goto out;
41522028508SToomas Soome }
41622028508SToomas Soome phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff);
41722028508SToomas Soome
41822028508SToomas Soome for (i = 0; i < ehdr->e_phnum; i++) {
41922028508SToomas Soome /* We want to load PT_LOAD segments only.. */
42022028508SToomas Soome if (phdr[i].p_type != PT_LOAD)
42122028508SToomas Soome continue;
42222028508SToomas Soome
42322028508SToomas Soome #ifdef ELF_VERBOSE
42422028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
42522028508SToomas Soome printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
42622028508SToomas Soome (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
42722028508SToomas Soome (long)(phdr[i].p_paddr + off),
428*de11c9b6SToomas Soome (long)(phdr[i].p_paddr + off +
429*de11c9b6SToomas Soome phdr[i].p_memsz - 1));
43022028508SToomas Soome } else {
43122028508SToomas Soome printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
43222028508SToomas Soome (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
43322028508SToomas Soome (long)(phdr[i].p_vaddr + off),
434*de11c9b6SToomas Soome (long)(phdr[i].p_vaddr + off +
435*de11c9b6SToomas Soome phdr[i].p_memsz - 1));
43622028508SToomas Soome }
43722028508SToomas Soome #else
43822028508SToomas Soome if ((phdr[i].p_flags & PF_W) == 0) {
43922028508SToomas Soome printf("text=0x%lx ", (long)phdr[i].p_filesz);
44022028508SToomas Soome } else {
44122028508SToomas Soome printf("data=0x%lx", (long)phdr[i].p_filesz);
44222028508SToomas Soome if (phdr[i].p_filesz < phdr[i].p_memsz)
443*de11c9b6SToomas Soome printf("+0x%lx",
444*de11c9b6SToomas Soome (long)(phdr[i].p_memsz -phdr[i].p_filesz));
44522028508SToomas Soome printf(" ");
44622028508SToomas Soome }
44722028508SToomas Soome #endif
44822028508SToomas Soome fpcopy = 0;
44922028508SToomas Soome if (ef->firstlen > phdr[i].p_offset) {
45022028508SToomas Soome fpcopy = ef->firstlen - phdr[i].p_offset;
45122028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
452*de11c9b6SToomas Soome archsw.arch_copyin(ef->firstpage +
453*de11c9b6SToomas Soome phdr[i].p_offset,
45422028508SToomas Soome phdr[i].p_paddr + off, fpcopy);
45522028508SToomas Soome } else {
456*de11c9b6SToomas Soome archsw.arch_copyin(ef->firstpage +
457*de11c9b6SToomas Soome phdr[i].p_offset,
45822028508SToomas Soome phdr[i].p_vaddr + off, fpcopy);
45922028508SToomas Soome }
46022028508SToomas Soome }
46122028508SToomas Soome if (phdr[i].p_filesz > fpcopy) {
46222028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
463*de11c9b6SToomas Soome if (kern_pread(ef->fd,
464*de11c9b6SToomas Soome phdr[i].p_paddr + off + fpcopy,
46522028508SToomas Soome phdr[i].p_filesz - fpcopy,
46622028508SToomas Soome phdr[i].p_offset + fpcopy) != 0) {
467*de11c9b6SToomas Soome printf("\nelf"
468*de11c9b6SToomas Soome __XSTRING(__ELF_WORD_SIZE)
46922028508SToomas Soome "_loadimage: read failed\n");
47022028508SToomas Soome goto out;
47122028508SToomas Soome }
47222028508SToomas Soome } else {
473*de11c9b6SToomas Soome if (kern_pread(ef->fd,
474*de11c9b6SToomas Soome phdr[i].p_vaddr + off + fpcopy,
47522028508SToomas Soome phdr[i].p_filesz - fpcopy,
47622028508SToomas Soome phdr[i].p_offset + fpcopy) != 0) {
477*de11c9b6SToomas Soome printf("\nelf"
478*de11c9b6SToomas Soome __XSTRING(__ELF_WORD_SIZE)
47922028508SToomas Soome "_loadimage: read failed\n");
48022028508SToomas Soome goto out;
48122028508SToomas Soome }
48222028508SToomas Soome }
48322028508SToomas Soome }
48422028508SToomas Soome /* clear space from oversized segments; eg: bss */
48522028508SToomas Soome if (phdr[i].p_filesz < phdr[i].p_memsz) {
48622028508SToomas Soome #ifdef ELF_VERBOSE
48722028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
48822028508SToomas Soome printf(" (bss: 0x%lx-0x%lx)",
489*de11c9b6SToomas Soome (long)(phdr[i].p_paddr + off +
490*de11c9b6SToomas Soome phdr[i].p_filesz),
491*de11c9b6SToomas Soome (long)(phdr[i].p_paddr + off +
492*de11c9b6SToomas Soome phdr[i].p_memsz - 1));
49322028508SToomas Soome } else {
49422028508SToomas Soome printf(" (bss: 0x%lx-0x%lx)",
495*de11c9b6SToomas Soome (long)(phdr[i].p_vaddr + off +
496*de11c9b6SToomas Soome phdr[i].p_filesz),
497*de11c9b6SToomas Soome (long)(phdr[i].p_vaddr + off +
498*de11c9b6SToomas Soome phdr[i].p_memsz - 1));
49922028508SToomas Soome }
50022028508SToomas Soome #endif
50122028508SToomas Soome
50222028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
503*de11c9b6SToomas Soome kern_bzero(phdr[i].p_paddr + off +
504*de11c9b6SToomas Soome phdr[i].p_filesz,
50522028508SToomas Soome phdr[i].p_memsz - phdr[i].p_filesz);
50622028508SToomas Soome } else {
507*de11c9b6SToomas Soome kern_bzero(phdr[i].p_vaddr + off +
508*de11c9b6SToomas Soome phdr[i].p_filesz,
50922028508SToomas Soome phdr[i].p_memsz - phdr[i].p_filesz);
51022028508SToomas Soome }
51122028508SToomas Soome }
51222028508SToomas Soome #ifdef ELF_VERBOSE
51322028508SToomas Soome printf("\n");
51422028508SToomas Soome #endif
51522028508SToomas Soome
51622028508SToomas Soome if (archsw.arch_loadseg != NULL)
51722028508SToomas Soome archsw.arch_loadseg(ehdr, phdr + i, off);
51822028508SToomas Soome
51922028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
520*de11c9b6SToomas Soome if (firstaddr == 0 ||
521*de11c9b6SToomas Soome firstaddr > (phdr[i].p_paddr + off))
52222028508SToomas Soome firstaddr = phdr[i].p_paddr + off;
523*de11c9b6SToomas Soome if (lastaddr == 0 || lastaddr <
524*de11c9b6SToomas Soome (phdr[i].p_paddr + off + phdr[i].p_memsz))
525*de11c9b6SToomas Soome lastaddr = phdr[i].p_paddr + off +
526*de11c9b6SToomas Soome phdr[i].p_memsz;
52722028508SToomas Soome } else {
528*de11c9b6SToomas Soome if (firstaddr == 0 ||
529*de11c9b6SToomas Soome firstaddr > (phdr[i].p_vaddr + off))
53022028508SToomas Soome firstaddr = phdr[i].p_vaddr + off;
531*de11c9b6SToomas Soome if (lastaddr == 0 || lastaddr <
532*de11c9b6SToomas Soome (phdr[i].p_vaddr + off + phdr[i].p_memsz))
533*de11c9b6SToomas Soome lastaddr = phdr[i].p_vaddr + off +
534*de11c9b6SToomas Soome phdr[i].p_memsz;
53522028508SToomas Soome }
53622028508SToomas Soome }
53722028508SToomas Soome lastaddr = roundup(lastaddr, sizeof (long));
53822028508SToomas Soome
53922028508SToomas Soome /*
54022028508SToomas Soome * Get the section headers. We need this for finding the .ctors
54122028508SToomas Soome * section as well as for loading any symbols. Both may be hard
54222028508SToomas Soome * to do if reading from a .gz file as it involves seeking. I
54322028508SToomas Soome * think the rule is going to have to be that you must strip a
54422028508SToomas Soome * file to remove symbols before gzipping it.
54522028508SToomas Soome */
54622028508SToomas Soome chunk = ehdr->e_shnum * ehdr->e_shentsize;
54722028508SToomas Soome if (chunk == 0 || ehdr->e_shoff == 0)
54822028508SToomas Soome goto nosyms;
54922028508SToomas Soome shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk);
55022028508SToomas Soome if (shdr == NULL) {
55122028508SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
55222028508SToomas Soome "_loadimage: failed to read section headers");
55322028508SToomas Soome goto nosyms;
55422028508SToomas Soome }
55522028508SToomas Soome file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr);
55622028508SToomas Soome
55722028508SToomas Soome /*
55822028508SToomas Soome * Read the section string table and look for the .ctors section.
55922028508SToomas Soome * We need to tell the kernel where it is so that it can call the
56022028508SToomas Soome * ctors.
56122028508SToomas Soome */
56222028508SToomas Soome chunk = shdr[ehdr->e_shstrndx].sh_size;
56322028508SToomas Soome if (chunk) {
564*de11c9b6SToomas Soome shstr = alloc_pread(ef->fd, shdr[ehdr->e_shstrndx].sh_offset,
565*de11c9b6SToomas Soome chunk);
56622028508SToomas Soome if (shstr) {
56722028508SToomas Soome for (i = 0; i < ehdr->e_shnum; i++) {
568*de11c9b6SToomas Soome if (strcmp(shstr + shdr[i].sh_name,
569*de11c9b6SToomas Soome ".ctors") != 0)
57022028508SToomas Soome continue;
57122028508SToomas Soome ctors = shdr[i].sh_addr;
572*de11c9b6SToomas Soome file_addmetadata(fp, MODINFOMD_CTORS_ADDR,
573*de11c9b6SToomas Soome sizeof (ctors), &ctors);
57422028508SToomas Soome size = shdr[i].sh_size;
575*de11c9b6SToomas Soome file_addmetadata(fp, MODINFOMD_CTORS_SIZE,
576*de11c9b6SToomas Soome sizeof (size), &size);
57722028508SToomas Soome break;
57822028508SToomas Soome }
57922028508SToomas Soome free(shstr);
58022028508SToomas Soome }
58122028508SToomas Soome }
58222028508SToomas Soome
58322028508SToomas Soome /*
58422028508SToomas Soome * Now load any symbols.
58522028508SToomas Soome */
58622028508SToomas Soome symtabindex = -1;
58722028508SToomas Soome symstrindex = -1;
58822028508SToomas Soome for (i = 0; i < ehdr->e_shnum; i++) {
58922028508SToomas Soome if (shdr[i].sh_type != SHT_SYMTAB)
59022028508SToomas Soome continue;
59122028508SToomas Soome for (j = 0; j < ehdr->e_phnum; j++) {
59222028508SToomas Soome if (phdr[j].p_type != PT_LOAD)
59322028508SToomas Soome continue;
59422028508SToomas Soome if (shdr[i].sh_offset >= phdr[j].p_offset &&
59522028508SToomas Soome (shdr[i].sh_offset + shdr[i].sh_size <=
59622028508SToomas Soome phdr[j].p_offset + phdr[j].p_filesz)) {
59722028508SToomas Soome shdr[i].sh_offset = 0;
59822028508SToomas Soome shdr[i].sh_size = 0;
59922028508SToomas Soome break;
60022028508SToomas Soome }
60122028508SToomas Soome }
60222028508SToomas Soome if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
60322028508SToomas Soome continue; /* alread loaded in a PT_LOAD above */
60422028508SToomas Soome /* Save it for loading below */
60522028508SToomas Soome symtabindex = i;
60622028508SToomas Soome symstrindex = shdr[i].sh_link;
60722028508SToomas Soome }
60822028508SToomas Soome if (symtabindex < 0 || symstrindex < 0)
60922028508SToomas Soome goto nosyms;
61022028508SToomas Soome
61122028508SToomas Soome /* Ok, committed to a load. */
61222028508SToomas Soome #ifndef ELF_VERBOSE
61322028508SToomas Soome printf("syms=[");
61422028508SToomas Soome #endif
61522028508SToomas Soome ssym = lastaddr;
61622028508SToomas Soome for (i = symtabindex; i >= 0; i = symstrindex) {
61722028508SToomas Soome #ifdef ELF_VERBOSE
61822028508SToomas Soome char *secname;
61922028508SToomas Soome
62022028508SToomas Soome switch (shdr[i].sh_type) {
62122028508SToomas Soome case SHT_SYMTAB: /* Symbol table */
62222028508SToomas Soome secname = "symtab";
62322028508SToomas Soome break;
62422028508SToomas Soome case SHT_STRTAB: /* String table */
62522028508SToomas Soome secname = "strtab";
62622028508SToomas Soome break;
62722028508SToomas Soome default:
62822028508SToomas Soome secname = "WHOA!!";
62922028508SToomas Soome break;
63022028508SToomas Soome }
63122028508SToomas Soome #endif
63222028508SToomas Soome
63322028508SToomas Soome size = shdr[i].sh_size;
63422028508SToomas Soome archsw.arch_copyin(&size, lastaddr, sizeof (size));
63522028508SToomas Soome lastaddr += sizeof (size);
63622028508SToomas Soome
63722028508SToomas Soome #ifdef ELF_VERBOSE
63822028508SToomas Soome printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname,
63922028508SToomas Soome (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset,
640*de11c9b6SToomas Soome (uintmax_t)lastaddr,
641*de11c9b6SToomas Soome (uintmax_t)(lastaddr + shdr[i].sh_size));
64222028508SToomas Soome #else
64322028508SToomas Soome if (i == symstrindex)
64422028508SToomas Soome printf("+");
64522028508SToomas Soome printf("0x%lx+0x%lx", (long)sizeof (size), (long)size);
64622028508SToomas Soome #endif
64722028508SToomas Soome
64822028508SToomas Soome if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) {
649*de11c9b6SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage:"
650*de11c9b6SToomas Soome " could not seek for symbols - skipped!");
65122028508SToomas Soome lastaddr = ssym;
65222028508SToomas Soome ssym = 0;
65322028508SToomas Soome goto nosyms;
65422028508SToomas Soome }
65522028508SToomas Soome result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size);
65622028508SToomas Soome if (result < 0 || (size_t)result != shdr[i].sh_size) {
657*de11c9b6SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: "
658*de11c9b6SToomas Soome "could not read symbols - skipped! (%ju != %ju)",
659*de11c9b6SToomas Soome (uintmax_t)result, (uintmax_t)shdr[i].sh_size);
66022028508SToomas Soome lastaddr = ssym;
66122028508SToomas Soome ssym = 0;
66222028508SToomas Soome goto nosyms;
66322028508SToomas Soome }
66422028508SToomas Soome /* Reset offsets relative to ssym */
66522028508SToomas Soome lastaddr += shdr[i].sh_size;
66622028508SToomas Soome lastaddr = roundup(lastaddr, sizeof (size));
66722028508SToomas Soome if (i == symtabindex)
66822028508SToomas Soome symtabindex = -1;
66922028508SToomas Soome else if (i == symstrindex)
67022028508SToomas Soome symstrindex = -1;
67122028508SToomas Soome }
67222028508SToomas Soome esym = lastaddr;
67322028508SToomas Soome #ifndef ELF_VERBOSE
67422028508SToomas Soome printf("]");
67522028508SToomas Soome #endif
67622028508SToomas Soome
67722028508SToomas Soome file_addmetadata(fp, MODINFOMD_SSYM, sizeof (ssym), &ssym);
67822028508SToomas Soome file_addmetadata(fp, MODINFOMD_ESYM, sizeof (esym), &esym);
67922028508SToomas Soome
68022028508SToomas Soome nosyms:
68122028508SToomas Soome printf("\n");
68222028508SToomas Soome
68322028508SToomas Soome ret = lastaddr - firstaddr;
68422028508SToomas Soome if (ehdr->e_ident[EI_OSABI] != ELFOSABI_SOLARIS)
68522028508SToomas Soome fp->f_addr = firstaddr;
68622028508SToomas Soome
68722028508SToomas Soome php = NULL;
68822028508SToomas Soome for (i = 0; i < ehdr->e_phnum; i++) {
68922028508SToomas Soome if (phdr[i].p_type == PT_DYNAMIC) {
69022028508SToomas Soome php = phdr + i;
69122028508SToomas Soome adp = php->p_vaddr;
692*de11c9b6SToomas Soome file_addmetadata(fp, MODINFOMD_DYNAMIC,
693*de11c9b6SToomas Soome sizeof (adp), &adp);
69422028508SToomas Soome break;
69522028508SToomas Soome }
69622028508SToomas Soome }
69722028508SToomas Soome
69822028508SToomas Soome if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */
69922028508SToomas Soome goto out;
70022028508SToomas Soome
70122028508SToomas Soome ndp = php->p_filesz / sizeof (Elf_Dyn);
70222028508SToomas Soome if (ndp == 0)
70322028508SToomas Soome goto out;
70422028508SToomas Soome dp = malloc(php->p_filesz);
70522028508SToomas Soome if (dp == NULL)
70622028508SToomas Soome goto out;
70722028508SToomas Soome if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
70822028508SToomas Soome archsw.arch_copyout(php->p_paddr + off, dp, php->p_filesz);
70922028508SToomas Soome else
71022028508SToomas Soome archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz);
71122028508SToomas Soome
71222028508SToomas Soome ef->strsz = 0;
71322028508SToomas Soome for (i = 0; i < ndp; i++) {
71422028508SToomas Soome if (dp[i].d_tag == 0)
71522028508SToomas Soome break;
71622028508SToomas Soome switch (dp[i].d_tag) {
71722028508SToomas Soome case DT_HASH:
718*de11c9b6SToomas Soome ef->hashtab = (Elf_Hashelt*)(uintptr_t)
719*de11c9b6SToomas Soome (dp[i].d_un.d_ptr + off);
72022028508SToomas Soome break;
72122028508SToomas Soome case DT_STRTAB:
722*de11c9b6SToomas Soome ef->strtab = (char *)(uintptr_t)
723*de11c9b6SToomas Soome (dp[i].d_un.d_ptr + off);
72422028508SToomas Soome break;
72522028508SToomas Soome case DT_STRSZ:
72622028508SToomas Soome ef->strsz = dp[i].d_un.d_val;
72722028508SToomas Soome break;
72822028508SToomas Soome case DT_SYMTAB:
729*de11c9b6SToomas Soome ef->symtab = (Elf_Sym*)(uintptr_t)
730*de11c9b6SToomas Soome (dp[i].d_un.d_ptr + off);
73122028508SToomas Soome break;
73222028508SToomas Soome case DT_REL:
733*de11c9b6SToomas Soome ef->rel = (Elf_Rel *)(uintptr_t)
734*de11c9b6SToomas Soome (dp[i].d_un.d_ptr + off);
73522028508SToomas Soome break;
73622028508SToomas Soome case DT_RELSZ:
73722028508SToomas Soome ef->relsz = dp[i].d_un.d_val;
73822028508SToomas Soome break;
73922028508SToomas Soome case DT_RELA:
740*de11c9b6SToomas Soome ef->rela = (Elf_Rela *)(uintptr_t)
741*de11c9b6SToomas Soome (dp[i].d_un.d_ptr + off);
74222028508SToomas Soome break;
74322028508SToomas Soome case DT_RELASZ:
74422028508SToomas Soome ef->relasz = dp[i].d_un.d_val;
74522028508SToomas Soome break;
74622028508SToomas Soome default:
74722028508SToomas Soome break;
74822028508SToomas Soome }
74922028508SToomas Soome }
75022028508SToomas Soome if (ef->hashtab == NULL || ef->symtab == NULL ||
75122028508SToomas Soome ef->strtab == NULL || ef->strsz == 0)
75222028508SToomas Soome goto out;
75322028508SToomas Soome COPYOUT(ef->hashtab, &ef->nbuckets, sizeof (ef->nbuckets));
75422028508SToomas Soome COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof (ef->nchains));
75522028508SToomas Soome ef->buckets = ef->hashtab + 2;
75622028508SToomas Soome ef->chains = ef->buckets + ef->nbuckets;
75722028508SToomas Soome
758*de11c9b6SToomas Soome if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set",
759*de11c9b6SToomas Soome &sym) != 0)
760*de11c9b6SToomas Soome return (0);
76122028508SToomas Soome p_start = sym.st_value + ef->off;
762*de11c9b6SToomas Soome if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set",
763*de11c9b6SToomas Soome &sym) != 0)
764*de11c9b6SToomas Soome return (ENOENT);
76522028508SToomas Soome p_end = sym.st_value + ef->off;
76622028508SToomas Soome
76722028508SToomas Soome if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0)
76822028508SToomas Soome goto out;
76922028508SToomas Soome
77022028508SToomas Soome if (ef->kernel) /* kernel must not depend on anything */
77122028508SToomas Soome goto out;
77222028508SToomas Soome
77322028508SToomas Soome out:
77422028508SToomas Soome free(dp);
77522028508SToomas Soome free(shdr);
776*de11c9b6SToomas Soome return (ret);
77722028508SToomas Soome }
77822028508SToomas Soome
77922028508SToomas Soome static char invalid_name[] = "bad";
78022028508SToomas Soome
78122028508SToomas Soome char *
fake_modname(const char * name)78222028508SToomas Soome fake_modname(const char *name)
78322028508SToomas Soome {
78422028508SToomas Soome const char *sp, *ep;
78522028508SToomas Soome char *fp;
78622028508SToomas Soome size_t len;
78722028508SToomas Soome
78822028508SToomas Soome sp = strrchr(name, '/');
78922028508SToomas Soome if (sp)
79022028508SToomas Soome sp++;
79122028508SToomas Soome else
79222028508SToomas Soome sp = name;
79322028508SToomas Soome ep = strrchr(name, '.');
79422028508SToomas Soome if (ep) {
79522028508SToomas Soome if (ep == name) {
79622028508SToomas Soome sp = invalid_name;
79722028508SToomas Soome ep = invalid_name + sizeof (invalid_name) - 1;
79822028508SToomas Soome }
799*de11c9b6SToomas Soome } else {
80022028508SToomas Soome ep = name + strlen(name);
801*de11c9b6SToomas Soome }
80222028508SToomas Soome len = ep - sp;
80322028508SToomas Soome fp = malloc(len + 1);
80422028508SToomas Soome if (fp == NULL)
805*de11c9b6SToomas Soome return (NULL);
80622028508SToomas Soome memcpy(fp, sp, len);
80722028508SToomas Soome fp[len] = '\0';
808*de11c9b6SToomas Soome return (fp);
80922028508SToomas Soome }
81022028508SToomas Soome
81122028508SToomas Soome #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
81222028508SToomas Soome struct mod_metadata64 {
81322028508SToomas Soome int md_version; /* structure version MDTV_* */
81422028508SToomas Soome int md_type; /* type of entry MDT_* */
815*de11c9b6SToomas Soome uint64_t md_data; /* specific data */
816*de11c9b6SToomas Soome uint64_t md_cval; /* common string label */
81722028508SToomas Soome };
81822028508SToomas Soome #endif
81922028508SToomas Soome #if defined(__amd64__) && __ELF_WORD_SIZE == 32
82022028508SToomas Soome struct mod_metadata32 {
82122028508SToomas Soome int md_version; /* structure version MDTV_* */
82222028508SToomas Soome int md_type; /* type of entry MDT_* */
823*de11c9b6SToomas Soome uint32_t md_data; /* specific data */
824*de11c9b6SToomas Soome uint32_t md_cval; /* common string label */
82522028508SToomas Soome };
82622028508SToomas Soome #endif
82722028508SToomas Soome
82822028508SToomas Soome int
__elfN(load_modmetadata)829*de11c9b6SToomas Soome __elfN(load_modmetadata)(struct preloaded_file *fp, uint64_t dest)
83022028508SToomas Soome {
83122028508SToomas Soome struct elf_file ef;
83222028508SToomas Soome int err, i, j;
83322028508SToomas Soome Elf_Shdr *sh_meta, *shdr = NULL;
83422028508SToomas Soome Elf_Shdr *sh_data[2];
83522028508SToomas Soome char *shstrtab = NULL;
83622028508SToomas Soome size_t size;
83722028508SToomas Soome Elf_Addr p_start, p_end;
83822028508SToomas Soome
83922028508SToomas Soome bzero(&ef, sizeof (struct elf_file));
84022028508SToomas Soome ef.fd = -1;
84122028508SToomas Soome
84222028508SToomas Soome err = __elfN(load_elf_header)(fp->f_name, &ef);
84322028508SToomas Soome if (err != 0)
84422028508SToomas Soome goto out;
84522028508SToomas Soome
84622028508SToomas Soome if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) {
84722028508SToomas Soome ef.kernel = 1;
84822028508SToomas Soome } else if (ef.ehdr->e_type != ET_DYN) {
84922028508SToomas Soome err = EFTYPE;
85022028508SToomas Soome goto out;
85122028508SToomas Soome }
85222028508SToomas Soome
85322028508SToomas Soome size = ef.ehdr->e_shnum * ef.ehdr->e_shentsize;
85422028508SToomas Soome shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size);
85522028508SToomas Soome if (shdr == NULL) {
85622028508SToomas Soome err = ENOMEM;
85722028508SToomas Soome goto out;
85822028508SToomas Soome }
85922028508SToomas Soome
86022028508SToomas Soome /* Load shstrtab. */
86122028508SToomas Soome shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset,
86222028508SToomas Soome shdr[ef.ehdr->e_shstrndx].sh_size);
86322028508SToomas Soome if (shstrtab == NULL) {
86422028508SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
86522028508SToomas Soome "load_modmetadata: unable to load shstrtab\n");
86622028508SToomas Soome err = EFTYPE;
86722028508SToomas Soome goto out;
86822028508SToomas Soome }
86922028508SToomas Soome
87022028508SToomas Soome /* Find set_modmetadata_set and data sections. */
87122028508SToomas Soome sh_data[0] = sh_data[1] = sh_meta = NULL;
87222028508SToomas Soome for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) {
87322028508SToomas Soome if (strcmp(&shstrtab[shdr[i].sh_name],
87422028508SToomas Soome "set_modmetadata_set") == 0) {
87522028508SToomas Soome sh_meta = &shdr[i];
87622028508SToomas Soome }
87722028508SToomas Soome if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) ||
87822028508SToomas Soome (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) {
87922028508SToomas Soome sh_data[j++] = &shdr[i];
88022028508SToomas Soome }
88122028508SToomas Soome }
88222028508SToomas Soome if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) {
883*de11c9b6SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: "
884*de11c9b6SToomas Soome "unable to find set_modmetadata_set or data sections\n");
88522028508SToomas Soome err = EFTYPE;
88622028508SToomas Soome goto out;
88722028508SToomas Soome }
88822028508SToomas Soome
88922028508SToomas Soome /* Load set_modmetadata_set into memory */
89022028508SToomas Soome err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset);
89122028508SToomas Soome if (err != 0) {
892*de11c9b6SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: "
893*de11c9b6SToomas Soome "unable to load set_modmetadata_set: %d\n", err);
89422028508SToomas Soome goto out;
89522028508SToomas Soome }
89622028508SToomas Soome p_start = dest;
89722028508SToomas Soome p_end = dest + sh_meta->sh_size;
89822028508SToomas Soome dest += sh_meta->sh_size;
89922028508SToomas Soome
90022028508SToomas Soome /* Load data sections into memory. */
90122028508SToomas Soome err = kern_pread(ef.fd, dest, sh_data[0]->sh_size,
90222028508SToomas Soome sh_data[0]->sh_offset);
90322028508SToomas Soome if (err != 0) {
90422028508SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
90522028508SToomas Soome "load_modmetadata: unable to load data: %d\n", err);
90622028508SToomas Soome goto out;
90722028508SToomas Soome }
90822028508SToomas Soome
90922028508SToomas Soome /*
91022028508SToomas Soome * We have to increment the dest, so that the offset is the same into
91122028508SToomas Soome * both the .rodata and .data sections.
91222028508SToomas Soome */
91322028508SToomas Soome ef.off = -(sh_data[0]->sh_addr - dest);
91422028508SToomas Soome dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr);
91522028508SToomas Soome
91622028508SToomas Soome err = kern_pread(ef.fd, dest, sh_data[1]->sh_size,
91722028508SToomas Soome sh_data[1]->sh_offset);
91822028508SToomas Soome if (err != 0) {
91922028508SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
92022028508SToomas Soome "load_modmetadata: unable to load data: %d\n", err);
92122028508SToomas Soome goto out;
92222028508SToomas Soome }
92322028508SToomas Soome
92422028508SToomas Soome err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end);
92522028508SToomas Soome if (err != 0) {
92622028508SToomas Soome printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
92722028508SToomas Soome "load_modmetadata: unable to parse metadata: %d\n", err);
92822028508SToomas Soome goto out;
92922028508SToomas Soome }
93022028508SToomas Soome
93122028508SToomas Soome out:
93222028508SToomas Soome if (shstrtab != NULL)
93322028508SToomas Soome free(shstrtab);
93422028508SToomas Soome if (shdr != NULL)
93522028508SToomas Soome free(shdr);
93622028508SToomas Soome if (ef.firstpage != NULL)
93722028508SToomas Soome free(ef.firstpage);
93822028508SToomas Soome if (ef.fd != -1)
93922028508SToomas Soome close(ef.fd);
94022028508SToomas Soome return (err);
94122028508SToomas Soome }
94222028508SToomas Soome
94322028508SToomas Soome int
__elfN(parse_modmetadata)94422028508SToomas Soome __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef,
94522028508SToomas Soome Elf_Addr p_start, Elf_Addr p_end)
94622028508SToomas Soome {
94722028508SToomas Soome struct mod_metadata md;
94822028508SToomas Soome #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
94922028508SToomas Soome struct mod_metadata64 md64;
95022028508SToomas Soome #elif defined(__amd64__) && __ELF_WORD_SIZE == 32
95122028508SToomas Soome struct mod_metadata32 md32;
95222028508SToomas Soome #endif
95322028508SToomas Soome struct mod_depend *mdepend;
95422028508SToomas Soome struct mod_version mver;
95522028508SToomas Soome char *s;
95622028508SToomas Soome int error, modcnt, minfolen;
95722028508SToomas Soome Elf_Addr v, p;
95822028508SToomas Soome
95922028508SToomas Soome modcnt = 0;
96022028508SToomas Soome p = p_start;
96122028508SToomas Soome while (p < p_end) {
96222028508SToomas Soome COPYOUT(p, &v, sizeof (v));
96322028508SToomas Soome error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof (v));
96422028508SToomas Soome if (error == EOPNOTSUPP)
96522028508SToomas Soome v += ef->off;
96622028508SToomas Soome else if (error != 0)
96722028508SToomas Soome return (error);
96822028508SToomas Soome #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
96922028508SToomas Soome COPYOUT(v, &md64, sizeof (md64));
97022028508SToomas Soome error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof (md64));
97122028508SToomas Soome if (error == EOPNOTSUPP) {
97222028508SToomas Soome md64.md_cval += ef->off;
97322028508SToomas Soome md64.md_data += ef->off;
97422028508SToomas Soome } else if (error != 0)
97522028508SToomas Soome return (error);
97622028508SToomas Soome md.md_version = md64.md_version;
97722028508SToomas Soome md.md_type = md64.md_type;
97822028508SToomas Soome md.md_cval = (const char *)(uintptr_t)md64.md_cval;
97922028508SToomas Soome md.md_data = (void *)(uintptr_t)md64.md_data;
98022028508SToomas Soome #elif defined(__amd64__) && __ELF_WORD_SIZE == 32
98122028508SToomas Soome COPYOUT(v, &md32, sizeof (md32));
98222028508SToomas Soome error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof (md32));
98322028508SToomas Soome if (error == EOPNOTSUPP) {
98422028508SToomas Soome md32.md_cval += ef->off;
98522028508SToomas Soome md32.md_data += ef->off;
98622028508SToomas Soome } else if (error != 0)
98722028508SToomas Soome return (error);
98822028508SToomas Soome md.md_version = md32.md_version;
98922028508SToomas Soome md.md_type = md32.md_type;
99022028508SToomas Soome md.md_cval = (const char *)(uintptr_t)md32.md_cval;
99122028508SToomas Soome md.md_data = (void *)(uintptr_t)md32.md_data;
99222028508SToomas Soome #else
99322028508SToomas Soome COPYOUT(v, &md, sizeof (md));
99422028508SToomas Soome error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof (md));
99522028508SToomas Soome if (error == EOPNOTSUPP) {
99622028508SToomas Soome md.md_cval += ef->off;
997*de11c9b6SToomas Soome md.md_data = (void *)((uintptr_t)md.md_data +
998*de11c9b6SToomas Soome (uintptr_t)ef->off);
99922028508SToomas Soome } else if (error != 0)
100022028508SToomas Soome return (error);
100122028508SToomas Soome #endif
100222028508SToomas Soome p += sizeof (Elf_Addr);
100322028508SToomas Soome switch (md.md_type) {
100422028508SToomas Soome case MDT_DEPEND:
100522028508SToomas Soome if (ef->kernel) /* kernel must not depend on anything */
100622028508SToomas Soome break;
100722028508SToomas Soome s = strdupout((vm_offset_t)md.md_cval);
100822028508SToomas Soome minfolen = sizeof (*mdepend) + strlen(s) + 1;
100922028508SToomas Soome mdepend = malloc(minfolen);
101022028508SToomas Soome if (mdepend == NULL)
1011*de11c9b6SToomas Soome return (ENOMEM);
1012*de11c9b6SToomas Soome COPYOUT((vm_offset_t)md.md_data, mdepend,
1013*de11c9b6SToomas Soome sizeof (*mdepend));
101422028508SToomas Soome strcpy((char *)(mdepend + 1), s);
101522028508SToomas Soome free(s);
1016*de11c9b6SToomas Soome file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen,
1017*de11c9b6SToomas Soome mdepend);
101822028508SToomas Soome free(mdepend);
101922028508SToomas Soome break;
102022028508SToomas Soome case MDT_VERSION:
102122028508SToomas Soome s = strdupout((vm_offset_t)md.md_cval);
102222028508SToomas Soome COPYOUT((vm_offset_t)md.md_data, &mver, sizeof (mver));
102322028508SToomas Soome file_addmodule(fp, s, mver.mv_version, NULL);
102422028508SToomas Soome free(s);
102522028508SToomas Soome modcnt++;
102622028508SToomas Soome break;
102722028508SToomas Soome }
102822028508SToomas Soome }
102922028508SToomas Soome if (modcnt == 0) {
103022028508SToomas Soome s = fake_modname(fp->f_name);
103122028508SToomas Soome file_addmodule(fp, s, 1, NULL);
103222028508SToomas Soome free(s);
103322028508SToomas Soome }
1034*de11c9b6SToomas Soome return (0);
103522028508SToomas Soome }
103622028508SToomas Soome
103722028508SToomas Soome static unsigned long
elf_hash(const char * name)103822028508SToomas Soome elf_hash(const char *name)
103922028508SToomas Soome {
104022028508SToomas Soome const unsigned char *p = (const unsigned char *) name;
104122028508SToomas Soome unsigned long h = 0;
104222028508SToomas Soome unsigned long g;
104322028508SToomas Soome
104422028508SToomas Soome while (*p != '\0') {
104522028508SToomas Soome h = (h << 4) + *p++;
104622028508SToomas Soome if ((g = h & 0xf0000000) != 0)
104722028508SToomas Soome h ^= g >> 24;
104822028508SToomas Soome h &= ~g;
104922028508SToomas Soome }
1050*de11c9b6SToomas Soome return (h);
105122028508SToomas Soome }
105222028508SToomas Soome
1053*de11c9b6SToomas Soome static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE)
1054*de11c9b6SToomas Soome "_lookup_symbol: corrupt symbol table\n";
1055*de11c9b6SToomas Soome
105622028508SToomas Soome int
__elfN(lookup_symbol)105722028508SToomas Soome __elfN(lookup_symbol)(struct preloaded_file *fp __unused, elf_file_t ef,
105822028508SToomas Soome const char *name, Elf_Sym *symp)
105922028508SToomas Soome {
106022028508SToomas Soome Elf_Hashelt symnum;
106122028508SToomas Soome Elf_Sym sym;
106222028508SToomas Soome char *strp;
106322028508SToomas Soome unsigned long hash;
106422028508SToomas Soome
106522028508SToomas Soome hash = elf_hash(name);
106622028508SToomas Soome COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof (symnum));
106722028508SToomas Soome
106822028508SToomas Soome while (symnum != STN_UNDEF) {
106922028508SToomas Soome if (symnum >= ef->nchains) {
107022028508SToomas Soome printf(__elfN(bad_symtable));
1071*de11c9b6SToomas Soome return (ENOENT);
107222028508SToomas Soome }
107322028508SToomas Soome
107422028508SToomas Soome COPYOUT(ef->symtab + symnum, &sym, sizeof (sym));
107522028508SToomas Soome if (sym.st_name == 0) {
107622028508SToomas Soome printf(__elfN(bad_symtable));
1077*de11c9b6SToomas Soome return (ENOENT);
107822028508SToomas Soome }
107922028508SToomas Soome
108022028508SToomas Soome strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name));
108122028508SToomas Soome if (strcmp(name, strp) == 0) {
108222028508SToomas Soome free(strp);
108322028508SToomas Soome if (sym.st_shndx != SHN_UNDEF ||
108422028508SToomas Soome (sym.st_value != 0 &&
108522028508SToomas Soome ELF_ST_TYPE(sym.st_info) == STT_FUNC)) {
108622028508SToomas Soome *symp = sym;
1087*de11c9b6SToomas Soome return (0);
108822028508SToomas Soome }
1089*de11c9b6SToomas Soome return (ENOENT);
109022028508SToomas Soome }
109122028508SToomas Soome free(strp);
109222028508SToomas Soome COPYOUT(&ef->chains[symnum], &symnum, sizeof (symnum));
109322028508SToomas Soome }
1094*de11c9b6SToomas Soome return (ENOENT);
109522028508SToomas Soome }
109622028508SToomas Soome
109722028508SToomas Soome /*
109822028508SToomas Soome * Apply any intra-module relocations to the value. p is the load address
109922028508SToomas Soome * of the value and val/len is the value to be modified. This does NOT modify
110022028508SToomas Soome * the image in-place, because this is done by kern_linker later on.
110122028508SToomas Soome *
110222028508SToomas Soome * Returns EOPNOTSUPP if no relocation method is supplied.
110322028508SToomas Soome */
110422028508SToomas Soome static int
__elfN(reloc_ptr)1105*de11c9b6SToomas Soome __elfN(reloc_ptr)(struct preloaded_file *mp __unused, elf_file_t ef,
110622028508SToomas Soome Elf_Addr p, void *val, size_t len)
110722028508SToomas Soome {
110822028508SToomas Soome size_t n;
110922028508SToomas Soome Elf_Rela a;
111022028508SToomas Soome Elf_Rel r;
111122028508SToomas Soome int error;
111222028508SToomas Soome
111322028508SToomas Soome /*
111422028508SToomas Soome * The kernel is already relocated, but we still want to apply
111522028508SToomas Soome * offset adjustments.
111622028508SToomas Soome */
111722028508SToomas Soome if (ef->kernel)
111822028508SToomas Soome return (EOPNOTSUPP);
111922028508SToomas Soome
112022028508SToomas Soome for (n = 0; n < ef->relsz / sizeof (r); n++) {
112122028508SToomas Soome COPYOUT(ef->rel + n, &r, sizeof (r));
112222028508SToomas Soome
112322028508SToomas Soome error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL,
112422028508SToomas Soome ef->off, p, val, len);
112522028508SToomas Soome if (error != 0)
112622028508SToomas Soome return (error);
112722028508SToomas Soome }
112822028508SToomas Soome for (n = 0; n < ef->relasz / sizeof (a); n++) {
112922028508SToomas Soome COPYOUT(ef->rela + n, &a, sizeof (a));
113022028508SToomas Soome
113122028508SToomas Soome error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA,
113222028508SToomas Soome ef->off, p, val, len);
113322028508SToomas Soome if (error != 0)
113422028508SToomas Soome return (error);
113522028508SToomas Soome }
113622028508SToomas Soome
113722028508SToomas Soome return (0);
113822028508SToomas Soome }
113922028508SToomas Soome
114022028508SToomas Soome static Elf_Addr
__elfN(symaddr)114122028508SToomas Soome __elfN(symaddr)(struct elf_file *ef __unused, Elf_Size symidx __unused)
114222028508SToomas Soome {
114322028508SToomas Soome /* Symbol lookup by index not required here. */
114422028508SToomas Soome return (0);
114522028508SToomas Soome }
1146