summaryrefslogtreecommitdiffstats
path: root/lib/libc/include
diff options
context:
space:
mode:
authorguenther <guenther@openbsd.org>2016-05-07 19:05:21 +0000
committerguenther <guenther@openbsd.org>2016-05-07 19:05:21 +0000
commitfe38b55cb0aae270de3f844146814682e8cd345c (patch)
tree9825cc8aa96314e8e79ea1802ccbc9349772680b /lib/libc/include
parentImplement ACPI 5.0 GeneralPurposeIo OpRegion support. This basically allows (diff)
downloadwireguard-openbsd-fe38b55cb0aae270de3f844146814682e8cd345c.tar.xz
wireguard-openbsd-fe38b55cb0aae270de3f844146814682e8cd345c.zip
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 'lib/libc/include')
-rw-r--r--lib/libc/include/cancel.h75
-rw-r--r--lib/libc/include/namespace.h6
-rw-r--r--lib/libc/include/thread_private.h306
3 files changed, 252 insertions, 135 deletions
diff --git a/lib/libc/include/cancel.h b/lib/libc/include/cancel.h
new file mode 100644
index 00000000000..4f4add471b7
--- /dev/null
+++ b/lib/libc/include/cancel.h
@@ -0,0 +1,75 @@
+/* $OpenBSD: cancel.h,v 1.1 2016/05/07 19:05:22 guenther Exp $ */
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef _CANCEL_H_
+#define _CANCEL_H_
+
+#include <tib.h>
+#include "thread_private.h"
+
+__BEGIN_HIDDEN_DECLS
+/* process a cancel request at a cancel point */
+__dead void _thread_canceled(void);
+__END_HIDDEN_DECLS
+
+#ifdef LIBC
+/*
+ * Redirect macros that would use the syscall to instead use our callback
+ */
+#define __get_tcb() _thread_cb.tc_tcb()
+#endif
+
+#define PREP_CANCEL_POINT(tib) \
+ int _cantcancel = (tib)->tib_cantcancel
+
+#define ENTER_CANCEL_POINT_INNER(tib, can_cancel, delay) \
+ if (_cantcancel == 0) { \
+ (tib)->tib_cancel_point = (delay) ? \
+ CANCEL_POINT_DELAYED : CANCEL_POINT; \
+ if (can_cancel) { \
+ __asm volatile("":::"memory"); \
+ if (__predict_false((tib)->tib_canceled)) \
+ _thread_canceled(); \
+ } \
+ }
+
+#define LEAVE_CANCEL_POINT_INNER(tib, can_cancel) \
+ if (_cantcancel == 0) { \
+ (tib)->tib_cancel_point = 0; \
+ if (can_cancel) { \
+ __asm volatile("":::"memory"); \
+ if (__predict_false((tib)->tib_canceled)) \
+ _thread_canceled(); \
+ } \
+ }
+
+/*
+ * Enter or leave a cancelation point, optionally processing pending
+ * cancelation requests. Note that ENTER_CANCEL_POINT opens a block
+ * and LEAVE_CANCEL_POINT must close that same block.
+ */
+#define ENTER_CANCEL_POINT(can_cancel) \
+ { \
+ struct tib *_tib = TIB_GET(); \
+ PREP_CANCEL_POINT(_tib); \
+ ENTER_CANCEL_POINT_INNER(_tib, can_cancel, 0)
+
+#define LEAVE_CANCEL_POINT(can_cancel) \
+ LEAVE_CANCEL_POINT_INNER(_tib, can_cancel); \
+ }
+
+#endif /* _CANCEL_H_ */
diff --git a/lib/libc/include/namespace.h b/lib/libc/include/namespace.h
index 16d524f666f..980c1a8dd61 100644
--- a/lib/libc/include/namespace.h
+++ b/lib/libc/include/namespace.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: namespace.h,v 1.9 2016/04/05 04:28:32 guenther Exp $ */
+/* $OpenBSD: namespace.h,v 1.10 2016/05/07 19:05:22 guenther Exp $ */
#ifndef _LIBC_NAMESPACE_H_
#define _LIBC_NAMESPACE_H_
@@ -139,12 +139,14 @@
#define CANCEL(x) _libc_##x##_cancel
#define WRAP(x) _libc_##x##_wrap
#define HIDDEN_STRING(x) "_libc_" __STRING(x)
+#define CANCEL_STRING(x) "_libc_" __STRING(x) "_cancel"
#define WRAP_STRING(x) "_libc_" __STRING(x) "_wrap"
#define PROTO_NORMAL(x) __dso_hidden typeof(x) x asm(HIDDEN_STRING(x))
#define PROTO_STD_DEPRECATED(x) typeof(x) x __attribute__((deprecated))
#define PROTO_DEPRECATED(x) typeof(x) x __attribute__((deprecated, weak))
-#define PROTO_CANCEL(x) PROTO_NORMAL(x), CANCEL(x)
+#define PROTO_CANCEL(x) __dso_hidden typeof(x) HIDDEN(x), \
+ x asm(CANCEL_STRING(x))
#define PROTO_WRAP(x) PROTO_NORMAL(x), WRAP(x)
#define DEF_STRONG(x) __strong_alias(x, HIDDEN(x))
diff --git a/lib/libc/include/thread_private.h b/lib/libc/include/thread_private.h
index 43ebf7d96e8..c2e0cf0b3fd 100644
--- a/lib/libc/include/thread_private.h
+++ b/lib/libc/include/thread_private.h
@@ -1,52 +1,108 @@
-/* $OpenBSD: thread_private.h,v 1.26 2015/04/07 01:27:07 guenther Exp $ */
+/* $OpenBSD: thread_private.h,v 1.27 2016/05/07 19:05:22 guenther Exp $ */
/* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */
#ifndef _THREAD_PRIVATE_H_
#define _THREAD_PRIVATE_H_
-/*
- * This file defines the thread library interface to libc. Thread
- * libraries must implement the functions described here for proper
- * inter-operation with libc. libc contains weak versions of the
- * described functions for operation in a non-threaded environment.
- */
+#include <stdio.h> /* for FILE and __isthreaded */
/*
- * This variable is 0 until a second thread is created.
+ * The callbacks needed by libc to handle the threaded case.
+ * NOTE: Bump the version when you change the struct contents!
+ *
+ * tc_canceled:
+ * If not NULL, what to do when canceled (otherwise _exit(0))
+ *
+ * tc_flockfile, tc_ftrylockfile, and tc_funlockfile:
+ * If not NULL, these implement the flockfile() family.
+ * XXX In theory, you should be able to lock a FILE before
+ * XXX loading libpthread and have that be a real lock on it,
+ * XXX but that doesn't work without the libc base version
+ * XXX tracking the recursion count.
+ *
+ * tc_malloc_lock and tc_malloc_unlock:
+ * tc_atexit_lock and tc_atexit_unlock:
+ * tc_atfork_lock and tc_atfork_unlock:
+ * tc_arc4_lock and tc_arc4_unlock:
+ * The locks used by the malloc, atexit, atfork, and arc4 subsystems.
+ * These have to be ordered specially in the fork/vfork wrappers
+ * and may be implemented differently than the general mutexes
+ * in the callbacks below.
+ *
+ * tc_mutex_lock and tc_mutex_unlock:
+ * Lock and unlock the given mutex. If the given mutex is NULL
+ * a mutex is allocated and initialized automatically.
+ *
+ * tc_mutex_destroy:
+ * Destroy/deallocate the given mutex.
+ *
+ * tc_tag_lock and tc_tag_unlock:
+ * Lock and unlock the mutex associated with the given tag.
+ * If the given tag is NULL a tag is allocated and initialized
+ * automatically.
+ *
+ * tc_tag_storage:
+ * Returns a pointer to per-thread instance of data associated
+ * with the given tag. If the given tag is NULL a tag is
+ * allocated and initialized automatically.
+ *
+ * tc_fork, tc_vfork:
+ * If not NULL, they are called instead of the syscall stub, so that
+ * the thread library can do necessary locking and reinitialization.
+ *
+ *
+ * If <machine/tcb.h> doesn't define TCB_GET(), then locating the TCB in a
+ * threaded process requires a syscall (__get_tcb(2)) which is too much
+ * overhead for single-threaded processes. For those archs, there are two
+ * additional callbacks, though they are placed first in the struct for
+ * convenience in ASM:
+ *
+ * tc_errnoptr:
+ * Returns the address of the thread's errno.
+ *
+ * tc_tcb:
+ * Returns the address of the thread's TCB.
*/
-extern int __isthreaded;
+struct thread_callbacks {
+ int *(*tc_errnoptr)(void); /* MUST BE FIRST */
+ void *(*tc_tcb)(void);
+ __dead void (*tc_canceled)(void);
+ void (*tc_flockfile)(FILE *);
+ int (*tc_ftrylockfile)(FILE *);
+ void (*tc_funlockfile)(FILE *);
+ void (*tc_malloc_lock)(void);
+ void (*tc_malloc_unlock)(void);
+ void (*tc_atexit_lock)(void);
+ void (*tc_atexit_unlock)(void);
+ void (*tc_atfork_lock)(void);
+ void (*tc_atfork_unlock)(void);
+ void (*tc_arc4_lock)(void);
+ void (*tc_arc4_unlock)(void);
+ void (*tc_mutex_lock)(void **);
+ void (*tc_mutex_unlock)(void **);
+ void (*tc_mutex_destroy)(void **);
+ void (*tc_tag_lock)(void **);
+ void (*tc_tag_unlock)(void **);
+ void *(*tc_tag_storage)(void **, void *, size_t, void *);
+ __pid_t (*tc_fork)(void);
+ __pid_t (*tc_vfork)(void);
+};
+
+__BEGIN_PUBLIC_DECLS
/*
- * Weak symbols are used in libc so that the thread library can
- * efficiently wrap libc functions.
- *
- * Use WEAK_NAME(n) to get a libc-private name for n (_weak_n),
- * WEAK_ALIAS(n) to generate the weak symbol n pointing to _weak_n,
- * WEAK_PROTOTYPE(n) to generate a prototype for _weak_n (based on n).
+ * Set the callbacks used by libc
*/
-#define WEAK_NAME(name) __CONCAT(_weak_,name)
-#define WEAK_ALIAS(name) __weak_alias(name, WEAK_NAME(name))
-#ifdef __GNUC__
-#define WEAK_PROTOTYPE(name) __typeof__(name) WEAK_NAME(name)
-#else
-#define WEAK_PROTOTYPE(name) /* typeof() only in gcc */
-#endif
+void _thread_set_callbacks(const struct thread_callbacks *_cb, size_t _len);
+__END_PUBLIC_DECLS
-/*
- * Ditto for hand-written syscall stubs:
- *
- * Use STUB_NAME(n) to get the strong name of the stub: _thread_sys_n
- * STUB_ALIAS(n) to generate the weak symbol n pointing to _thread_sys_n,
- * STUB_PROTOTYPE(n) to generate a prototype for _thread_sys_n (based on n).
- */
-#define STUB_NAME(name) __CONCAT(_thread_sys_,name)
-#define STUB_ALIAS(name) __weak_alias(name, STUB_NAME(name))
-#ifdef __GNUC__
-#define STUB_PROTOTYPE(name) __typeof__(name) STUB_NAME(name)
-#else
-#define STUB_PROTOTYPE(name) /* typeof() only in gcc */
-#endif
+#ifdef __LIBC__
+__BEGIN_HIDDEN_DECLS
+/* the current set */
+extern struct thread_callbacks _thread_cb;
+__END_HIDDEN_DECLS
+#endif /* __LIBC__ */
/*
* helper macro to make unique names in the thread namespace
@@ -54,39 +110,11 @@ extern int __isthreaded;
#define __THREAD_NAME(name) __CONCAT(_thread_tagname_,name)
/*
- * helper functions that exist as (weak) null functions in libc and
- * (strong) functions in the thread library. These functions:
- *
- * _thread_tag_lock:
- * lock the mutex associated with the given tag. If the given
- * tag is NULL a tag is first allocated.
- *
- * _thread_tag_unlock:
- * unlock the mutex associated with the given tag. If the given
- * tag is NULL a tag is first allocated.
- *
- * _thread_tag_storage:
- * return a pointer to per thread instance of data associated
- * with the given tag. If the given tag is NULL a tag is first
- * allocated.
- *
- * _thread_mutex_lock:
- * lock the given mutex. If the given mutex is NULL,
- * rely on rthreads/pthreads implementation to initialize
- * the mutex before locking.
- *
- * _thread_mutex_unlock:
- * unlock the given mutex.
- *
- * _thread_mutex_destroy:
- * destroy the given mutex.
+ * Resolver code is special cased in that it uses global keys.
*/
-void _thread_tag_lock(void **);
-void _thread_tag_unlock(void **);
-void *_thread_tag_storage(void **, void *, size_t, void *);
-void _thread_mutex_lock(void **);
-void _thread_mutex_unlock(void **);
-void _thread_mutex_destroy(void **);
+extern void *__THREAD_NAME(_res);
+extern void *__THREAD_NAME(_res_ext);
+extern void *__THREAD_NAME(serv_mutex);
/*
* Macros used in libc to access thread mutex, keys, and per thread storage.
@@ -99,13 +127,40 @@ void _thread_mutex_destroy(void **);
static void *__THREAD_NAME(name)
#define _THREAD_PRIVATE_MUTEX(name) \
static void *__THREAD_NAME(name)
+
+
+#ifndef __LIBC__ /* building some sort of reach around */
+
+#define _THREAD_PRIVATE_MUTEX_LOCK(name) do {} while (0)
+#define _THREAD_PRIVATE_MUTEX_UNLOCK(name) do {} while (0)
+#define _THREAD_PRIVATE(keyname, storage, error) &(storage)
+#define _MUTEX_LOCK(mutex) do {} while (0)
+#define _MUTEX_UNLOCK(mutex) do {} while (0)
+#define _MUTEX_DESTROY(mutex) do {} while (0)
+#define _MALLOC_LOCK() do {} while (0)
+#define _MALLOC_UNLOCK() do {} while (0)
+#define _ATEXIT_LOCK() do {} while (0)
+#define _ATEXIT_UNLOCK() do {} while (0)
+#define _ATFORK_LOCK() do {} while (0)
+#define _ATFORK_UNLOCK() do {} while (0)
+#define _ARC4_LOCK() do {} while (0)
+#define _ARC4_UNLOCK() do {} while (0)
+
+#else /* building libc */
#define _THREAD_PRIVATE_MUTEX_LOCK(name) \
- _thread_tag_lock(&(__THREAD_NAME(name)))
+ do { \
+ if (_thread_cb.tc_tag_lock != NULL) \
+ _thread_cb.tc_tag_lock(&(__THREAD_NAME(name))); \
+ } while (0)
#define _THREAD_PRIVATE_MUTEX_UNLOCK(name) \
- _thread_tag_unlock(&(__THREAD_NAME(name)))
+ do { \
+ if (_thread_cb.tc_tag_unlock != NULL) \
+ _thread_cb.tc_tag_unlock(&(__THREAD_NAME(name))); \
+ } while (0)
#define _THREAD_PRIVATE(keyname, storage, error) \
- _thread_tag_storage(&(__THREAD_NAME(keyname)), &(storage), \
- sizeof (storage), error)
+ (_thread_cb.tc_tag_storage == NULL ? &(storage) : \
+ _thread_cb.tc_tag_storage(&(__THREAD_NAME(keyname)), \
+ &(storage), sizeof(storage), error))
/*
* Macros used in libc to access mutexes.
@@ -113,80 +168,65 @@ void _thread_mutex_destroy(void **);
#define _MUTEX_LOCK(mutex) \
do { \
if (__isthreaded) \
- _thread_mutex_lock(mutex); \
+ _thread_cb.tc_mutex_lock(mutex); \
} while (0)
#define _MUTEX_UNLOCK(mutex) \
do { \
if (__isthreaded) \
- _thread_mutex_unlock(mutex); \
+ _thread_cb.tc_mutex_unlock(mutex); \
} while (0)
#define _MUTEX_DESTROY(mutex) \
do { \
if (__isthreaded) \
- _thread_mutex_destroy(mutex); \
+ _thread_cb.tc_mutex_destroy(mutex); \
} while (0)
/*
- * Resolver code is special cased in that it uses global keys.
- */
-extern void *__THREAD_NAME(_res);
-extern void *__THREAD_NAME(_res_ext);
-extern void *__THREAD_NAME(serv_mutex);
-
-/*
* malloc lock/unlock prototypes and definitions
*/
-void _thread_malloc_lock(void);
-void _thread_malloc_unlock(void);
-
-#define _MALLOC_LOCK() do { \
- if (__isthreaded) \
- _thread_malloc_lock(); \
- } while (0)
-#define _MALLOC_UNLOCK() do { \
- if (__isthreaded) \
- _thread_malloc_unlock();\
- } while (0)
-
-void _thread_atexit_lock(void);
-void _thread_atexit_unlock(void);
-
-#define _ATEXIT_LOCK() do { \
- if (__isthreaded) \
- _thread_atexit_lock(); \
- } while (0)
-#define _ATEXIT_UNLOCK() do { \
- if (__isthreaded) \
- _thread_atexit_unlock();\
- } while (0)
-
-void _thread_atfork_lock(void);
-void _thread_atfork_unlock(void);
-
-#define _ATFORK_LOCK() do { \
- if (__isthreaded) \
- _thread_atfork_lock(); \
- } while (0)
-#define _ATFORK_UNLOCK() do { \
- if (__isthreaded) \
- _thread_atfork_unlock();\
- } while (0)
-
-void _thread_arc4_lock(void);
-void _thread_arc4_unlock(void);
-
-#define _ARC4_LOCK() do { \
- if (__isthreaded) \
- _thread_arc4_lock(); \
- } while (0)
-#define _ARC4_UNLOCK() do { \
- if (__isthreaded) \
- _thread_arc4_unlock();\
- } while (0)
+#define _MALLOC_LOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_malloc_lock(); \
+ } while (0)
+#define _MALLOC_UNLOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_malloc_unlock(); \
+ } while (0)
-/*
- * Wrapper for _thread_sys_fork()
- */
-pid_t _thread_fork(void);
+#define _ATEXIT_LOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_atexit_lock(); \
+ } while (0)
+#define _ATEXIT_UNLOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_atexit_unlock(); \
+ } while (0)
+
+#define _ATFORK_LOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_atfork_lock(); \
+ } while (0)
+#define _ATFORK_UNLOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_atfork_unlock(); \
+ } while (0)
+
+#define _ARC4_LOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_arc4_lock(); \
+ } while (0)
+#define _ARC4_UNLOCK() \
+ do { \
+ if (__isthreaded) \
+ _thread_cb.tc_arc4_unlock(); \
+ } while (0)
+#endif /* __LIBC__ */
#endif /* _THREAD_PRIVATE_H_ */