From b7707c54ce73d5a56311dacdeaf4434d0d3c3673 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 21 Apr 2021 11:49:58 +0200 Subject: [PATCH] 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. --- components/esp32/ld/esp32.project.ld.in | 5 ++- components/esp32c3/ld/esp32c3.project.ld.in | 3 +- components/esp32s2/ld/esp32s2.project.ld.in | 5 ++- components/esp32s3/ld/esp32s3.project.ld.in | 5 ++- components/freertos/port/riscv/port.c | 46 +++++++++++++++++++-- components/freertos/port/xtensa/port.c | 17 ++++---- 6 files changed, 66 insertions(+), 15 deletions(-) diff --git a/components/esp32/ld/esp32.project.ld.in b/components/esp32/ld/esp32.project.ld.in index cc0370fee6..4d1d7bd701 100644 --- a/components/esp32/ld/esp32.project.ld.in +++ b/components/esp32/ld/esp32.project.ld.in @@ -270,7 +270,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(.); @@ -286,6 +285,8 @@ SECTIONS .flash.rodata : ALIGN(0x10) { + _flash_rodata_start = ABSOLUTE(.); + mapping[flash_rodata] @@ -343,6 +344,8 @@ SECTIONS . = ALIGN(4); } >default_rodata_seg + _flash_rodata_align = ALIGNOF(.flash.rodata); + .flash.text : { _stext = .; diff --git a/components/esp32c3/ld/esp32c3.project.ld.in b/components/esp32c3/ld/esp32c3.project.ld.in index 0a59b20fc4..2419f1500f 100644 --- a/components/esp32c3/ld/esp32c3.project.ld.in +++ b/components/esp32c3/ld/esp32c3.project.ld.in @@ -300,7 +300,6 @@ SECTIONS _rodata_reserved_start = .; } > default_rodata_seg - /* When modifying the alignment, don't forget to update tls_section_alignment in pxPortInitialiseStack */ .flash.appdesc : ALIGN(0x10) { _rodata_start = ABSOLUTE(.); @@ -316,6 +315,8 @@ SECTIONS .flash.rodata : ALIGN(0x10) { + _flash_rodata_start = ABSOLUTE(.); + mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ diff --git a/components/esp32s2/ld/esp32s2.project.ld.in b/components/esp32s2/ld/esp32s2.project.ld.in index 74564b0ebb..172e8296fc 100644 --- a/components/esp32s2/ld/esp32s2.project.ld.in +++ b/components/esp32s2/ld/esp32s2.project.ld.in @@ -265,7 +265,6 @@ SECTIONS _bss_end = ABSOLUTE(.); } > dram0_0_seg - /* When modifying the alignment, update tls_section_alignment in pxPortInitialiseStack */ .flash.appdesc : ALIGN(0x10) { _rodata_reserved_start = ABSOLUTE(.); @@ -282,6 +281,8 @@ SECTIONS .flash.rodata : ALIGN(0x10) { + _flash_rodata_start = ABSOLUTE(.); + mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ @@ -338,6 +339,8 @@ SECTIONS . = ALIGN(4); } >default_rodata_seg + _flash_rodata_align = ALIGNOF(.flash.rodata); + .flash.text : { _stext = .; diff --git a/components/esp32s3/ld/esp32s3.project.ld.in b/components/esp32s3/ld/esp32s3.project.ld.in index b65758ef3e..4735e1fd0f 100644 --- a/components/esp32s3/ld/esp32s3.project.ld.in +++ b/components/esp32s3/ld/esp32s3.project.ld.in @@ -331,7 +331,6 @@ SECTIONS _rodata_reserved_start = .; } > default_rodata_seg - /* When modifying the alignment, don't forget to update tls_section_alignment in pxPortInitialiseStack */ .flash.appdesc : ALIGN(0x10) { _rodata_start = ABSOLUTE(.); @@ -347,6 +346,8 @@ SECTIONS .flash.rodata : ALIGN(0x10) { + _flash_rodata_start = ABSOLUTE(.); + mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ @@ -400,6 +401,8 @@ SECTIONS . = ALIGN(4); } > default_rodata_seg + _flash_rodata_align = ALIGNOF(.flash.rodata); + /* Marks the end of IRAM code segment */ .iram0.text_end (NOLOAD) : { diff --git a/components/freertos/port/riscv/port.c b/components/freertos/port/riscv/port.c index e53c20eb2c..2f6786841a 100644 --- a/components/freertos/port/riscv/port.c +++ b/components/freertos/port/riscv/port.c @@ -225,18 +225,58 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC extern uint32_t __global_pointer$; uint8_t* task_thread_local_start; uint8_t* threadptr; - extern char _thread_local_start, _thread_local_end, _rodata_start; + extern char _thread_local_start, _thread_local_end, _flash_rodata_start; /* Byte pointer, so that subsequent calculations don't depend on sizeof(StackType_t). */ uint8_t* sp = (uint8_t*) pxTopOfStack; - /* Set up TLS area */ + /* Set up TLS 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! + */ + uint32_t thread_local_sz = (uint32_t) (&_thread_local_end - &_thread_local_start); thread_local_sz = ALIGNUP(0x10, thread_local_sz); sp -= thread_local_sz; task_thread_local_start = sp; memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz); - threadptr = task_thread_local_start - (&_thread_local_start - &_rodata_start); + threadptr = task_thread_local_start - (&_thread_local_start - &_flash_rodata_start); /* Simulate the stack frame as it would be created by a context switch interrupt. */ sp -= RV_STK_FRMSZ; diff --git a/components/freertos/port/xtensa/port.c b/components/freertos/port/xtensa/port.c index 438d9dcfe5..edd8205df5 100644 --- a/components/freertos/port/xtensa/port.c +++ b/components/freertos/port/xtensa/port.c @@ -189,7 +189,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; @@ -248,24 +248,25 @@ 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 diagram in port/riscv/port.c illustrates the calculations below. + */ 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) */