#define _DYN_LOADER
#include <sys/types.h>
#include <sys/exec_elf.h>
#include <sys/syscall.h>
#include <sys/unistd.h>
#include <machine/pal.h>
#include <machine/reloc.h>
#include "util.h"
#include "resolve.h"
#define DT_PROC(n) ((n) - DT_LOPROC + DT_NUM)
int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
int
_dl_md_reloc(elf_object_t *object, int rel, int relasz)
{
long i;
long numrela;
long relrel;
int fails = 0;
Elf_Addr loff;
Elf_Addr prev_value = 0;
const Elf_Sym *prev_sym = NULL;
Elf_RelA *relas;
loff = object->obj_base;
numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
relrel = rel == DT_RELA ? object->relacount : 0;
relas = (Elf_RelA *)(object->Dyn.info[rel]);
if (relas == NULL)
return 0;
if (relrel > numrela)
_dl_die("relacount > numrel: %ld > %ld", relrel, numrela);
if (! object->Dyn.info[DT_PROC(DT_ALPHA_PLTRO)])
_dl_die("unsupported insecure PLT object");
for (i = 0; i < relrel; i++, relas++) {
Elf_Addr *r_addr;
r_addr = (Elf_Addr *)(relas->r_offset + loff);
if ((((Elf_Addr)r_addr) & 0x7) != 0) {
Elf_Addr tmp;
_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
tmp += loff;
_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
} else
*r_addr += loff;
}
for (; i < numrela; i++, relas++) {
Elf_Addr *r_addr;
struct sym_res sr;
const Elf_Sym *sym;
const char *symn;
r_addr = (Elf_Addr *)(relas->r_offset + loff);
if (ELF_R_SYM(relas->r_info) == 0xffffffff)
continue;
sym = object->dyn.symtab;
sym += ELF_R_SYM(relas->r_info);
symn = object->dyn.strtab + sym->st_name;
switch (ELF_R_TYPE(relas->r_info)) {
case R_TYPE(REFQUAD):
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
sym, object);
if (sr.sym == NULL)
goto resolve_failed;
*r_addr += sr.obj->obj_base + sr.sym->st_value +
relas->r_addend;
break;
case R_TYPE(RELATIVE):
if ((((Elf_Addr) r_addr) & 0x7) != 0) {
Elf_Addr tmp;
#if 0
_dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr,
ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff);
#endif
_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
tmp += loff;
_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
} else
*r_addr += loff;
break;
case R_TYPE(JMP_SLOT):
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
sym, object);
if (sr.sym == NULL)
goto resolve_failed;
*r_addr = sr.obj->obj_base + sr.sym->st_value +
relas->r_addend;
break;
case R_TYPE(GLOB_DAT):
if (sym == prev_sym) {
*r_addr = prev_value + relas->r_addend;
break;
}
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
sym, object);
if (sr.sym == NULL)
goto resolve_failed;
prev_sym = sym;
prev_value = sr.obj->obj_base + sr.sym->st_value;
*r_addr = prev_value + relas->r_addend;
break;
case R_TYPE(NONE):
break;
default:
_dl_die("%s: unsupported relocation '%s' %lld at %p",
object->load_name, symn,
ELF_R_TYPE(relas->r_info), (void *)r_addr);
}
continue;
resolve_failed:
if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
fails++;
}
__asm volatile("imb" : : : "memory");
return fails;
}
Elf_Addr
_dl_bind(elf_object_t *object, int reloff)
{
Elf_RelA *rela;
struct sym_res sr;
const Elf_Sym *sym;
const char *symn;
uint64_t cookie = pcookie;
struct {
struct __kbind param;
Elf_Addr newval;
} buf;
rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
sym = object->dyn.symtab;
sym += ELF_R_SYM(rela->r_info);
symn = object->dyn.strtab + sym->st_name;
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 + rela->r_addend;
if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
return buf.newval;
buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
buf.param.kb_size = sizeof(Elf_Addr);
{
register long syscall_num __asm("$0") = SYS_kbind;
register void *arg1 __asm("$16") = &buf;
register long arg2 __asm("$17") = sizeof(buf);
register long arg3 __asm("$18") = cookie;
__asm volatile( "call_pal %1" : "+r" (syscall_num)
: "i" (PAL_OSF1_callsys), "r" (arg1), "r" (arg2),
"r" (arg3) : "$19", "$20", "memory");
}
return buf.newval;
}
void _dl_bind_start(void) __dso_hidden;
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
int fails = 0;
Elf_Addr *pltgot;
if (object->Dyn.info[DT_PLTREL] != DT_RELA)
return 0;
pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
if (!lazy || pltgot == NULL) {
fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
} else {
if (object->obj_base != 0) {
int i, size;
Elf_Addr *addr;
Elf_RelA *rela;
size = object->Dyn.info[DT_PLTRELSZ] /
sizeof(Elf_RelA);
rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
for (i = 0; i < size; i++) {
addr = (Elf_Addr *)(object->obj_base +
rela[i].r_offset);
*addr += object->obj_base;
}
}
pltgot[0] = (Elf_Addr)_dl_bind_start;
pltgot[1] = (Elf_Addr)object;
}
return fails;
}