diff options
author | Ian Dowse <iedowse@FreeBSD.org> | 2004-08-28 23:03:05 +0000 |
---|---|---|
committer | Ian Dowse <iedowse@FreeBSD.org> | 2004-08-28 23:03:05 +0000 |
commit | 45b8d7c46eee9babdf3277421561f69ce80f68a4 (patch) | |
tree | a007fb362198e8ded37c0b9b88dfba7840982665 /sys | |
parent | 11e9b7ef700602c024ead01b8ed33b6f769f0895 (diff) | |
download | src-45b8d7c46eee9babdf3277421561f69ce80f68a4.tar.gz src-45b8d7c46eee9babdf3277421561f69ce80f68a4.zip |
Notes
Diffstat (limited to 'sys')
-rw-r--r-- | sys/boot/common/Makefile.inc | 5 | ||||
-rw-r--r-- | sys/boot/common/bootstrap.h | 10 | ||||
-rw-r--r-- | sys/boot/common/load_elf.c | 103 | ||||
-rw-r--r-- | sys/boot/common/reloc_elf.c | 199 | ||||
-rw-r--r-- | sys/boot/common/reloc_elf32.c | 6 | ||||
-rw-r--r-- | sys/boot/common/reloc_elf64.c | 6 |
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" |