From 73eeb757dad6123971edda029d185e4daa632c91 Mon Sep 17 00:00:00 2001 From: guenther <> Date: Sat, 7 May 2016 19:05:22 +0000 Subject: 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 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@ --- src/lib/libc/include/cancel.h | 75 +++++++++ src/lib/libc/include/namespace.h | 6 +- src/lib/libc/include/thread_private.h | 306 +++++++++++++++++++--------------- 3 files changed, 252 insertions(+), 135 deletions(-) create mode 100644 src/lib/libc/include/cancel.h (limited to 'src') diff --git a/src/lib/libc/include/cancel.h b/src/lib/libc/include/cancel.h new file mode 100644 index 0000000000..4f4add471b --- /dev/null +++ b/src/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 + * + * 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 +#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/src/lib/libc/include/namespace.h b/src/lib/libc/include/namespace.h index 16d524f666..980c1a8dd6 100644 --- a/src/lib/libc/include/namespace.h +++ b/src/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/src/lib/libc/include/thread_private.h b/src/lib/libc/include/thread_private.h index 43ebf7d96e..c2e0cf0b3f 100644 --- a/src/lib/libc/include/thread_private.h +++ b/src/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 */ #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 /* 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 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_ */ -- cgit v1.2.3-55-g6feb