score: Fix TLS support for some code models

Store symbols with an arbitrary absolute address such as _TLS_Size,
_TLS_Alignment, _TLS_Data_size, and _TLS_BSS_size in an object to avoid issues
with some code models.

Update #4953.
This commit is contained in:
Sebastian Huber
2023-09-13 12:14:48 +02:00
parent 36b330910d
commit 206bbeb31b
4 changed files with 109 additions and 60 deletions

View File

@@ -10,7 +10,7 @@
*/ */
/* /*
* Copyright (C) 2014, 2022 embedded brains GmbH & Co. KG * Copyright (C) 2014, 2023 embedded brains GmbH & Co. KG
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@@ -59,31 +59,51 @@ extern "C" {
* @{ * @{
*/ */
extern char _TLS_Data_begin[]; /**
* @brief Represents the TLS configuration.
extern char _TLS_Data_end[]; */
typedef struct {
extern char _TLS_Data_size[]; /**
* @brief This member is initialized to _TLS_Data_begin.
extern char _TLS_BSS_begin[]; */
const char *data_begin;
extern char _TLS_BSS_end[];
extern char _TLS_BSS_size[];
extern char _TLS_Size[];
/** /**
* @brief The TLS section alignment. * @brief This member is initialized to _TLS_Data_size.
*
* This symbol is provided by the linker command file as the maximum alignment
* of the .tdata and .tbss sections. The linker ensures that the first TLS
* output section is aligned to the maximum alignment of all TLS output
* sections, see function _bfd_elf_tls_setup() in bfd/elflink.c of the GNU
* Binutils sources. The linker command file must take into account the case
* that the .tdata section is empty and the .tbss section is non-empty.
*/ */
extern char _TLS_Alignment[]; const char *data_size;
/**
* @brief This member is initialized to _TLS_BSS_begin.
*/
const char *bss_begin;
/**
* @brief This member is initialized to _TLS_BSS_size.
*/
const char *bss_size;
/**
* @brief This member is initialized to _TLS_Size.
*/
const char *size;
/**
* @brief This member is initialized to _TLS_Alignment.
*/
const char *alignment;
} TLS_Configuration;
/**
* @brief Provides the TLS configuration.
*
* Directly using symbols with an arbitrary absolute address such as
* _TLS_Alignment may not work with all code models (for example the AArch64
* tiny and small code models). Store the addresses in a read-only object.
* Using the volatile qualifier ensures that the compiler actually loads the
* address from the object.
*/
extern const volatile TLS_Configuration _TLS_Configuration;
typedef struct { typedef struct {
/* /*
@@ -115,39 +135,25 @@ typedef struct {
uintptr_t offset; uintptr_t offset;
} TLS_Index; } TLS_Index;
/**
* @brief Gets the size of the thread-local storage data in bytes.
*
* @return Returns the size of the thread-local storage data in bytes.
*/
static inline uintptr_t _TLS_Get_size( void )
{
uintptr_t size;
/*
* We must be careful with using _TLS_Size here since this could lead GCC to
* assume that this symbol is not 0 and the tests for 0 will be optimized
* away.
*/
size = (uintptr_t) _TLS_Size;
RTEMS_OBFUSCATE_VARIABLE( size );
return size;
}
/** /**
* @brief Gets the size of the thread control block area in bytes. * @brief Gets the size of the thread control block area in bytes.
* *
* @param config is the TLS configuration.
*
* @return Returns the size of the thread control block area in bytes. * @return Returns the size of the thread control block area in bytes.
*/ */
static inline uintptr_t _TLS_Get_thread_control_block_area_size( void ) static inline uintptr_t _TLS_Get_thread_control_block_area_size(
const volatile TLS_Configuration *config
)
{ {
#if CPU_THREAD_LOCAL_STORAGE_VARIANT == 11 #if CPU_THREAD_LOCAL_STORAGE_VARIANT == 11
uintptr_t alignment; uintptr_t alignment;
alignment = (uintptr_t) _TLS_Alignment; alignment = (uintptr_t) config->alignment;
return RTEMS_ALIGN_UP( sizeof( TLS_Thread_control_block ), alignment ); return RTEMS_ALIGN_UP( sizeof( TLS_Thread_control_block ), alignment );
#else #else
(void) config;
return sizeof( TLS_Thread_control_block ); return sizeof( TLS_Thread_control_block );
#endif #endif
} }
@@ -163,17 +169,23 @@ uintptr_t _TLS_Get_allocation_size( void );
/** /**
* @brief Initializes the thread-local storage data. * @brief Initializes the thread-local storage data.
* *
* @param config is the TLS configuration.
*
* @param[out] tls_data is the thread-local storage data to initialize. * @param[out] tls_data is the thread-local storage data to initialize.
*/ */
static inline void _TLS_Copy_and_clear( void *tls_data ) static inline void _TLS_Copy_and_clear(
const volatile TLS_Configuration *config,
void *tls_data
)
{ {
tls_data = memcpy( tls_data, _TLS_Data_begin, (uintptr_t) _TLS_Data_size ); tls_data =
memcpy( tls_data, config->data_begin, (uintptr_t) config->data_size );
memset( memset(
(char *) tls_data + (char *) tls_data +
(uintptr_t) _TLS_BSS_begin - (uintptr_t) _TLS_Data_begin, (uintptr_t) config->bss_begin - (uintptr_t) config->data_begin,
0, 0,
(uintptr_t) _TLS_BSS_size (uintptr_t) config->bss_size
); );
} }
@@ -213,6 +225,7 @@ static inline void _TLS_Initialize_TCB_and_DTV(
*/ */
static inline void *_TLS_Initialize_area( void *tls_area ) static inline void *_TLS_Initialize_area( void *tls_area )
{ {
const volatile TLS_Configuration *config;
uintptr_t alignment; uintptr_t alignment;
void *tls_data; void *tls_data;
TLS_Thread_control_block *tcb; TLS_Thread_control_block *tcb;
@@ -226,7 +239,8 @@ static inline void *_TLS_Initialize_area( void *tls_area )
uintptr_t alignment_2; uintptr_t alignment_2;
#endif #endif
alignment = (uintptr_t) _TLS_Alignment; config = &_TLS_Configuration;
alignment = (uintptr_t) config->alignment;
#ifdef __i386__ #ifdef __i386__
dtv = NULL; dtv = NULL;
@@ -249,7 +263,7 @@ static inline void *_TLS_Initialize_area( void *tls_area )
#elif CPU_THREAD_LOCAL_STORAGE_VARIANT == 20 #elif CPU_THREAD_LOCAL_STORAGE_VARIANT == 20
alignment_2 = RTEMS_ALIGN_UP( alignment, CPU_SIZEOF_POINTER ); alignment_2 = RTEMS_ALIGN_UP( alignment, CPU_SIZEOF_POINTER );
tls_area = (void *) RTEMS_ALIGN_UP( (uintptr_t) tls_area, alignment_2 ); tls_area = (void *) RTEMS_ALIGN_UP( (uintptr_t) tls_area, alignment_2 );
size = _TLS_Get_size(); size = (uintptr_t) config->size;
tcb = (TLS_Thread_control_block *) tcb = (TLS_Thread_control_block *)
((char *) tls_area + RTEMS_ALIGN_UP( size, alignment_2 )); ((char *) tls_area + RTEMS_ALIGN_UP( size, alignment_2 ));
tls_data = (char *) tcb - RTEMS_ALIGN_UP( size, alignment ); tls_data = (char *) tcb - RTEMS_ALIGN_UP( size, alignment );
@@ -259,7 +273,7 @@ static inline void *_TLS_Initialize_area( void *tls_area )
#endif #endif
_TLS_Initialize_TCB_and_DTV( tls_data, tcb, dtv ); _TLS_Initialize_TCB_and_DTV( tls_data, tcb, dtv );
_TLS_Copy_and_clear( tls_data ); _TLS_Copy_and_clear( config, tls_data );
return return_value; return return_value;
} }

View File

@@ -48,7 +48,7 @@ void *__tls_get_addr(const TLS_Index *ti)
{ {
const Thread_Control *executing = _Thread_Get_executing(); const Thread_Control *executing = _Thread_Get_executing();
void *tls_data = (char *) executing->Registers.thread_id void *tls_data = (char *) executing->Registers.thread_id
+ _TLS_Get_thread_control_block_area_size(); + _TLS_Get_thread_control_block_area_size( &_TLS_Configuration );
assert(ti->module == 1); assert(ti->module == 1);

View File

@@ -10,7 +10,7 @@
*/ */
/* /*
* Copyright (C) 2014, 2022 embedded brains GmbH & Co. KG * Copyright (C) 2014, 2023 embedded brains GmbH & Co. KG
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@@ -42,14 +42,47 @@
#include <rtems/score/interr.h> #include <rtems/score/interr.h>
#include <rtems/score/thread.h> #include <rtems/score/thread.h>
extern char _TLS_Data_begin[];
extern char _TLS_Data_size[];
extern char _TLS_BSS_begin[];
extern char _TLS_BSS_size[];
extern char _TLS_Size[];
/**
* @brief The TLS section alignment.
*
* This symbol is provided by the linker command file as the maximum alignment
* of the .tdata and .tbss sections. The linker ensures that the first TLS
* output section is aligned to the maximum alignment of all TLS output
* sections, see function _bfd_elf_tls_setup() in bfd/elflink.c of the GNU
* Binutils sources. The linker command file must take into account the case
* that the .tdata section is empty and the .tbss section is non-empty.
*/
extern char _TLS_Alignment[];
const volatile TLS_Configuration _TLS_Configuration = {
.data_begin = _TLS_Data_begin,
.data_size = _TLS_Data_size,
.bss_begin = _TLS_BSS_begin,
.bss_size = _TLS_BSS_size,
.size = _TLS_Size,
.alignment = _TLS_Alignment
};
static uintptr_t _TLS_Allocation_size; static uintptr_t _TLS_Allocation_size;
uintptr_t _TLS_Get_allocation_size( void ) uintptr_t _TLS_Get_allocation_size( void )
{ {
const volatile TLS_Configuration *config;
uintptr_t size; uintptr_t size;
uintptr_t allocation_size; uintptr_t allocation_size;
size = _TLS_Get_size(); config = &_TLS_Configuration;
size = (uintptr_t) config->size;
if ( size == 0 ) { if ( size == 0 ) {
return 0; return 0;
@@ -66,7 +99,7 @@ uintptr_t _TLS_Get_allocation_size( void )
* shall meet the stack alignment requirement. * shall meet the stack alignment requirement.
*/ */
stack_align = CPU_STACK_ALIGNMENT; stack_align = CPU_STACK_ALIGNMENT;
tls_align = RTEMS_ALIGN_UP( (uintptr_t) _TLS_Alignment, stack_align ); tls_align = RTEMS_ALIGN_UP( (uintptr_t) config->alignment, stack_align );
#ifndef __i386__ #ifndef __i386__
/* Reserve space for the dynamic thread vector */ /* Reserve space for the dynamic thread vector */

View File

@@ -67,9 +67,11 @@ static void task(rtems_task_argument arg)
static void check_tls_size(void) static void check_tls_size(void)
{ {
const volatile TLS_Configuration *config;
uintptr_t tls_size; uintptr_t tls_size;
tls_size = _TLS_Get_size(); config = &_TLS_Configuration;
tls_size = (uintptr_t) config->size;
if (tls_size != 1) { if (tls_size != 1) {
printk( printk(