Resolve the GOT before doing relocations. Then, when doing relocations, for

symbols in the global part of the symbol table, use the updated GOT entry
rather than doing a lookup.  (This provides the same effect as `-z combreloc'
on other platforms -- at most one lookup is done per symbol.)

Unfortunately, it is necessary to turn off lazy binding on MIPS.  As the
comment says:

                         * XXX DANGER WILL ROBINSON!
                         * You might think this is stupid, as it intentionally
                         * defeats lazy binding -- and you'd be right.
                         * Unfortunately, for lazy binding to work right, we
                         * need to a way to force the GOT slots used for
                         * function pointers to be resolved immediately.  This
                         * is supposed to be done automatically by the linker,
                         * by not outputting a PLT slot and setting st_value
                         * to 0, but GNU ld does not do so reliably.
This commit is contained in:
mycroft 2002-09-25 03:52:06 +00:00
parent 7798fe3a64
commit a3c903f7cb
1 changed files with 91 additions and 94 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: mips_reloc.c,v 1.33 2002/09/14 23:53:21 thorpej Exp $ */ /* $NetBSD: mips_reloc.c,v 1.34 2002/09/25 03:52:06 mycroft Exp $ */
/* /*
* Copyright 1997 Michael L. Hitch <mhitch@montana.edu> * Copyright 1997 Michael L. Hitch <mhitch@montana.edu>
@ -108,27 +108,6 @@ _rtld_relocate_nonplt_self(dynp, relocbase)
break; break;
} }
} }
rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
for (; rel < rellim; rel++) {
where = (Elf_Addr *)(relocbase + rel->r_offset);
switch (ELF_R_TYPE(rel->r_info)) {
case R_TYPE(NONE):
break;
case R_TYPE(REL32):
sym = symtab + ELF_R_SYM(rel->r_info);
if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
ELF_ST_TYPE(sym->st_info) == STT_SECTION)
*where += (Elf_Addr)(sym->st_value + relocbase);
else
abort();
break;
default:
abort();
}
}
i = (got[1] & 0x80000000) ? 2 : 1; i = (got[1] & 0x80000000) ? 2 : 1;
/* Relocate the local GOT entries */ /* Relocate the local GOT entries */
@ -142,6 +121,26 @@ _rtld_relocate_nonplt_self(dynp, relocbase)
++sym; ++sym;
++got; ++got;
} }
rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
for (; rel < rellim; rel++) {
where = (Elf_Addr *)(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(sym->st_info == STT_SECTION);
*where += (Elf_Addr)(sym->st_value + relocbase);
break;
default:
abort();
}
}
} }
/* /*
@ -182,6 +181,60 @@ _rtld_relocate_nonplt_objects(obj, self)
if (self) if (self)
return 0; return 0;
i = (got[1] & 0x80000000) ? 2 : 1;
/* Relocate the local GOT entries */
got += i;
for (; i < obj->local_gotno; i++)
*got++ += (Elf_Addr)obj->relocbase;
sym = obj->symtab + obj->gotsym;
/* Now do the global GOT entries */
for (i = obj->gotsym; i < obj->symtabno; i++) {
rdbg((" doing got %d sym %p (%s, %x)", i - obj->gotsym, sym,
sym->st_name + obj->strtab, *got));
if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
sym->st_shndx == SHN_UNDEF) {
/*
* XXX DANGER WILL ROBINSON!
* You might think this is stupid, as it intentionally
* defeats lazy binding -- and you'd be right.
* Unfortunately, for lazy binding to work right, we
* need to a way to force the GOT slots used for
* function pointers to be resolved immediately. This
* is supposed to be done automatically by the linker,
* by not outputting a PLT slot and setting st_value
* to 0, but GNU ld does not do so reliably.
*/
def = _rtld_find_symdef(i, obj, &defobj, true);
if (def == NULL)
return -1;
*got = def->st_value + (Elf_Addr)defobj->relocbase;
} else if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
sym->st_value != 0) {
/*
* If there are non-PLT references to the function,
* st_value should be 0, forcing us to resolve the
* address immediately.
*/
*got = sym->st_value + (Elf_Addr)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;
} else {
def = _rtld_find_symdef(i, obj, &defobj, true);
if (def == NULL)
return -1;
*got = def->st_value + (Elf_Addr)defobj->relocbase;
}
rdbg((" --> now %x", *got));
++sym;
++got;
}
got = obj->pltgot;
for (rel = obj->rel; rel < obj->rellim; rel++) { for (rel = obj->rel; rel < obj->rellim; rel++) {
Elf_Addr *where, tmp; Elf_Addr *where, tmp;
unsigned long symnum; unsigned long symnum;
@ -197,9 +250,16 @@ _rtld_relocate_nonplt_objects(obj, self)
/* 32-bit PC-relative reference */ /* 32-bit PC-relative reference */
def = obj->symtab + symnum; def = obj->symtab + symnum;
if (ELF_ST_BIND(def->st_info) == STB_LOCAL && if (symnum >= obj->gotsym) {
(ELF_ST_TYPE(def->st_info) == STT_SECTION || tmp = *where;
ELF_ST_TYPE(def->st_info) == STT_NOTYPE)) { tmp += got[obj->local_gotno + symnum - obj->gotsym];
*where = tmp;
rdbg(("REL32/G %s in %s --> %p in %s",
obj->strtab + def->st_name, obj->path,
(void *)tmp, obj->path));
break;
} else {
/* /*
* XXX: ABI DIFFERENCE! * XXX: ABI DIFFERENCE!
* *
@ -220,48 +280,18 @@ _rtld_relocate_nonplt_objects(obj, self)
* *
* --rkb, Oct 6, 2001 * --rkb, Oct 6, 2001
*/ */
if (__predict_true(RELOC_ALIGNED_P(where))) { tmp = *where;
tmp = *where;
if (def->st_info == STT_SECTION && if (def->st_info == STT_SECTION &&
tmp < def->st_value) tmp < def->st_value)
tmp += (Elf_Addr)def->st_value; tmp += (Elf_Addr)def->st_value;
tmp += (Elf_Addr)obj->relocbase; tmp += (Elf_Addr)obj->relocbase;
*where = tmp; *where = tmp;
} else {
tmp = load_ptr(where);
if (def->st_info == STT_SECTION &&
tmp < def->st_value)
tmp += (Elf_Addr)def->st_value;
tmp += (Elf_Addr)obj->relocbase;
store_ptr(where, tmp);
}
rdbg(("REL32 %s in %s --> %p in %s", rdbg(("REL32 %s in %s --> %p in %s",
obj->strtab + def->st_name, obj->path, obj->strtab + def->st_name, obj->path,
(void *)tmp, obj->path)); (void *)tmp, obj->path));
} else {
def = _rtld_find_symdef(symnum, obj, &defobj,
false);
if (def == NULL)
return -1;
if (__predict_true(RELOC_ALIGNED_P(where))) {
tmp = *where +
(Elf_Addr)(defobj->relocbase +
def->st_value);
*where = tmp;
} else {
tmp = load_ptr(where) +
(Elf_Addr)(defobj->relocbase +
def->st_value);
store_ptr(where, tmp);
}
rdbg(("REL32 %s in %s --> %p in %s",
obj->strtab + obj->symtab[symnum].st_name,
obj->path, (void *)tmp, defobj->path));
} }
break; break;
@ -278,39 +308,6 @@ _rtld_relocate_nonplt_objects(obj, self)
} }
} }
i = (got[1] & 0x80000000) ? 2 : 1;
/* Relocate the local GOT entries */
got += i;
for (; i < obj->local_gotno; i++)
*got++ += (Elf_Addr)obj->relocbase;
sym = obj->symtab + obj->gotsym;
/* Now do the global GOT entries */
for (i = obj->gotsym; i < obj->symtabno; i++) {
rdbg((" doing got %d sym %p (%s, %x)", i - obj->gotsym, sym,
sym->st_name + obj->strtab, *got));
if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
sym->st_value != 0)
/* The symbol table contains the address of the PLT
slot. However, sometimes a PLT slot is not
allocated, so we have to check st_value first. */
*got = sym->st_value + (Elf_Addr)obj->relocbase;
else if (ELF_ST_TYPE(sym->st_info) == STT_SECTION &&
ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
/* Symbols with index SHN_ABS are not relocated. */
if (sym->st_shndx != SHN_ABS)
*got = sym->st_value +
(Elf_Addr)obj->relocbase;
} else {
def = _rtld_find_symdef(i, obj, &defobj, true);
if (def == NULL)
return -1;
*got = def->st_value + (Elf_Addr)defobj->relocbase;
}
++sym;
++got;
}
return 0; return 0;
} }