/** * @file * * @ingroup RTEMSScoreTLS * * @brief Thread-Local Storage (TLS) */ /* * Copyright (c) 2014 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 * 82178 Puchheim * Germany * * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #ifndef _RTEMS_SCORE_TLS_H #define _RTEMS_SCORE_TLS_H #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** * @defgroup RTEMSScoreTLS Thread-Local Storage (TLS) * * @ingroup RTEMSScore * * @brief Thread-local storage (TLS) support. * * Variants I and II are according to Ulrich Drepper, "ELF Handling For * Thread-Local Storage". * * @{ */ extern char _TLS_Data_begin[]; extern char _TLS_Data_end[]; extern char _TLS_Data_size[]; extern char _TLS_BSS_begin[]; extern char _TLS_BSS_end[]; extern char _TLS_BSS_size[]; extern char _TLS_Size[]; extern char _TLS_Alignment[]; typedef struct { /* * FIXME: Not sure if the generation number type is correct for all * architectures. */ uint32_t generation_number; void *tls_blocks[1]; } TLS_Dynamic_thread_vector; typedef struct TLS_Thread_control_block { #ifdef __i386__ struct TLS_Thread_control_block *tcb; #else TLS_Dynamic_thread_vector *dtv; uintptr_t reserved; #endif } TLS_Thread_control_block; typedef struct { uintptr_t module; uintptr_t offset; } TLS_Index; static inline uintptr_t _TLS_Get_size( void ) { /* * Do not use _TLS_Size here since this will lead GCC to assume that this * symbol is not 0 and the tests for 0 will be optimized away. */ return (uintptr_t) _TLS_BSS_end - (uintptr_t) _TLS_Data_begin; } static inline uintptr_t _TLS_Heap_align_up( uintptr_t val ) { uintptr_t msk = CPU_HEAP_ALIGNMENT - 1; return (val + msk) & ~msk; } static inline uintptr_t _TLS_Get_thread_control_block_area_size( uintptr_t alignment ) { return alignment <= sizeof(TLS_Thread_control_block) ? sizeof(TLS_Thread_control_block) : alignment; } static inline uintptr_t _TLS_Get_allocation_size( uintptr_t size, uintptr_t alignment ) { uintptr_t allocation_size = 0; allocation_size += _TLS_Heap_align_up( size ); allocation_size += _TLS_Get_thread_control_block_area_size( alignment ); #ifndef __i386__ allocation_size += sizeof(TLS_Dynamic_thread_vector); #endif return allocation_size; } static inline void *_TLS_Copy_and_clear( void *tls_area ) { tls_area = memcpy( tls_area, _TLS_Data_begin, (size_t) ((uintptr_t)_TLS_Data_size) ); memset( (char *) tls_area + (size_t)((intptr_t) _TLS_BSS_begin) - (size_t)((intptr_t) _TLS_Data_begin), 0, ((size_t) (intptr_t)_TLS_BSS_size) ); return tls_area; } static inline void *_TLS_Initialize( void *tls_block, TLS_Thread_control_block *tcb, TLS_Dynamic_thread_vector *dtv ) { #ifdef __i386__ (void) dtv; tcb->tcb = tcb; #else tcb->dtv = dtv; dtv->generation_number = 1; dtv->tls_blocks[0] = tls_block; #endif return _TLS_Copy_and_clear( tls_block ); } /* Use Variant I, TLS offsets emitted by linker takes the TCB into account */ static inline void *_TLS_TCB_at_area_begin_initialize( void *tls_area ) { void *tls_block = (char *) tls_area + _TLS_Get_thread_control_block_area_size( (uintptr_t) _TLS_Alignment ); TLS_Thread_control_block *tcb = tls_area; uintptr_t aligned_size = _TLS_Heap_align_up( (uintptr_t) _TLS_Size ); TLS_Dynamic_thread_vector *dtv = (TLS_Dynamic_thread_vector *) ((char *) tls_block + aligned_size); return _TLS_Initialize( tls_block, tcb, dtv ); } /* Use Variant I, TLS offsets emitted by linker neglects the TCB */ static inline void *_TLS_TCB_before_TLS_block_initialize( void *tls_area ) { void *tls_block = (char *) tls_area + _TLS_Get_thread_control_block_area_size( (uintptr_t) _TLS_Alignment ); TLS_Thread_control_block *tcb = (TLS_Thread_control_block *) ((char *) tls_block - sizeof(*tcb)); uintptr_t aligned_size = _TLS_Heap_align_up( (uintptr_t) _TLS_Size ); TLS_Dynamic_thread_vector *dtv = (TLS_Dynamic_thread_vector *) ((char *) tls_block + aligned_size); return _TLS_Initialize( tls_block, tcb, dtv ); } /* Use Variant II */ static inline void *_TLS_TCB_after_TLS_block_initialize( void *tls_area ) { uintptr_t size = (uintptr_t) _TLS_Size; uintptr_t tls_align = (uintptr_t) _TLS_Alignment; uintptr_t tls_mask = tls_align - 1; uintptr_t heap_align = _TLS_Heap_align_up( tls_align ); uintptr_t heap_mask = heap_align - 1; TLS_Thread_control_block *tcb = (TLS_Thread_control_block *) ((char *) tls_area + ((size + heap_mask) & ~heap_mask)); void *tls_block = (char *) tcb - ((size + tls_mask) & ~tls_mask); TLS_Dynamic_thread_vector *dtv = (TLS_Dynamic_thread_vector *) ((char *) tcb + sizeof(*tcb)); _TLS_Initialize( tls_block, tcb, dtv ); return tcb; } /** @} */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _RTEMS_SCORE_TLS_H */