diff options
author | 2015-09-01 05:10:43 +0000 | |
---|---|---|
committer | 2015-09-01 05:10:43 +0000 | |
commit | 7ad3219a3e432939f4caa545e2577b67b31bb929 (patch) | |
tree | 34992bbb3633a79cfa30d43f0777ed3120ba6644 | |
parent | dont need the kernel lock for mpsafe bpfs (again) (diff) | |
download | wireguard-openbsd-7ad3219a3e432939f4caa545e2577b67b31bb929.tar.xz wireguard-openbsd-7ad3219a3e432939f4caa545e2577b67b31bb929.zip |
Use kbind for lazy binding GOT/PLT updates on m88k and sparc.
Much discussion with and assistance from miod and deraadt
ok miod@
-rw-r--r-- | libexec/ld.so/m88k/rtld_machine.c | 48 | ||||
-rw-r--r-- | libexec/ld.so/sparc/rtld_machine.c | 100 |
2 files changed, 88 insertions, 60 deletions
diff --git a/libexec/ld.so/m88k/rtld_machine.c b/libexec/ld.so/m88k/rtld_machine.c index 49ab412474e..3d87aab0421 100644 --- a/libexec/ld.so/m88k/rtld_machine.c +++ b/libexec/ld.so/m88k/rtld_machine.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtld_machine.c,v 1.11 2014/12/14 19:55:12 miod Exp $ */ +/* $OpenBSD: rtld_machine.c,v 1.12 2015/09/01 05:10:43 guenther Exp $ */ /* * Copyright (c) 2013 Miodrag Vallat. @@ -45,10 +45,11 @@ #include <sys/types.h> #include <sys/mman.h> +#include <sys/syscall.h> +#include <sys/unistd.h> #include <nlist.h> #include <link.h> -#include <signal.h> #include "syscall.h" #include "archdep.h" @@ -57,6 +58,8 @@ 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) { @@ -398,11 +401,15 @@ Elf_Addr _dl_bind(elf_object_t *object, int reloff) { Elf_RelA *rel; - Elf_Addr *r_addr, ooff, value; + Elf_Addr ooff; const Elf_Sym *sym, *this; const char *symn; const elf_object_t *sobj; - sigset_t savedmask; + uint64_t cookie = pcookie; + struct { + struct __kbind param; + Elf_Addr newval; + } buf; rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); @@ -410,7 +417,6 @@ _dl_bind(elf_object_t *object, int reloff) sym += ELF_R_SYM(rel->r_info); symn = object->dyn.strtab + sym->st_name; - r_addr = (Elf_Addr *)(object->obj_base + rel->r_offset); this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, &sobj); @@ -419,26 +425,26 @@ _dl_bind(elf_object_t *object, int reloff) *(volatile int *)0 = 0; /* XXX */ } - value = ooff + this->st_value; + buf.newval = ooff + this->st_value; - if (sobj->traced && _dl_trace_plt(sobj, symn)) - return value; + if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn)) + return (buf.newval); - /* if GOT is protected, allow the write */ - if (object->got_size != 0) { - _dl_thread_bind_lock(0, &savedmask); - _dl_mprotect((void*)object->got_start, object->got_size, - PROT_READ | PROT_WRITE); - } + buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset); + buf.param.kb_size = sizeof(Elf_Addr); - *r_addr = value; + /* directly code the syscall, so that it's actually inline here */ + { + 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; - /* put the GOT back to RO */ - if (object->got_size != 0) { - _dl_mprotect((void*)object->got_start, object->got_size, - PROT_READ); - _dl_thread_bind_lock(1, &savedmask); + __asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0" + : "+r" (arg1), "+r" (arg2) : "r" (syscall_num), + "r" (arg3), "r" (arg4) : "cc", "memory"); } - return (value); + return (buf.newval); } diff --git a/libexec/ld.so/sparc/rtld_machine.c b/libexec/ld.so/sparc/rtld_machine.c index d3806a9dad6..9f210cd3d85 100644 --- a/libexec/ld.so/sparc/rtld_machine.c +++ b/libexec/ld.so/sparc/rtld_machine.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtld_machine.c,v 1.39 2015/07/26 03:08:16 guenther Exp $ */ +/* $OpenBSD: rtld_machine.c,v 1.40 2015/09/01 05:10:43 guenther Exp $ */ /* * Copyright (c) 1999 Dale Rahn @@ -29,20 +29,23 @@ #define _DYN_LOADER -#include <sys/types.h> -#include <sys/mman.h> #include <sys/param.h> +#include <sys/mman.h> #include <sys/sysctl.h> +#include <sys/syscall.h> +#include <sys/unistd.h> #include <machine/cpu.h> +#include <machine/trap.h> #include <nlist.h> #include <link.h> -#include <signal.h> #include "syscall.h" #include "archdep.h" #include "resolve.h" +int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; + /* * The following table holds for each relocation type: * - the width in bits of the memory location the relocation @@ -143,7 +146,7 @@ static int reloc_target_bitmask[] = { #define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t]) static inline void -_dl_reloc_plt(Elf_Addr *where, Elf_Addr value) +_dl_reloc_plt(Elf_Addr *where1, Elf_Addr *where2, Elf_Addr value) { /* * At the PLT entry pointed at by `where', we now construct @@ -154,23 +157,24 @@ _dl_reloc_plt(Elf_Addr *where, Elf_Addr value) * sethi %hi(addr), %g1 * jmp %g1+%lo(addr) * - * We write the third instruction first, since that leaves the - * previous `b,a' at the second word in place. Hence the whole + * If this was being directly applied to the PLT during resolution + * of a lazy binding, then to make it thread safe we would need to + * update the third instruction first, since that leaves the + * previous `b,a' at the second word in place, so that the whole * PLT slot can be atomically change to the new sequence by - * writing the `sethi' instruction at word 2. + * writing the `sethi' instruction at word 2. We would also need + * iflush instructions to guarantee that the third instruction + * made it to the I-cache before the second instruction. + * + * HOWEVER, we do lazy binding via the kbind syscall, so we can + * write them in order here and reorder by the kbind blocking. + * Non-lazy binding does lots of work before returning, so no + * reordering or iflushing is needed. */ #define SETHI 0x03000000 #define JMP 0x81c06000 -#define NOP 0x01000000 - where[2] = JMP | (value & 0x000003ff); - where[1] = SETHI | ((value >> 10) & 0x003fffff); - __asm volatile("iflush %0+8" : : "r" (where)); - __asm volatile("iflush %0+4" : : "r" (where)); - /* - * iflush requires 5 subsequent cycles to be sure all copies - * are flushed from the CPU and the icache. - */ - __asm volatile("nop;nop;nop;nop;nop"); + *where1 = SETHI | ((value >> 10) & 0x003fffff); + *where2 = JMP | (value & 0x000003ff); } int @@ -301,7 +305,7 @@ resolve_failed: } if (type == R_TYPE(JMP_SLOT)) { - _dl_reloc_plt(where, value); + _dl_reloc_plt(&where[1], &where[2], value); continue; } @@ -341,7 +345,11 @@ _dl_bind(elf_object_t *object, int reloff) const elf_object_t *sobj; Elf_Addr value; Elf_RelA *rela; - sigset_t savedmask; + int64_t cookie = pcookie; + struct { + struct __kbind param[2]; + Elf_Word buf[2]; + } buf; rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); @@ -349,7 +357,6 @@ _dl_bind(elf_object_t *object, int reloff) sym += ELF_R_SYM(rela->r_info); symn = object->dyn.strtab + sym->st_name; - addr = (Elf_Addr *)(object->obj_base + rela->r_offset); this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj); @@ -360,25 +367,40 @@ _dl_bind(elf_object_t *object, int reloff) value = ooff + this->st_value; - if (sobj->traced && _dl_trace_plt(sobj, symn)) - return value; + if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn)) + return (value); - /* if PLT is protected, allow the write */ - if (object->plt_size != 0) { - _dl_thread_bind_lock(0, &savedmask); - /* mprotect the actual modified region, not the whole plt */ - _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3, - PROT_READ|PROT_WRITE|PROT_EXEC); - } - - _dl_reloc_plt(addr, value); - - /* if PLT is (to be protected, change back to RO/X */ - if (object->plt_size != 0) { - /* mprotect the actual modified region, not the whole plt */ - _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3, - PROT_READ|PROT_EXEC); - _dl_thread_bind_lock(1, &savedmask); + /* + * Relocations require two blocks to be written: the second word + * then the first word. So the layout of the buffer we pass to + * kbind() needs to be this: + * +------------+ + * | kbind.addr | + * | kbind.size | + * | kbind.addr | + * | kbind.size | + * | word 2 | + * | word 1 | + * +------------+ + */ + addr = (Elf_Addr *)(object->obj_base + rela->r_offset); + _dl_reloc_plt(&buf.buf[1], &buf.buf[0], value); + buf.param[0].kb_addr = &addr[2]; + buf.param[0].kb_size = sizeof(Elf_Word); + buf.param[1].kb_addr = &addr[1]; + buf.param[1].kb_size = sizeof(Elf_Word); + + /* directly code the syscall, so that it's actually inline here */ + { + register long syscall_num __asm("g1") = SYS_kbind; + register void *arg1 __asm("o0") = &buf; + register long arg2 __asm("o1") = sizeof(buf); + register long arg3 __asm("o2") = 0xffffffff & (cookie >> 32); + register long arg4 __asm("o3") = 0xffffffff & cookie; + + __asm volatile("t %2" : "+r" (arg1), "+r" (arg2) + : "i" (ST_SYSCALL), "r" (syscall_num), "r" (arg3), + "r" (arg4) : "cc", "memory" ); } return (value); |