summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2010-01-03 22:18:04 +0000
committerkettenis <kettenis@openbsd.org>2010-01-03 22:18:04 +0000
commit8cd9acbbc7c62d06bb2ca70c10977f9a5d8e8e1f (patch)
treebfc1d398263d7e375e341a6e312f30ea3f826f1d
parentInstall mandoc_char(7), a comprehensive overview of mandoc(1) special (diff)
downloadwireguard-openbsd-8cd9acbbc7c62d06bb2ca70c10977f9a5d8e8e1f.tar.xz
wireguard-openbsd-8cd9acbbc7c62d06bb2ca70c10977f9a5d8e8e1f.zip
Make lazy binding work on hppa.
ok miod@
-rw-r--r--libexec/ld.so/hppa/ldasm.S11
-rw-r--r--libexec/ld.so/hppa/rtld_machine.c82
2 files changed, 81 insertions, 12 deletions
diff --git a/libexec/ld.so/hppa/ldasm.S b/libexec/ld.so/hppa/ldasm.S
index 6f5f6e6446c..69827916c1e 100644
--- a/libexec/ld.so/hppa/ldasm.S
+++ b/libexec/ld.so/hppa/ldasm.S
@@ -1,4 +1,4 @@
-/* $OpenBSD: ldasm.S,v 1.4 2006/05/03 16:10:52 drahn Exp $ */
+/* $OpenBSD: ldasm.S,v 1.5 2010/01/03 22:18:04 kettenis Exp $ */
/*
* Copyright (c) 2004 Michael Shalayeff
@@ -123,6 +123,13 @@ ENTRY(hppa_call,0)
ldwm -HPPA_FRAME_SIZE(sp), r3
EXIT(hppa_call)
+/*
+ * This is a magic branch instruction that is used by GCC's
+ * __canonicalize_funcptr_for_compare() function to fixup relocations
+ * in order to do function pointer comparisons.
+ */
+ bl _dl_bind, rp
+
ENTRY(_dl_bind_start,32)
copy r3, r1
copy sp, r3
@@ -133,6 +140,7 @@ ENTRY(_dl_bind_start,32)
stw arg1, HPPA_FRAME_ARG(1)(r3)
stw arg2, HPPA_FRAME_ARG(2)(r3)
stw arg3, HPPA_FRAME_ARG(3)(r3)
+ stw t1, 4(r3)
stw ret0, 8(r3)
stw ret1, 12(r3)
@@ -150,6 +158,7 @@ ENTRY(_dl_bind_start,32)
ldw HPPA_FRAME_ARG(1)(r3), arg1
ldw HPPA_FRAME_ARG(2)(r3), arg2
ldw HPPA_FRAME_ARG(3)(r3), arg3
+ ldw 4(r3), t1
ldw 8(r3), ret0
ldw 12(r3), ret0
diff --git a/libexec/ld.so/hppa/rtld_machine.c b/libexec/ld.so/hppa/rtld_machine.c
index 89bc37c4e4f..ab0714b8c21 100644
--- a/libexec/ld.so/hppa/rtld_machine.c
+++ b/libexec/ld.so/hppa/rtld_machine.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtld_machine.c,v 1.16 2008/11/09 12:34:47 tobias Exp $ */
+/* $OpenBSD: rtld_machine.c,v 1.17 2010/01/03 22:18:04 kettenis Exp $ */
/*
* Copyright (c) 2004 Michael Shalayeff
@@ -285,6 +285,18 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz)
return (fails);
}
+extern void _dl_bind_start(void);
+
+#define PLT_STUB_SIZE (7 * 4)
+#define PLT_ENTRY_SIZE (2 * 4)
+#define PLT_STUB_GOTOFF (4 * 4)
+
+#define PLT_STUB_MAGIC1 0x00c0ffee
+#define PLT_STUB_MAGIC2 0xdeadbeef
+
+#define PLT_STUB_INSN1 0x0e801081 /* ldw 0(%r20), %r1 */
+#define PLT_STUB_INSN2 0xe820c000 /* bv %r0(%r1) */
+
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
@@ -296,8 +308,6 @@ _dl_md_reloc_got(elf_object_t *object, int lazy)
if (object->dyn.pltrel != DT_RELA)
return (0);
- lazy = 0; /* force busy binding */
-
object->got_addr = NULL;
object->got_size = 0;
this = NULL;
@@ -323,17 +333,66 @@ _dl_md_reloc_got(elf_object_t *object, int lazy)
if (!lazy) {
fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
} else {
+ register Elf_Addr ltp __asm ("%r19");
+ Elf_Addr *got = NULL;
+
rela = (Elf_RelA *)(object->dyn.jmprel);
numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
ooff = object->obj_base;
+ /*
+ * Find the PLT stub by looking at all the
+ * relocations. The PLT stub should be at the end of
+ * the .plt section so we start with the last
+ * relocation, since the linker should have emitted
+ * them in order.
+ */
+ for (i = numrela - 1; i >= 0; i--) {
+ got = (Elf_Addr *)(ooff + rela[i].r_offset +
+ PLT_ENTRY_SIZE + PLT_STUB_SIZE);
+ if (got[-2] == PLT_STUB_MAGIC1 ||
+ got[-1] == PLT_STUB_MAGIC2)
+ break;
+ got = NULL;
+ }
+ if (got == NULL)
+ return (1);
+
+ /*
+ * Patch up the PLT stub such that it doesn't clobber
+ * %r22, which is used to pass on the errno values
+ * from failed system calls to __cerrno() in libc.
+ */
+ got[-7] = PLT_STUB_INSN1;
+ got[-6] = PLT_STUB_INSN2;
+ __asm __volatile("fdc 0(%0)" :: "r" (&got[-7]));
+ __asm __volatile("fdc 0(%0)" :: "r" (&got[-6]));
+ __asm __volatile("sync");
+ __asm __volatile("fic 0(%0)" :: "r" (&got[-7]));
+ __asm __volatile("fic 0(%0)" :: "r" (&got[-6]));
+ __asm __volatile("sync");
+
+ /*
+ * Fill in the PLT stub such that it invokes the
+ * _dl_bind_start() trampoline to fix up the
+ * relocation.
+ */
+ got[1] = (Elf_Addr)object;
+ got[-2] = (Elf_Addr)&_dl_bind_start;
+ got[-1] = ltp;
for (i = 0; i < numrela; i++, rela++) {
Elf_Addr *r_addr = (Elf_Addr *)(ooff + rela->r_offset);
+ if (ELF_R_TYPE(rela->r_info) != RELOC_IPLT) {
+ _dl_printf("unexpected reloc 0x%x\n",
+ ELF_R_TYPE(rela->r_info));
+ return (1);
+ }
+
if (ELF_R_SYM(rela->r_info)) {
- r_addr[0] = 0; /* TODO */
- r_addr[1] = (Elf_Addr) ((void *)rela -
- (void *)object->dyn.jmprel);
+ r_addr[0] = (Elf_Addr)got - PLT_STUB_GOTOFF;
+ r_addr[1] = (Elf_Addr) (rela -
+ (Elf_RelA *)object->dyn.jmprel);
} else {
r_addr[0] = ooff + rela->r_addend;
r_addr[1] = (Elf_Addr)object->dyn.pltgot;
@@ -342,7 +401,7 @@ _dl_md_reloc_got(elf_object_t *object, int lazy)
}
if (object->got_size != 0)
_dl_mprotect((void *)object->got_start, object->got_size,
- GOT_PERMS);
+ GOT_PERMS|PROT_EXEC);
return (fails);
}
@@ -361,7 +420,7 @@ _dl_bind(elf_object_t *object, int reloff)
Elf_RelA *rela;
sigset_t omask, nmask;
- rela = (Elf_RelA *)(object->dyn.jmprel + reloff);
+ rela = (Elf_RelA *)object->dyn.jmprel + reloff;
sym = object->dyn.symtab;
sym += ELF_R_SYM(rela->r_info);
@@ -375,8 +434,9 @@ _dl_bind(elf_object_t *object, int reloff)
_dl_printf("lazy binding failed!\n");
*((int *)0) = 0; /* XXX */
}
+ DL_DEB(("%s: %s\n", symn, sobj->load_name));
- value = ooff + this->st_value;
+ value = ooff + this->st_value + rela->r_addend;
/* if PLT+GOT is protected, allow the write */
if (object->got_size != 0) {
@@ -395,10 +455,10 @@ _dl_bind(elf_object_t *object, int reloff)
if (object->got_size != 0) {
/* mprotect the actual modified region, not the whole plt */
_dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3,
- PROT_READ);
+ PROT_READ|PROT_EXEC);
_dl_thread_bind_lock(1);
_dl_sigprocmask(SIG_SETMASK, &omask, NULL);
}
- return (value);
+ return ((Elf_Addr)addr);
}