freertos: fix TLS run-time address calculation

Since dd849ffc, _rodata_start label has been moved to a different
linker output section from where the TLS templates (.tdata, .tbss)
are located. Since link-time addresses of thread-local variables are
calculated relative to the section start address, this resulted in
incorrect calculation of THREADPTR/$tp registers.

Fix by introducing new linker label, _flash_rodata_start, which points
to the .flash.rodata output section where TLS variables are located,
and use it when calculating THREADPTR/$tp.

Also remove the hardcoded rodata section alignment for Xtensa targets.
Alignment of rodata can be affected by the user application, which is
the issue dd849ffc was fixing. To accommodate any possible alignment,
save it in a linker label (_flash_rodata_align) and then use when
calculating THREADPTR. Note that this is not required on RISC-V, since
this target doesn't use TPOFF.
This commit is contained in:
Ivan Grokhotkov 2021-04-21 11:49:58 +02:00 committed by Omar Chebib
parent 974b8dd4c4
commit af7b21851e
3 changed files with 56 additions and 12 deletions

View File

@ -249,7 +249,6 @@ SECTIONS
ASSERT(((_bss_end - ORIGIN(dram0_0_seg)) <= LENGTH(dram0_0_seg)),
"DRAM segment data does not fit.")
/* When modifying the alignment, update tls_section_alignment in pxPortInitialiseStack */
.flash.appdesc : ALIGN(0x10)
{
_rodata_start = ABSOLUTE(.);
@ -265,6 +264,8 @@ SECTIONS
.flash.rodata : ALIGN(0x10)
{
_flash_rodata_start = ABSOLUTE(.);
mapping[flash_rodata]
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
@ -320,6 +321,8 @@ SECTIONS
. = ALIGN(4);
} >default_rodata_seg
_flash_rodata_align = ALIGNOF(.flash.rodata);
.flash.text :
{
_stext = .;

View File

@ -232,7 +232,6 @@ SECTIONS
_heap_start = ABSOLUTE(.);
} > dram0_0_seg
/* When modifying the alignment, update tls_section_alignment in pxPortInitialiseStack */
.flash.appdesc : ALIGN(0x10)
{
_rodata_reserved_start = ABSOLUTE(.);
@ -249,6 +248,8 @@ SECTIONS
.flash.rodata : ALIGN(0x10)
{
_flash_rodata_start = ABSOLUTE(.);
mapping[flash_rodata]
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
@ -305,6 +306,8 @@ SECTIONS
. = ALIGN(4);
} >default_rodata_seg
_flash_rodata_align = ALIGNOF(.flash.rodata);
.flash.text :
{
_stext = .;

View File

@ -164,7 +164,7 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px
#endif
uint32_t *threadptr;
void *task_thread_local_start;
extern int _thread_local_start, _thread_local_end, _rodata_start;
extern int _thread_local_start, _thread_local_end, _flash_rodata_start, _flash_rodata_align;
// TODO: check that TLS area fits the stack
uint32_t thread_local_sz = (uint8_t *)&_thread_local_end - (uint8_t *)&_thread_local_start;
@ -223,24 +223,62 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px
frame->vpri = 0xFFFFFFFF;
#endif
/* Init threadptr reg and TLS vars */
/* Init threadptr register and set up TLS run-time area.
* The following diagram illustrates the layout of link-time and run-time
* TLS sections.
*
* +-------------+
* |Section: | Linker symbols:
* |.flash.rodata| ---------------
* 0x0+-------------+ <-- _flash_rodata_start
* ^ | |
* | | Other data |
* | | ... |
* | +-------------+ <-- _thread_local_start
* | |.tbss | ^
* v | | |
* 0xNNNN|int example; | | (thread_local_size)
* |.tdata | v
* +-------------+ <-- _thread_local_end
* | Other data |
* | ... |
* | |
* +-------------+
*
* Local variables of
* pxPortInitialiseStack
* -----------------------
* +-------------+ <-- pxTopOfStack
* |.tdata (*) | ^
* ^ |int example; | |(thread_local_size
* | | | |
* | |.tbss (*) | v
* | +-------------+ <-- task_thread_local_start
* 0xNNNN | | | ^
* | | | |
* | | | |_thread_local_start - _rodata_start
* | | | |
* | | | v
* v +-------------+ <-- threadptr
*
* (*) The stack grows downward!
*/
task_thread_local_start = (void *)(((uint32_t)pxTopOfStack - XT_CP_SIZE - thread_local_sz) & ~0xf);
memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz);
threadptr = (uint32_t *)(sp + XT_STK_EXTRA);
/* Calculate THREADPTR value:
/* Calculate THREADPTR value.
* The generated code will add THREADPTR value to a constant value determined at link time,
* to get the address of the TLS variable.
* The constant value is calculated by the linker as follows
* (search for 'tpoff' in elf32-xtensa.c in BFD):
* offset = address - tls_section_vma + align_up(TCB_SIZE, tls_section_alignment)
* where TCB_SIZE is hardcoded to 8. There doesn't seem to be a way to propagate
* the section alignment value from the ld script into the code, so it is hardcoded
* in both places.
* where TCB_SIZE is hardcoded to 8.
* Note this is slightly different compared to the RISC-V port, where offset = address - tls_section_vma.
*/
const uint32_t tls_section_alignment = 0x10; /* has to be in sync with ALIGN value of .flash.rodata section */
const uint32_t tls_section_alignment = (uint32_t) &_flash_rodata_align; /* ALIGN value of .flash.rodata section */
const uint32_t tcb_size = 8; /* Unrelated to FreeRTOS, this is the constant from BFD */
const uint32_t base = (tcb_size + tls_section_alignment - 1) & (~(tls_section_alignment - 1));
*threadptr = (uint32_t)task_thread_local_start - ((uint32_t)&_thread_local_start - (uint32_t)&_rodata_start) - base;
*threadptr = (uint32_t)task_thread_local_start - ((uint32_t)&_thread_local_start - (uint32_t)&_flash_rodata_start) - base;
#if XCHAL_CP_NUM > 0
/* Init the coprocessor save area (see xtensa_context.h) */
@ -385,7 +423,7 @@ uint32_t xPortGetTickRateHz(void) {
void __attribute__((optimize("-O3"))) vPortEnterCritical(portMUX_TYPE *mux)
{
BaseType_t oldInterruptLevel = portENTER_CRITICAL_NESTED();
/* Interrupts may already be disabled (because we're doing this recursively)
/* Interrupts may already be disabled (because we're doing this recursively)
* but we can't get the interrupt level after
* vPortCPUAquireMutex, because it also may mess with interrupts.
* Get it here first, then later figure out if we're nesting
@ -434,4 +472,4 @@ void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, c
dest = strcat(dest, str[i]);
}
esp_system_abort(buf);
}
}