#define _DYN_LOADER
#include <sys/types.h>
#include <sys/exec_elf.h>
#include <sys/syscall.h>
#include <sys/unistd.h>
#include <machine/reloc.h>
#include "util.h"
#include "resolve.h"
int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
static inline void
_dl_reloc_ent(Elf_Addr r_addr, Elf_Addr value)
{
if ((r_addr & 7) == 0)
*(u_int64_t *)r_addr += value;
else {
u_int64_t robj;
_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
robj += value;
_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
}
}
int
_dl_md_reloc(elf_object_t *object, int rel, int relsz)
{
int i;
int numrel;
int fails = 0;
Elf_Addr loff;
Elf_Rel *relocs;
const Elf_Sym *sym, *this;
Elf_Addr prev_value = 0;
const Elf_Sym *prev_sym = NULL;
loff = object->obj_base;
numrel = object->Dyn.info[relsz] / sizeof(Elf_Rel);
relocs = (Elf_Rel *)(object->Dyn.info[rel]);
if (relocs == NULL)
return 0;
DL_DEB(("relocating %d\n", numrel));
for (i = 0; i < numrel; i++, relocs++) {
Elf_Addr r_addr = relocs->r_offset + loff;
const char *symn;
if (ELF_R_SYM(relocs->r_info) == 0xffffff)
continue;
sym = object->dyn.symtab;
sym += ELF_R_SYM(relocs->r_info);
symn = object->dyn.strtab + sym->st_name;
this = NULL;
if (ELF_R_SYM(relocs->r_info)) {
if (sym == prev_sym)
this = sym;
else if (!(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
ELF_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
struct sym_res sr;
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT,
sym, object);
if (sr.sym == NULL) {
if (ELF_ST_BIND(sym->st_info) !=
STB_WEAK)
fails++;
continue;
}
prev_sym = sym;
prev_value = sr.obj->obj_base +
sr.sym->st_value;
this = sym;
}
}
switch (ELF_R_TYPE(relocs->r_info)) {
case R_MIPS_REL32_64:
if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
(ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) )
_dl_reloc_ent(r_addr, loff + sym->st_value);
else if (this)
_dl_reloc_ent(r_addr, prev_value);
break;
case R_MIPS_NONE:
break;
default:
_dl_die("unsupported relocation '%llu'",
ELF_R_TYPE(relocs->r_info));
}
}
DL_DEB(("done %d fails\n", fails));
return fails;
}
extern void _dl_bind_start(void);
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
int i, n;
Elf_Addr loff;
Elf_Addr *gotp;
const Elf_Sym *symp;
const char *strt;
if (object->status & STAT_GOT_DONE)
return 0;
loff = object->obj_base;
strt = object->dyn.strtab;
gotp = object->dyn.pltgot;
n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];
DL_DEB(("loff: 0x%lx\n", (unsigned long)loff));
gotp[0] = (long)_dl_bind_start;
gotp[1] = (long)object;
for (i = 2; i < n; i++) {
gotp[i] += loff;
}
gotp += n;
symp = object->dyn.symtab;
symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] -
object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
while (n--) {
const char *symn = strt + symp->st_name;
struct sym_res sr;
if (symp->st_shndx == SHN_UNDEF &&
ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
if (symp->st_value == 0 || !lazy) {
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
symp, object);
if (sr.sym)
*gotp = sr.sym->st_value +
sr.obj->obj_base;
} else
*gotp = symp->st_value + loff;
} else if (symp->st_shndx == SHN_COMMON ||
symp->st_shndx == SHN_UNDEF) {
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
symp, object);
if (sr.sym)
*gotp = sr.sym->st_value + sr.obj->obj_base;
} else if ((ELF_ST_TYPE(symp->st_info) == STT_FUNC &&
symp->st_value != *gotp) ||
ELF_ST_VISIBILITY(symp->st_other) == STV_PROTECTED) {
*gotp += loff;
} else {
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
symp, object);
if (sr.sym)
*gotp = sr.sym->st_value + sr.obj->obj_base;
else
*gotp = symp->st_value + loff;
}
gotp++;
symp++;
}
object->status |= STAT_GOT_DONE;
return 0;
}
Elf_Addr
_dl_bind(elf_object_t *object, int symidx)
{
Elf_Addr *gotp = object->dyn.pltgot;
struct sym_res sr;
const Elf_Sym *sym;
const char *symn;
int64_t cookie = pcookie;
struct {
struct __kbind param;
Elf_Addr newval;
} buf;
int n;
sym = object->dyn.symtab;
sym += symidx;
symn = object->dyn.strtab + sym->st_name;
n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] -
object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
sym, object);
if (sr.sym == NULL)
_dl_die("lazy binding failed!");
buf.newval = sr.obj->obj_base + sr.sym->st_value;
if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
return buf.newval;
buf.param.kb_addr = &gotp[n + symidx];
buf.param.kb_size = sizeof(Elf_Addr);
{
register long syscall_num __asm("v0") = SYS_kbind;
register void *arg1 __asm("a0") = &buf;
register long arg2 __asm("a1") = sizeof(buf);
register long arg3 __asm("a2") = cookie;
__asm volatile("syscall" : "+r" (syscall_num)
: "r" (arg1), "r" (arg2), "r" (arg3)
: "v1", "a3", "memory");
}
return buf.newval;
}