#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 <machine/trap.h>
#include "util.h"
#include "resolve.h"
int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
#define _RF_S 0x80000000
#define _RF_A 0x40000000
#define _RF_P 0x20000000
#define _RF_G 0x10000000
#define _RF_B 0x08000000
#define _RF_U 0x04000000
#define _RF_SZ(s) (((s) & 0xff) << 8)
#define _RF_RS(s) ((s) & 0xff)
static const int reloc_target_flags[] = {
0,
_RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_G| _RF_SZ(32) | _RF_RS(0),
_RF_G| _RF_SZ(32) | _RF_RS(0),
_RF_G| _RF_SZ(32) | _RF_RS(10),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10),
_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_S| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0),
_RF_S| _RF_SZ(32) | _RF_RS(0),
_RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0),
_RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0),
_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_A| _RF_SZ(32) | _RF_RS(10),
_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0),
_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10),
_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(42),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(32),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(42),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(32),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(64) | _RF_RS(0),
_RF_A| _RF_SZ(64) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(22),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(12),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0),
_RF_S|_RF_A| _RF_U| _RF_SZ(64) | _RF_RS(0),
_RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0),
};
#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
static const long reloc_target_bitmask[] = {
#define _BM(x) (~(-(1ULL << (x))))
0,
_BM(8), _BM(16), _BM(32),
_BM(8), _BM(16), _BM(32),
_BM(30), _BM(22),
_BM(22), _BM(22),
_BM(13), _BM(10),
_BM(10), _BM(13), _BM(22),
_BM(10), _BM(22),
_BM(30), 0,
-1, _BM(32), -1,
_BM(32), _BM(32),
_BM(22), _BM(10),
_BM(32), _BM(22), _BM(10),
_BM(10), _BM(11), -1,
_BM(10), _BM(22),
_BM(10), _BM(22),
_BM(22), _BM(10), _BM(22),
_BM(16), _BM(19),
-1,
_BM(7), _BM(5), _BM(6)
-1, -1,
_BM(22), _BM(13),
_BM(22), _BM(10), _BM(13),
-1, -1, _BM(16),
#undef _BM
};
#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
static int _dl_reloc_plt(Elf_Word *where1, Elf_Word *where2, Elf_Word *pltaddr,
Elf_Addr value);
static int _dl_reloc_addend(Elf_Word *where, Elf_Addr value,
Elf64_Sxword addend, Elf_Addr base);
void _dl_install_plt(Elf_Word *pltgot, Elf_Addr proc);
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);
for (i = 0; i < relrel; i++, relas++) {
Elf_Addr *where;
where = (Elf_Addr *)(relas->r_offset + loff);
*where = relas->r_addend + loff;
}
for (; i < numrela; i++, relas++) {
Elf_Addr *where, value, mask;
Elf_Word type;
const Elf_Sym *sym;
const char *symn;
type = ELF_R_TYPE(relas->r_info);
if (type == R_TYPE(NONE) || type == R_TYPE(JMP_SLOT))
continue;
where = (Elf_Addr *)(relas->r_offset + loff);
if (RELOC_USE_ADDEND(type))
value = relas->r_addend;
else
value = 0;
sym = NULL;
symn = NULL;
if (RELOC_RESOLVE_SYMBOL(type)) {
sym = object->dyn.symtab;
sym += ELF_R_SYM(relas->r_info);
symn = object->dyn.strtab + sym->st_name;
if (sym->st_shndx != SHN_UNDEF &&
ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
value += loff;
} else if (sym == prev_sym) {
value += prev_value;
} else {
struct sym_res sr;
sr = _dl_find_symbol(symn,
SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
sym, object);
if (sr.sym == NULL) {
resolve_failed:
if (ELF_ST_BIND(sym->st_info) !=
STB_WEAK)
fails++;
continue;
}
prev_sym = sym;
prev_value = (Elf_Addr)(sr.obj->obj_base +
sr.sym->st_value);
value += prev_value;
}
}
if (type == R_TYPE(COPY)) {
void *dstaddr = where;
const void *srcaddr;
const Elf_Sym *dstsym = sym;
struct sym_res sr;
sr = _dl_find_symbol(symn,
SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
dstsym, object);
if (sr.sym == NULL)
goto resolve_failed;
srcaddr = (void *)(sr.obj->obj_base + sr.sym->st_value);
_dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
continue;
}
if (RELOC_PC_RELATIVE(type))
value -= (Elf_Addr)where;
if (RELOC_BASE_RELATIVE(type))
value += loff;
mask = RELOC_VALUE_BITMASK(type);
value >>= RELOC_VALUE_RIGHTSHIFT(type);
value &= mask;
if (RELOC_UNALIGNED(type)) {
Elf_Addr tmp = 0;
char *ptr = (char *)where;
int i, size = RELOC_TARGET_SIZE(type)/8;
for (i=0; i<size; i++)
tmp = (tmp << 8) | ptr[i];
tmp &= ~mask;
tmp |= value;
for (i=0; i<size; i++)
ptr[i] = ((tmp >> (8*i)) & 0xff);
} else if (RELOC_TARGET_SIZE(type) > 32) {
*where &= ~mask;
*where |= value;
} else {
Elf32_Addr *where32 = (Elf32_Addr *)where;
*where32 &= ~mask;
*where32 |= value;
}
}
return fails;
}
#define BAA 0x30680000
#define SETHI 0x03000000
#define JMP 0x81c06000
#define NOP 0x01000000
#define OR 0x82106000
#define ORG5 0x8a116000
#define XOR 0x82186000
#define MOV71 0x8210000f
#define MOV17 0x9e100001
#define CALL 0x40000000
#define SLLX 0x83287000
#define SLLXG5 0x8b297000
#define SRAX 0x83387000
#define SETHIG5 0x0b000000
#define ORG15 0x82804005
#define HIVAL(v, s) (((v) >> (s)) & 0x003fffff)
#define LOVAL(v) ((v) & 0x000003ff)
static int
_dl_reloc_plt(Elf_Word *where1, Elf_Word *where2, Elf_Word *pltaddr,
Elf_Addr value)
{
Elf_Addr offset;
offset = value - ((Elf_Addr)pltaddr);
if ((int64_t)(offset-4) <= (1L<<20) &&
(int64_t)(offset-4) >= -(1L<<20)) {
*where1 = BAA | (((offset-4) >> 2) &0x7ffff);
return 0;
} else if (value < (1UL<<32)) {
*where1 = SETHI | HIVAL(value, 10);
where2[0] = JMP | LOVAL(value);
return 1;
} else if (value > -(1UL<<32)) {
*where1 = SETHI | HIVAL(~value, 10);
where2[0] = XOR | ((~value) & 0x00001fff);
where2[1] = JMP;
return 2;
} else if ((int64_t)(offset-8) <= (1L<<31) &&
(int64_t)(offset-8) >= -((1L<<31) - 4)) {
*where1 = MOV71;
where2[0] = CALL | (((offset-8) >> 2) & 0x3fffffff);
where2[1] = MOV17;
return 2;
} else if (value < (1L<<42)) {
*where1 = SETHI | HIVAL(value, 20);
where2[0] = OR | LOVAL(value >> 10);
where2[1] = SLLX | 10;
where2[2] = JMP | LOVAL(value);
return 3;
} else if (value > -(1UL<<41)) {
*where1 = SETHI | HIVAL(value, 20);
where2[0] = OR | LOVAL(value >> 10);
where2[1] = SLLX | 32;
where2[2] = SRAX | 22;
where2[3] = JMP | LOVAL(value);
return 4;
} else {
*where1 = SETHIG5 | HIVAL(value, 42);
where2[0] = SETHI | HIVAL(value, 10);
where2[1] = ORG5 | LOVAL(value >> 32);
where2[2] = SLLXG5 | 32;
where2[3] = ORG15;
where2[4] = JMP | LOVAL(value);
return 5;
}
}
static int
_dl_reloc_addend(Elf_Word *where, Elf_Addr value, Elf64_Sxword addend,
Elf_Addr base)
{
*((Elf_Addr *)where) = value + addend - base;
return 2;
}
Elf_Addr
_dl_bind(elf_object_t *object, int index)
{
Elf_RelA *rela;
Elf_Word *addr;
Elf_Addr newvalue;
struct sym_res sr;
const Elf_Sym *sym;
const char *symn;
int64_t cookie = pcookie;
struct {
struct __kbind param[2];
Elf_Word newval[6];
} buf;
struct __kbind *param;
size_t psize;
int i;
rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
if (ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)) {
rela += index - 4;
} else
rela += index;
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!");
newvalue = sr.obj->obj_base + sr.sym->st_value;
if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
return newvalue;
addr = (Elf_Word *)(object->obj_base + rela->r_offset);
_dl_memset(&buf, 0, sizeof(buf));
if (__predict_false(rela->r_addend)) {
i = _dl_reloc_addend(&buf.newval[0], newvalue,
rela->r_addend, object->obj_base);
buf.param[1].kb_addr = addr;
buf.param[1].kb_size = i * sizeof(buf.newval[0]);
param = &buf.param[1];
psize = sizeof(struct __kbind) + i * sizeof(buf.newval[0]);
} else {
Elf_Word first;
i = _dl_reloc_plt(&first, &buf.newval[0], addr, newvalue);
if (i == 0) {
buf.param[1].kb_addr = &addr[1];
buf.param[1].kb_size = sizeof(Elf_Word);
buf.newval[0] = first;
param = &buf.param[1];
psize = sizeof(struct __kbind) + sizeof(buf.newval[0]);
} else {
buf.param[0].kb_addr = &addr[2];
buf.param[0].kb_size = i * sizeof(Elf_Word);
buf.param[1].kb_addr = &addr[1];
buf.param[1].kb_size = sizeof(Elf_Word);
buf.newval[i] = first;
param = &buf.param[0];
psize = 2 * sizeof(struct __kbind) +
(i + 1) * sizeof(buf.newval[0]);
}
}
{
register long syscall_num __asm("g1") = SYS_kbind;
register void *arg1 __asm("o0") = param;
register long arg2 __asm("o1") = psize;
register long arg3 __asm("o2") = cookie;
__asm volatile("t %2" : "+r" (arg1), "+r" (arg2)
: "i" (ST_SYSCALL), "r" (syscall_num), "r" (arg3)
: "cc", "memory");
}
return newvalue;
}
#define SAVE 0x9de3bf50
#define SETHI_l0 0x21000000
#define SETHI_l1 0x23000000
#define OR_l0_l0 0xa0142000
#define SLLX_l0_32_l0 0xa12c3020
#define OR_l0_l1_l0 0xa0140011
#define JMPL_l0_o1 0x93c42000
#define MOV_g1_o0 0x90100001
void
_dl_install_plt(Elf_Word *pltgot, Elf_Addr proc)
{
pltgot[0] = SAVE;
pltgot[1] = SETHI_l0 | HIVAL(proc, 42);
pltgot[2] = SETHI_l1 | HIVAL(proc, 10);
pltgot[3] = OR_l0_l0 | LOVAL((proc) >> 32);
pltgot[4] = SLLX_l0_32_l0;
pltgot[5] = OR_l0_l1_l0;
pltgot[6] = JMPL_l0_o1 | LOVAL(proc);
pltgot[7] = MOV_g1_o0;
}
void _dl_bind_start_0(long, long);
void _dl_bind_start_1(long, long);
static int
_dl_md_reloc_all_plt(elf_object_t *object)
{
long i;
long numrela;
int fails = 0;
Elf_Addr loff;
Elf_RelA *relas;
loff = object->obj_base;
numrela = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
relas = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
if (relas == NULL)
return 0;
for (i = 0; i < numrela; i++, relas++) {
Elf_Addr value;
Elf_Word *where;
struct sym_res sr;
const Elf_Sym *sym;
if (ELF_R_TYPE(relas->r_info) != R_TYPE(JMP_SLOT))
continue;
sym = object->dyn.symtab + ELF_R_SYM(relas->r_info);
sr = _dl_find_symbol(object->dyn.strtab + sym->st_name,
SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
sym, object);
if (sr.sym == NULL) {
if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
fails++;
continue;
}
where = (Elf_Word *)(relas->r_offset + loff);
value = sr.obj->obj_base + sr.sym->st_value;
if (__predict_false(relas->r_addend)) {
_dl_reloc_addend(&where[0], value,
relas->r_addend, object->obj_base);
} else
_dl_reloc_plt(&where[1], &where[2], where, value);
}
return fails;
}
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
int fails = 0;
Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
Elf_Word *entry = (Elf_Word *)pltgot;
if (object->Dyn.info[DT_PLTREL] != DT_RELA)
return 0;
if (!lazy) {
fails = _dl_md_reloc_all_plt(object);
} else {
_dl_install_plt(&entry[0], (Elf_Addr)&_dl_bind_start_0);
_dl_install_plt(&entry[8], (Elf_Addr)&_dl_bind_start_1);
pltgot[8] = (Elf_Addr)object;
}
return fails;
}