summaryrefslogtreecommitdiff
path: root/libexec/ld.so/tib.c
diff options
context:
space:
mode:
authorPhilip Guenther <guenther@cvs.openbsd.org>2016-05-07 19:05:25 +0000
committerPhilip Guenther <guenther@cvs.openbsd.org>2016-05-07 19:05:25 +0000
commitbe4e94637d7117375492627b79c41629c7da9bc3 (patch)
tree61a9fabcc07d38d2720bbb937e83103928286550 /libexec/ld.so/tib.c
parent33d8693716b4a6d503e0ba97f94a783905b87f28 (diff)
Use a Thread Information Block in both single and multi-threaded programs.
This stores errno, the cancelation flags, and related bits for each thread and is allocated by ld.so or libc.a. This is an ABI break from 5.9-stable! Make libpthread dlopen'able by moving the cancelation wrappers into libc and doing locking and fork/errno handling via callbacks that libpthread registers when it first initializes. 'errno' *must* be declared via <errno.h> now! Clean up libpthread's symbol exports like libc. On powerpc, offset the TIB/TCB/TLS data from the register per the ELF spec. Testing by various, particularly sthen@ and patrick@ ok kettenis@
Diffstat (limited to 'libexec/ld.so/tib.c')
-rw-r--r--libexec/ld.so/tib.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/libexec/ld.so/tib.c b/libexec/ld.so/tib.c
new file mode 100644
index 00000000000..ed190ea90ad
--- /dev/null
+++ b/libexec/ld.so/tib.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2016 Philip Guenther <guenther@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Thread Information Block (TIB) and Thread Local Storage (TLS) handling
+ * (the TCB, Thread Control Block, is part of the TIB)
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/exec_elf.h>
+
+#include <tib.h>
+
+#include "archdep.h"
+#include "resolve.h"
+#include "util.h"
+#include "syscall.h"
+
+/* If we need the syscall, use our local syscall definition */
+#define __set_tcb(tcb) _dl_set_tcb(tcb)
+
+
+static int static_tls_size;
+
+int _dl_tib_static_done;
+
+/*
+ * Allocate a TIB for passing to __tfork for a new thread. 'extra'
+ * is the amount of space to allocate on the side of the TIB opposite
+ * of the TLS data: before the TIB for variant 1 and after the TIB
+ * for variant 2. If non-zero, tib_thread is set to point to that area.
+ */
+void *
+_dl_allocate_tib(size_t extra)
+{
+ char *base;
+ struct tib *tib;
+ char *thread = NULL;
+ struct elf_object *obj;
+
+#if TLS_VARIANT == 1
+ /* round up the extra size to align the tib after it */
+ extra = ELF_ROUND(extra, sizeof(void *));
+ base = _dl_malloc(extra + sizeof *tib + static_tls_size);
+ tib = (struct tib *)(base + extra);
+ if (extra)
+ thread = base;
+#define TLS_ADDR(tibp, offset) ((char *)(tibp) + sizeof(struct tib) + (offset))
+
+#elif TLS_VARIANT == 2
+ /* round up the tib size to align the extra area after it */
+ base = _dl_malloc(ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN) +
+ extra + static_tls_size);
+ tib = (struct tib *)(base + static_tls_size);
+ if (extra)
+ thread = (char *)tib + ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN);
+#define TLS_ADDR(tibp, offset) ((char *)(tibp) - (offset))
+
+#endif
+
+ for (obj = _dl_objects; obj != NULL; obj = obj->next) {
+ if (obj->tls_msize != 0) {
+ char *addr = TLS_ADDR(tib, obj->tls_offset);
+
+ _dl_memset(addr + obj->tls_fsize, 0,
+ obj->tls_msize - obj->tls_fsize);
+ if (obj->tls_static_data != NULL)
+ _dl_bcopy(obj->tls_static_data, addr,
+ obj->tls_fsize);
+ DL_DEB(("\t%s has index %u addr %p msize %u fsize %u\n",
+ obj->load_name, obj->tls_offset,
+ (void *)addr, obj->tls_msize, obj->tls_fsize));
+ }
+ }
+
+ TIB_INIT(tib, NULL, thread);
+
+ DL_DEB(("tib new=%p\n", (void *)tib));
+
+ return (tib);
+}
+
+void
+_dl_free_tib(void *tib, size_t extra)
+{
+ size_t tib_offset;
+
+#if TLS_VARIANT == 1
+ tib_offset = ELF_ROUND(extra, sizeof(void *));
+#elif TLS_VARIANT == 2
+ tib_offset = static_tls_size;
+#endif
+
+ DL_DEB(("free tib=%p\n", (void *)tib));
+ _dl_free((char *)tib - tib_offset);
+}
+
+
+/*
+ * Record what's necessary for handling TLS for an object.
+ */
+void
+_dl_set_tls(elf_object_t *object, Elf_Phdr *ptls, Elf_Addr libaddr,
+ const char *libname)
+{
+ if (ptls->p_vaddr != 0 && ptls->p_filesz != 0)
+ object->tls_static_data = (void *)(ptls->p_vaddr + libaddr);
+ object->tls_fsize = ptls->p_filesz;
+ object->tls_msize = ptls->p_memsz;
+ object->tls_align = ptls->p_align;
+
+ DL_DEB(("tls %x %x %x %x\n",
+ object->tls_static_data, object->tls_fsize, object->tls_msize,
+ object->tls_align));
+}
+
+static inline Elf_Addr
+allocate_tls_offset(Elf_Addr msize, Elf_Addr align)
+{
+ Elf_Addr offset;
+
+#if TLS_VARIANT == 1
+ /* round up to the required alignment, then allocate the space */
+ offset = ELF_ROUND(static_tls_size, align);
+ static_tls_size += msize;
+#elif TLS_VARIANT == 2
+ /*
+ * allocate the space, then round up to the alignment
+ * (these are negative offsets, so rounding up really rounds the
+ * address down)
+ */
+ static_tls_size = ELF_ROUND(static_tls_size + msize, align);
+ offset = static_tls_size;
+#else
+# error "unknown TLS_VARIANT"
+#endif
+ return offset;
+}
+
+/*
+ * Calculate the TLS offset for each object with static TLS.
+ */
+void
+_dl_allocate_tls_offsets(void)
+{
+ struct elf_object *obj;
+
+ for (obj = _dl_objects; obj != NULL; obj = obj->next) {
+ if (obj->tls_msize != 0) {
+ obj->tls_offset = allocate_tls_offset(obj->tls_msize,
+ obj->tls_align);
+ }
+ }
+
+ /* no more static TLS allocations after this */
+ _dl_tib_static_done = 1;
+}
+
+/*
+ * Allocate the TIB + TLS for the initial thread.
+ */
+void
+_dl_allocate_first_tib(void)
+{
+ struct tib *tib;
+
+ tib = _dl_allocate_tib(0);
+ tib->tib_tid = _dl_getthrid();
+
+ TCB_SET(TIB_TO_TCB(tib));
+}