#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"
int _dl_cacheflush(unsigned long, size_t);
Elf_Addr _dl_bind(elf_object_t *object, int reloff);
void _dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr);
int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
int
_dl_md_reloc(elf_object_t *object, int rel, int relasz)
{
int i;
int numrela;
int relrela;
int fails = 0;
Elf_Addr loff;
Elf_RelA *relas;
Elf_Addr prev_value = 0, prev_ooff = 0;
const Elf_Sym *prev_sym = NULL;
loff = object->obj_base;
numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
relrela = rel == DT_RELA ? object->relacount : 0;
relas = (Elf_RelA *)(object->Dyn.info[rel]);
if (relas == NULL)
return 0;
if (relrela > numrela)
_dl_die("relacount > numrel: %d > %d", relrela, numrela);
for (i = 0; i < relrela; i++, relas++) {
Elf_Addr *r_addr;
r_addr = (Elf_Addr *)(relas->r_offset + loff);
*r_addr = relas->r_addend + loff;
}
for (; i < numrela; i++, relas++) {
Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
Elf_Addr addend, newval;
const Elf_Sym *sym;
const char *symn;
int type;
type = ELF_R_TYPE(relas->r_info);
if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
continue;
if (type == RELOC_NONE)
continue;
sym = object->dyn.symtab;
sym += ELF_R_SYM(relas->r_info);
symn = object->dyn.strtab + sym->st_name;
if (type == RELOC_COPY) {
struct sym_res sr;
sr = _dl_find_symbol(symn,
SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
sym, object);
if (sr.sym != NULL) {
_dl_bcopy((void *)(sr.obj->obj_base +
sr.sym->st_value), r_addr, sym->st_size);
} else
fails++;
continue;
}
if (ELF_R_SYM(relas->r_info) &&
!(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
sym != prev_sym) {
if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
prev_sym = sym;
prev_value = 0;
prev_ooff = object->obj_base;
} else {
struct sym_res sr;
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
((type == RELOC_GOTP_ENT) ?
SYM_PLT : SYM_NOTPLT), sym, object);
if (sr.sym == NULL) {
if (ELF_ST_BIND(sym->st_info) !=
STB_WEAK)
fails++;
continue;
}
prev_sym = sym;
prev_value = sr.sym->st_value;
prev_ooff = sr.obj->obj_base;
}
}
if (type == RELOC_GOTP_ENT) {
_dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
relas->r_addend + loff,
prev_ooff + prev_value);
continue;
}
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))
addend = relas->r_addend;
else
addend = prev_value + relas->r_addend;
switch (type) {
case RELOC_16L:
newval = prev_ooff + addend;
*(unsigned short *)r_addr = newval & 0xffff;
_dl_cacheflush((unsigned long)r_addr, 2);
break;
case RELOC_16H:
newval = prev_ooff + addend;
*(unsigned short *)r_addr = newval >> 16;
_dl_cacheflush((unsigned long)r_addr, 2);
break;
case RELOC_DISP26:
newval = prev_ooff + addend;
newval -= (Elf_Addr)r_addr;
if ((newval >> 28) != 0 && (newval >> 28) != 0x0f)
_dl_die("%s: out of range DISP26"
" relocation to '%s' at %p\n",
object->load_name, symn, (void *)r_addr);
*r_addr = (*r_addr & 0xfc000000) |
(((int32_t)newval >> 2) & 0x03ffffff);
_dl_cacheflush((unsigned long)r_addr, 4);
break;
case RELOC_32:
newval = prev_ooff + addend;
*r_addr = newval;
break;
case RELOC_BBASED_32:
newval = loff + addend;
*r_addr = newval;
break;
default:
_dl_die("%s: unsupported relocation '%s' %d at %p\n",
object->load_name, symn, type, (void *)r_addr);
}
}
return fails;
}
void
_dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
{
uint16_t *plt_entry = (uint16_t *)plt_addr;
*(Elf_Addr *)got_addr = val;
plt_entry[1] = got_addr >> 16;
plt_entry[3] = got_addr & 0xffff;
}
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
extern void _dl_bind_start(void);
int fails = 0;
Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
Elf_Addr plt_start, plt_end;
if (pltgot == NULL)
return 0;
pltgot[1] = (Elf_Addr)object;
pltgot[2] = (Elf_Addr)_dl_bind_start;
if (object->Dyn.info[DT_PLTREL] != DT_RELA)
return 0;
if (!lazy) {
fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
} else {
if (object->obj_base != 0) {
int cnt;
Elf_Addr *addr;
Elf_RelA *rela;
cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
for (; cnt != 0; cnt--, rela++) {
addr = (Elf_Addr *)(object->obj_base +
rela->r_offset);
_dl_md_reloc_gotp_ent((Elf_Addr)addr,
object->obj_base + rela->r_addend,
*addr + object->obj_base);
}
}
}
plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM];
plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM];
if ((!lazy || object->obj_base != 0) && plt_start != 0 &&
plt_end != 0) {
size_t plt_size = plt_end - plt_start;
if (plt_size != 0)
_dl_cacheflush(plt_start + object->obj_base, plt_size);
}
return fails;
}
Elf_Addr
_dl_bind(elf_object_t *object, int reloff)
{
Elf_RelA *rel;
struct sym_res sr;
const Elf_Sym *sym;
const char *symn;
uint64_t cookie = pcookie;
struct {
struct __kbind param;
Elf_Addr newval;
} buf;
rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
sym = object->dyn.symtab;
sym += ELF_R_SYM(rel->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;
if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
return buf.newval;
buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
buf.param.kb_size = sizeof(Elf_Addr);
{
register long syscall_num __asm("r13") = SYS_kbind;
register void *arg1 __asm("r2") = &buf;
register long arg2 __asm("r3") = sizeof(buf);
register long arg3 __asm("r4") = 0xffffffff & (cookie >> 32);
register long arg4 __asm("r5") = 0xffffffff & cookie;
__asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0"
: "+r" (arg1), "+r" (arg2) : "r" (syscall_num),
"r" (arg3), "r" (arg4) : "memory");
}
return buf.newval;
}