forked from Imagelibrary/rtems
218 lines
5.0 KiB
C
218 lines
5.0 KiB
C
/**
|
|
* @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
|
|
* <rtems@embedded-brains.de>
|
|
*
|
|
* 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 <rtems/score/cpu.h>
|
|
|
|
#include <string.h>
|
|
|
|
#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 */
|