summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf/mips/reloc.c
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2008-04-04 20:59:26 +0000
committerimp <imp@FreeBSD.org>2008-04-04 20:59:26 +0000
commitab840fe874374f3f4afe7ea4b7288127e4690a79 (patch)
treebd5448ef61276b8447bde82b8d4e0a807873e873 /libexec/rtld-elf/mips/reloc.c
parent64685606b60037cb77dad276001feb59198bab4f (diff)
downloadFreeBSD-src-ab840fe874374f3f4afe7ea4b7288127e4690a79.zip
FreeBSD-src-ab840fe874374f3f4afe7ea4b7288127e4690a79.tar.gz
MFp4: Add mips support for dynamic linking.
This code came from the merged mips2 and Juniper mips repositories. Warner Losh, Randall Seager, Oleksandr Tymoshenko and Olivier Houchard worked to merge, debug and integrate this code. This code may also contain code derived from NetBSD.
Diffstat (limited to 'libexec/rtld-elf/mips/reloc.c')
-rw-r--r--libexec/rtld-elf/mips/reloc.c357
1 files changed, 357 insertions, 0 deletions
diff --git a/libexec/rtld-elf/mips/reloc.c b/libexec/rtld-elf/mips/reloc.c
new file mode 100644
index 0000000..ab5dff3
--- /dev/null
+++ b/libexec/rtld-elf/mips/reloc.c
@@ -0,0 +1,357 @@
+/* $NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "debug.h"
+#include "rtld.h"
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
+ obj->pltgot[1] |= (Elf_Addr) obj;
+ }
+}
+
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ /* Do nothing */
+ return 0;
+}
+
+void _rtld_bind_start(void);
+void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
+
+int open();
+int _open();
+
+static __inline Elf_Addr
+load_ptr(void *where)
+{
+ Elf_Addr res;
+
+ memcpy(&res, where, sizeof(res));
+
+ return (res);
+}
+
+void
+store_ptr(void *where, Elf_Addr val)
+{
+
+ memcpy(where, &val, sizeof(val));
+}
+
+void
+_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rel *rel = 0, *rellim;
+ Elf_Addr relsz = 0;
+ const Elf_Sym *symtab = NULL, *sym;
+ Elf_Addr *where;
+ Elf_Addr *got = NULL;
+ Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0;
+ int i;
+
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_REL:
+ rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ relsz = dynp->d_un.d_val;
+ break;
+ case DT_SYMTAB:
+ symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_PLTGOT:
+ got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_MIPS_LOCAL_GOTNO:
+ local_gotno = dynp->d_un.d_val;
+ break;
+ case DT_MIPS_SYMTABNO:
+ symtabno = dynp->d_un.d_val;
+ break;
+ case DT_MIPS_GOTSYM:
+ gotsym = dynp->d_un.d_val;
+ break;
+ }
+ }
+
+ i = (got[1] & 0x80000000) ? 2 : 1;
+ /* Relocate the local GOT entries */
+ got += i;
+ for (; i < local_gotno; i++) {
+ *got++ += relocbase;
+ }
+
+ sym = symtab + gotsym;
+ /* Now do the global GOT entries */
+ for (i = gotsym; i < symtabno; i++) {
+ *got = sym->st_value + relocbase;
+ ++sym;
+ ++got;
+ }
+
+ rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
+ for (; rel < rellim; rel++) {
+ where = (void *)(relocbase + rel->r_offset);
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_TYPE(NONE):
+ break;
+
+ case R_TYPE(REL32):
+ assert(ELF_R_SYM(rel->r_info) < gotsym);
+ sym = symtab + ELF_R_SYM(rel->r_info);
+ assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL);
+ store_ptr(where, load_ptr(where) + relocbase);
+ break;
+
+ default:
+ abort();
+ break;
+ }
+ }
+}
+
+Elf_Addr
+_mips_rtld_bind(Obj_Entry *obj, Elf_Size reloff)
+{
+ Elf_Addr *got = obj->pltgot;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr target;
+
+ def = find_symdef(reloff, obj, &defobj, SYMLOOK_IN_PLT, NULL);
+ if (def == NULL)
+ _rtld_error("bind failed no symbol");
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ dbg("bind now/fixup at %s sym # %d in %s --> was=%p new=%p",
+ obj->path,
+ reloff, defobj->strtab + def->st_name,
+ (void *)got[obj->local_gotno + reloff - obj->gotsym],
+ (void *)target);
+ got[obj->local_gotno + reloff - obj->gotsym] = target;
+ return (Elf_Addr)target;
+}
+
+/*
+ * It is possible for the compiler to emit relocations for unaligned data.
+ * We handle this situation with these inlines.
+ */
+#define RELOC_ALIGNED_P(x) \
+ (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
+
+/*
+ * Process non-PLT relocations
+ */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
+{
+ const Elf_Rel *rel;
+ const Elf_Rel *rellim;
+ Elf_Addr *got = obj->pltgot;
+ const Elf_Sym *sym, *def;
+ const Obj_Entry *defobj;
+ int i;
+
+ /* The relocation for the dynamic loader has already been done. */
+ if (obj == obj_rtld)
+ return (0);
+
+ i = (got[1] & 0x80000000) ? 2 : 1;
+
+ /* Relocate the local GOT entries */
+ got += i;
+ dbg("got:%p for %d entries adding %x",
+ got, obj->local_gotno, (uint32_t)obj->relocbase);
+ for (; i < obj->local_gotno; i++) {
+ *got += (Elf_Addr)obj->relocbase;
+ got++;
+ }
+ sym = obj->symtab + obj->gotsym;
+
+
+ dbg("got:%p for %d entries",
+ got, obj->symtabno);
+ /* Now do the global GOT entries */
+ for (i = obj->gotsym; i < obj->symtabno; i++) {
+ if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
+ sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) {
+ /*
+ * If there are non-PLT references to the function,
+ * st_value should be 0, forcing us to resolve the
+ * address immediately.
+ *
+ * XXX DANGER WILL ROBINSON!
+ * The linker is not outputting PLT slots for calls to
+ * functions that are defined in the same shared
+ * library. This is a bug, because it can screw up
+ * link ordering rules if the symbol is defined in
+ * more than one module. For now, if there is a
+ * definition, we fail the test above and force a full
+ * symbol lookup. This means that all intra-module
+ * calls are bound immediately. - mycroft, 2003/09/24
+ */
+ *got = sym->st_value + (Elf_Addr)obj->relocbase;
+ if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) {
+ dbg("Warning2, i:%d maps to relocbase address:%x",
+ i, (uint32_t)obj->relocbase);
+ }
+
+ } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) {
+ /* Symbols with index SHN_ABS are not relocated. */
+ if (sym->st_shndx != SHN_ABS) {
+ *got = sym->st_value +
+ (Elf_Addr)obj->relocbase;
+ if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) {
+ dbg("Warning3, i:%d maps to relocbase address:%x",
+ i, (uint32_t)obj->relocbase);
+ }
+ }
+ } else {
+ /* TODO: add cache here */
+ def = find_symdef(i, obj, &defobj, false, NULL);
+ if (def == NULL) {
+ dbg("Warning4, cant find symbole %d", i);
+ return -1;
+ }
+ *got = def->st_value + (Elf_Addr)defobj->relocbase;
+ if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) {
+ dbg("Warning4, i:%d maps to relocbase address:%x",
+ i, (uint32_t)obj->relocbase);
+ dbg("via first obj symbol %s",
+ obj->strtab + obj->symtab[i].st_name);
+ dbg("found in obj %p:%s",
+ defobj, defobj->path);
+ }
+ }
+ ++sym;
+ ++got;
+ }
+ got = obj->pltgot;
+ rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ void *where;
+ Elf_Addr tmp;
+ unsigned long symnum;
+
+ where = obj->relocbase + rel->r_offset;
+ symnum = ELF_R_SYM(rel->r_info);
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_TYPE(NONE):
+ break;
+
+ case R_TYPE(REL32):
+ /* 32-bit PC-relative reference */
+ def = obj->symtab + symnum;
+ tmp = load_ptr(where);
+ if (tmp == 0) {
+ def = find_symdef(symnum, obj, &defobj, false, NULL);
+ if (def == NULL) {
+ dbg("Warning5, cant find symbole %d:%s", (int)symnum,
+ obj->strtab + obj->symtab[symnum].st_name);
+ } else {
+ tmp = def->st_value + (Elf_Addr)defobj->relocbase;
+ dbg("Correctiong symnum:%d:%s to addr:%x", (int)symnum,
+ obj->strtab + obj->symtab[symnum].st_name,
+ (u_int32_t)tmp
+ );
+ }
+ } else {
+ tmp += (Elf_Addr)obj->relocbase;
+ }
+ store_ptr(where, tmp);
+ if (tmp == (Elf_Addr)obj->relocbase) {
+ dbg("rel sym %p falls on relocbase symidx:%x symbol:%s", rel,
+ (uint32_t)ELF_R_SYM(rel->r_info),
+ obj->strtab + obj->symtab[symnum].st_name
+ );
+ }
+ break;
+
+ default:
+ dbg("sym = %lu, type = %lu, offset = %p, "
+ "contents = %p, symbol = %s",
+ symnum, (u_long)ELF_R_TYPE(rel->r_info),
+ (void *)rel->r_offset, (void *)load_ptr(where),
+ obj->strtab + obj->symtab[symnum].st_name);
+ _rtld_error("%s: Unsupported relocation type %ld "
+ "in non-PLT relocations\n",
+ obj->path, (u_long) ELF_R_TYPE(rel->r_info));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ dbg("reloc_plt obj:%p pltrel:%p sz:%d", obj, obj->pltrel, (int)obj->pltrelsize);
+ dbg("gottable %p num syms:%d", obj->pltgot, obj->symtabno );
+ dbg("*****************************************************");
+ rellim = (const Elf_Rel *)((char *)obj->pltrel +
+ obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where;
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr )obj->relocbase;
+ }
+
+ return (0);
+}
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj)
+{
+ /* Do nothing */
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
+ const Obj_Entry *obj, const Elf_Rel *rel)
+{
+
+ /* Do nothing */
+
+ return target;
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+
+}
+
+void *
+__tls_get_addr(tls_index* ti)
+{
+ return (NULL);
+}
OpenPOWER on IntegriCloud