Implement elf_reloc(). The RT specification says that we can expect

both Elf_Rel and Elf_Rela types of relocation, so handle them both
even though we only have Rel_Rela ATM. We don't handle 32-bit and
big-endian variants yet. Support for that is not trivial enough to
implement it without any evidence that we ever need it in the near
future.

For the FPTR relocations, we currently use the fptr_storage used by
_reloc() is locore.s. This is in no way a real solution, but for now
provides the service we need to get the basics going.

A static recursive function lookup_fdesc() is used to find the address
of a function in a way that keeps track of the load module so that
we can get the correct GP value if we need to construct an OPD (ie
there's no OPD yet for the function.

For simplicity, we create an OPD for the IPLT relocations as well and
simply fill the user provided function descriptor from the OPD. Since
the the official descriptors are unique, this has no bad side effects.
Note that we ignore the addend for FPTR relocations, but use the
addend for IPLT relocations as an offset to the function address.

This commit allows us to load and relocate modules and modules appear
to work correctly, although we probably need to make sure that we set
GP correctly in all cases when we have inter-module calls. This
especially applies to assembly coded functions that have cross module
calls.
This commit is contained in:
Marcel Moolenaar 2002-04-21 21:27:57 +00:00
parent 8420105927
commit 0f9d8c9b27

View File

@ -36,13 +36,57 @@
#include <sys/linker.h>
#include <machine/elf.h>
Elf_Addr link_elf_get_gp(linker_file_t);
extern Elf_Addr fptr_storage[];
static Elf_Addr
lookup_fdesc(linker_file_t lf, const char *sym)
{
Elf_Addr addr;
int i;
static int eot = 0;
addr = (Elf_Addr)linker_file_lookup_symbol(lf, sym, 0);
if (addr == NULL) {
for (i = 0; i < lf->ndeps; i++) {
addr = lookup_fdesc(lf->deps[i], sym);
if (addr != NULL)
return (addr);
}
return (NULL);
}
if (eot)
return (NULL);
/*
* Lookup and/or construct OPD
*/
for (i = 0; i < 8192; i += 2) {
if (fptr_storage[i] == addr)
return (Elf_Addr)(fptr_storage + i);
if (fptr_storage[i] == 0) {
fptr_storage[i] = addr;
fptr_storage[i+1] = link_elf_get_gp(lf);
return (Elf_Addr)(fptr_storage + i);
}
}
printf("%s: fptr table full\n", __func__);
eot = 1;
return (NULL);
}
/* Process one elf relocation with addend. */
int
elf_reloc(linker_file_t lf, const void *data, int type, const char *sym)
{
Elf_Addr relocbase = (Elf_Addr) lf->address;
Elf_Addr relocbase = (Elf_Addr)lf->address;
Elf_Addr *where;
Elf_Addr addend;
Elf_Addr addend, addr;
Elf_Word rtype;
const Elf_Rel *rel;
const Elf_Rela *rela;
@ -50,26 +94,70 @@ elf_reloc(linker_file_t lf, const void *data, int type, const char *sym)
switch (type) {
case ELF_RELOC_REL:
rel = (const Elf_Rel *)data;
where = (Elf_Addr *) (relocbase + rel->r_offset);
addend = *where;
where = (Elf_Addr *)(relocbase + rel->r_offset);
rtype = ELF_R_TYPE(rel->r_info);
switch (rtype) {
case R_IA64_DIR64LSB:
case R_IA64_FPTR64LSB:
case R_IA64_REL64LSB:
addend = *where;
break;
default:
addend = 0;
break;
}
break;
case ELF_RELOC_RELA:
rela = (const Elf_Rela *)data;
where = (Elf_Addr *) (relocbase + rela->r_offset);
addend = rela->r_addend;
where = (Elf_Addr *)(relocbase + rela->r_offset);
rtype = ELF_R_TYPE(rela->r_info);
addend = rela->r_addend;
break;
default:
panic("elf_reloc: unknown relocation mode %d\n", type);
panic("%s: invalid ELF relocation (0x%x)\n", __func__, type);
}
switch (rtype) {
default:
printf("kldload: unexpected relocation type %d\n",
(int) rtype);
case R_IA64_NONE:
break;
case R_IA64_DIR64LSB: /* word64 LSB S + A */
if (sym == NULL)
return -1;
addr = (Elf_Addr)linker_file_lookup_symbol(lf, sym, 1);
if (addr == 0)
return -1;
*where = addr + addend;
break;
case R_IA64_FPTR64LSB: /* word64 LSB @fptr(S + A) */
if (sym == NULL)
return -1;
if (addend != 0) {
printf("%s: addend ignored for OPD relocation\n",
__func__);
}
addr = lookup_fdesc(lf, sym);
if (addr == 0)
return -1;
*where = addr;
break;
case R_IA64_REL64LSB: /* word64 LSB BD + A */
*where = relocbase + addend;
break;
case R_IA64_IPLTLSB:
if (sym == NULL)
return -1;
/* lookup_fdesc() returns the address of the OPD. */
addr = lookup_fdesc(lf, sym);
if (addr == 0)
return -1;
where[0] = *((Elf_Addr*)addr) + addend;
where[1] = *((Elf_Addr*)addr + 1);
break;
default:
printf("%s: unknown relocation (0x%x)\n", __func__,
(int)rtype);
return -1;
}
return(0);
}