aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/boot/common/Makefile.inc5
-rw-r--r--sys/boot/common/bootstrap.h10
-rw-r--r--sys/boot/common/load_elf.c103
-rw-r--r--sys/boot/common/reloc_elf.c199
-rw-r--r--sys/boot/common/reloc_elf32.c6
-rw-r--r--sys/boot/common/reloc_elf64.c6
6 files changed, 294 insertions, 35 deletions
diff --git a/sys/boot/common/Makefile.inc b/sys/boot/common/Makefile.inc
index b2227c80ecd9..945a1a74a112 100644
--- a/sys/boot/common/Makefile.inc
+++ b/sys/boot/common/Makefile.inc
@@ -6,12 +6,13 @@ SRCS+= module.c panic.c
.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64"
SRCS+= load_elf32.c load_elf64.c
+SRCS+= reloc_elf32.c reloc_elf64.c
.endif
.if ${MACHINE_ARCH} == "powerpc"
-SRCS+= load_elf32.c
+SRCS+= load_elf32.c reloc_elf32.c
.endif
.if ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "ia64" || ${MACHINE_ARCH} == "alpha"
-SRCS+= load_elf64.c
+SRCS+= load_elf64.c reloc_elf64.c
.endif
.if defined(LOADER_NET_SUPPORT)
diff --git a/sys/boot/common/bootstrap.h b/sys/boot/common/bootstrap.h
index 21845ceab3af..e79172a0b850 100644
--- a/sys/boot/common/bootstrap.h
+++ b/sys/boot/common/bootstrap.h
@@ -235,7 +235,17 @@ int file_addmodule(struct preloaded_file *fp, char *modname, int version,
/* MI module loaders */
#ifdef __elfN
+/* Relocation types. */
+#define ELF_RELOC_REL 1
+#define ELF_RELOC_RELA 2
+
+struct elf_file;
+typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Word symidx);
+
int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result);
+int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr,
+ const void *reldata, int reltype, Elf_Addr relbase,
+ Elf_Addr dataaddr, void *data, size_t len);
#endif
/*
diff --git a/sys/boot/common/load_elf.c b/sys/boot/common/load_elf.c
index 27cc527beda2..a44145b5e520 100644
--- a/sys/boot/common/load_elf.c
+++ b/sys/boot/common/load_elf.c
@@ -58,6 +58,8 @@ typedef struct elf_file {
Elf_Hashelt nchains;
Elf_Hashelt *buckets;
Elf_Hashelt *chains;
+ Elf_Rel *rel;
+ size_t relsz;
Elf_Rela *rela;
size_t relasz;
char *strtab;
@@ -71,11 +73,10 @@ typedef struct elf_file {
static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t loadaddr);
static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym);
-#ifdef __sparc__
-static void __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
- void *p, void *val, size_t len);
-#endif
+static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
+ Elf_Addr p, void *val, size_t len);
static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef);
+static symaddr_fn __elfN(symaddr);
static char *fake_modname(const char *name);
const char *__elfN(kerneltype) = "elf kernel";
@@ -481,6 +482,12 @@ nosyms:
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;
@@ -563,7 +570,7 @@ __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef)
struct mod_version mver;
Elf_Sym sym;
char *s;
- int modcnt, minfolen;
+ int error, modcnt, minfolen;
Elf_Addr v, p, p_stop;
if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0)
@@ -576,25 +583,31 @@ __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef)
modcnt = 0;
while (p < p_stop) {
COPYOUT(p, &v, sizeof(v));
-#ifdef __sparc64__
- __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v));
-#else
- v += ef->off;
-#endif
+ error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v));
+ if (error == EOPNOTSUPP)
+ v += ef->off;
+ else if (error != 0)
+ return (error);
#if defined(__i386__) && __ELF_WORD_SIZE == 64
COPYOUT(v, &md64, sizeof(md64));
+ error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64));
+ if (error == EOPNOTSUPP) {
+ md64.md_cval += ef->off;
+ md64.md_data += ef->off;
+ } else if (error != 0)
+ return (error);
md.md_version = md64.md_version;
md.md_type = md64.md_type;
- md.md_cval = (const char *)(uintptr_t)(md64.md_cval + ef->off);
- md.md_data = (void *)(uintptr_t)(md64.md_data + ef->off);
+ md.md_cval = (const char *)(uintptr_t)md64.md_cval;
+ md.md_data = (void *)(uintptr_t)md64.md_data;
#else
COPYOUT(v, &md, sizeof(md));
-#ifdef __sparc64__
- __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md));
-#else
- md.md_cval += ef->off;
- md.md_data += ef->off;
-#endif
+ error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md));
+ if (error == EOPNOTSUPP) {
+ md.md_cval += ef->off;
+ md.md_data += ef->off;
+ } else if (error != 0)
+ return (error);
#endif
p += sizeof(Elf_Addr);
switch(md.md_type) {
@@ -687,29 +700,53 @@ __elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef, const char* name
return ENOENT;
}
-#ifdef __sparc__
/*
- * Apply any intra-module relocations to the value. *p is the load address
+ * Apply any intra-module relocations to the value. p is the load address
* of the value and val/len is the value to be modified. This does NOT modify
* the image in-place, because this is done by kern_linker later on.
+ *
+ * Returns EOPNOTSUPP if no relocation method is supplied.
*/
-static void
+static int
__elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
- void *p, void *val, size_t len)
+ Elf_Addr p, void *val, size_t len)
{
- Elf_Addr off = (Elf_Addr)p - ef->off, word;
size_t n;
- Elf_Rela r;
+ Elf_Rela a;
+ Elf_Rel r;
+ int error;
- for (n = 0; n < ef->relasz / sizeof(r); n++) {
- COPYOUT(ef->rela + n, &r, sizeof(r));
+ /*
+ * The kernel is already relocated, but we still want to apply
+ * offset adjustments.
+ */
+ if (ef->kernel)
+ return (EOPNOTSUPP);
- if (r.r_offset >= off && r.r_offset < off + len &&
- ELF_R_TYPE(r.r_info) == R_SPARC_RELATIVE) {
- word = ef->off + r.r_addend;
- bcopy(&word, (char *)val + (r.r_offset - off),
- sizeof(word));
- }
+ for (n = 0; n < ef->relsz / sizeof(r); n++) {
+ COPYOUT(ef->rel + n, &r, sizeof(r));
+
+ error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL,
+ ef->off, p, val, len);
+ if (error != 0)
+ return (error);
+ }
+ for (n = 0; n < ef->relasz / sizeof(a); n++) {
+ COPYOUT(ef->rela + n, &a, sizeof(a));
+
+ error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA,
+ ef->off, p, val, len);
+ if (error != 0)
+ return (error);
}
+
+ return (0);
+}
+
+static Elf_Addr
+__elfN(symaddr)(struct elf_file *ef, Elf_Word symidx)
+{
+
+ /* Symbol lookup by index not required here. */
+ return (0);
}
-#endif
diff --git a/sys/boot/common/reloc_elf.c b/sys/boot/common/reloc_elf.c
new file mode 100644
index 000000000000..d6ff99944318
--- /dev/null
+++ b/sys/boot/common/reloc_elf.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright 1996-1998 John D. Polstra.
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <machine/elf.h>
+
+#include <errno.h>
+#include <stand.h>
+
+#define FREEBSD_ELF
+#include <link.h>
+
+#include "bootstrap.h"
+
+#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l)
+
+/*
+ * Apply a single intra-module relocation to the data. `relbase' is the
+ * target relocation base for the section (i.e. it corresponds to where
+ * r_offset == 0). `dataaddr' is the relocated address corresponding to
+ * the start of the data, and `len' is the number of bytes.
+ */
+int
+__elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata,
+ int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len)
+{
+#ifdef __sparc__
+ Elf_Word w;
+ const Elf_Rela *a;
+
+ switch (reltype) {
+ case ELF_RELOC_RELA:
+ a = reldata;
+ if (relbase + a->r_offset >= dataaddr &&
+ relbase + a->r_offset < dataaddr + len) {
+ switch (ELF_R_TYPE(a->r_info)) {
+ case R_SPARC_RELATIVE:
+ w = relbase + a->r_addend;
+ bcopy(&w, (u_char *)data + (relbase +
+ a->r_offset - dataaddr), sizeof(w));
+ break;
+ default:
+ printf("\nunhandled relocation type %u\n",
+ (u_int)ELF_R_TYPE(a->r_info));
+ return (EFTYPE);
+ }
+ }
+ break;
+ }
+
+ return (0);
+#elif defined(__i386__) && __ELF_WORD_SIZE == 64
+ Elf64_Addr *where, val;
+ Elf_Addr addend, addr;
+ Elf_Word rtype, symidx;
+ const Elf_Rel *rel;
+ const Elf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_RELOC_REL:
+ rel = (const Elf_Rel *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
+ dataaddr);
+ addend = 0;
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+ addend = 0;
+ break;
+ case ELF_RELOC_RELA:
+ rela = (const Elf_Rela *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
+ dataaddr);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
+ return (0);
+
+ if (reltype == ELF_RELOC_REL)
+ addend = *where;
+
+/* XXX, definitions not available on i386. */
+#define R_X86_64_64 1
+#define R_X86_64_RELATIVE 8
+
+ switch (rtype) {
+ case R_X86_64_64: /* S + A */
+ addr = symaddr(ef, symidx);
+ if (addr == 0)
+ return (ESRCH);
+ val = addr + addend;
+ *where = val;
+ break;
+ case R_X86_64_RELATIVE:
+ addr = (Elf_Addr)addend + relbase;
+ val = addr;
+ *where = val;
+ break;
+ default:
+ printf("\nunhandled relocation type %u\n", (u_int)rtype);
+ return (EFTYPE);
+ }
+
+ return (0);
+#elif defined(__i386__) && __ELF_WORD_SIZE == 32
+ Elf_Addr addend, addr, *where, val;
+ Elf_Word rtype, symidx;
+ const Elf_Rel *rel;
+ const Elf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_RELOC_REL:
+ rel = (const Elf_Rel *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
+ dataaddr);
+ addend = 0;
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+ addend = 0;
+ break;
+ case ELF_RELOC_RELA:
+ rela = (const Elf_Rela *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
+ dataaddr);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
+ return (0);
+
+ if (reltype == ELF_RELOC_REL)
+ addend = *where;
+
+/* XXX, definitions not available on amd64. */
+#define R_386_32 1 /* Add symbol value. */
+#define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */
+#define R_386_RELATIVE 8 /* Add load address of shared object. */
+
+ switch (rtype) {
+ case R_386_RELATIVE:
+ addr = addend + relbase;
+ *where = addr;
+ break;
+ case R_386_32: /* S + A */
+ addr = symaddr(ef, symidx);
+ if (addr == 0)
+ return (ESRCH);
+ val = addr + addend;
+ *where = val;
+ break;
+ default:
+ printf("\nunhandled relocation type %u\n", (u_int)rtype);
+ return (EFTYPE);
+ }
+
+ return (0);
+#else
+ return (EOPNOTSUPP);
+#endif
+}
diff --git a/sys/boot/common/reloc_elf32.c b/sys/boot/common/reloc_elf32.c
new file mode 100644
index 000000000000..03d9d73bab7f
--- /dev/null
+++ b/sys/boot/common/reloc_elf32.c
@@ -0,0 +1,6 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 32
+
+#include "reloc_elf.c"
diff --git a/sys/boot/common/reloc_elf64.c b/sys/boot/common/reloc_elf64.c
new file mode 100644
index 000000000000..c8dcf2a36b0d
--- /dev/null
+++ b/sys/boot/common/reloc_elf64.c
@@ -0,0 +1,6 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+
+#include "reloc_elf.c"