From c29dbda5fd7626d662badfcc26b6a5ce63c4abda Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Tue, 23 Mar 2021 11:23:52 +0800 Subject: [PATCH 1/3] build: fix cache issue when .flash.text section alignment is uncommon rodata dummy section has now the same alignment as flash text section, and at least the same size. For these reasons, the cache will map correctly the following rodata section. --- components/esp32c3/ld/esp32c3.project.ld.in | 11 ++++++++++- components/esp32s3/ld/esp32s3.project.ld.in | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/components/esp32c3/ld/esp32c3.project.ld.in b/components/esp32c3/ld/esp32c3.project.ld.in index 342a968a95..bcbbf1859e 100644 --- a/components/esp32c3/ld/esp32c3.project.ld.in +++ b/components/esp32c3/ld/esp32c3.project.ld.in @@ -283,9 +283,18 @@ SECTIONS _flash_cache_start = ABSOLUTE(0); } > default_code_seg + /** + * This dummy section represents the .flash.text section but in default_rodata_seg. + * Thus, it must have its alignement and (at least) its size. + */ .flash_rodata_dummy (NOLOAD): { - . = SIZEOF(.flash.text); + /* Start at the same alignement constraint than .flash.text */ + . = ALIGN(ALIGNOF(.flash.text)); + /* Create an empty gap as big as .flash.text section */ + . = . + SIZEOF(.flash.text); + /* Prepare the alignement of the section above. Few bytes (0x20) must be + * added for the mapping header. */ . = ALIGN(0x10000) + 0x20; _rodata_reserved_start = .; } > default_rodata_seg diff --git a/components/esp32s3/ld/esp32s3.project.ld.in b/components/esp32s3/ld/esp32s3.project.ld.in index ff46c4d269..05e8f79e67 100644 --- a/components/esp32s3/ld/esp32s3.project.ld.in +++ b/components/esp32s3/ld/esp32s3.project.ld.in @@ -314,9 +314,18 @@ SECTIONS _flash_cache_start = ABSOLUTE(0); } > default_code_seg + /** + * This dummy section represents the .flash.text section but in default_rodata_seg. + * Thus, it must have its alignement and (at least) its size. + */ .flash_rodata_dummy (NOLOAD): { - . = SIZEOF(.flash.text); + /* Start at the same alignement constraint than .flash.text */ + . = ALIGN(ALIGNOF(.flash.text)); + /* Create an empty gap as big as .flash.text section */ + . = . + SIZEOF(.flash.text); + /* Prepare the alignement of the section above. Few bytes (0x20) must be + * added for the mapping header. */ . = ALIGN(0x10000) + 0x20; _rodata_reserved_start = .; } > default_rodata_seg From 375f969d43c4a3539ade4897074db785f2afbc94 Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Thu, 18 Mar 2021 12:01:04 +0800 Subject: [PATCH 2/3] build: (Custom) App version info is now on a dedicated section, independent of the rodata alignment It is now possible to have any alignment restriction on rodata in the user applicaiton. It will not affect the first section which must be aligned on a 16-byte bound. Closes https://github.com/espressif/esp-idf/issues/6719 --- components/esp32/ld/esp32.ld | 9 +++ components/esp32/ld/esp32.project.ld.in | 10 ++- components/esp32c3/ld/esp32c3.ld | 9 +++ components/esp32c3/ld/esp32c3.project.ld.in | 11 +++- components/esp32s2/ld/esp32s2.ld | 10 +++ components/esp32s2/ld/esp32s2.project.ld.in | 10 ++- components/esp32s3/ld/esp32s3.ld | 9 +++ components/esp32s3/ld/esp32s3.project.ld.in | 11 +++- .../build_system/ldalign_test/CMakeLists.txt | 18 ++++++ .../build_system/ldalign_test/README.txt | 7 ++ .../ldalign_test/check_alignment.py | 64 +++++++++++++++++++ .../ldalign_test/main/CMakeLists.txt | 2 + .../ldalign_test/main/test_main.c | 16 +++++ 13 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 tools/test_apps/build_system/ldalign_test/CMakeLists.txt create mode 100644 tools/test_apps/build_system/ldalign_test/README.txt create mode 100644 tools/test_apps/build_system/ldalign_test/check_alignment.py create mode 100644 tools/test_apps/build_system/ldalign_test/main/CMakeLists.txt create mode 100644 tools/test_apps/build_system/ldalign_test/main/test_main.c diff --git a/components/esp32/ld/esp32.ld b/components/esp32/ld/esp32.ld index 22544af788..ffde663904 100644 --- a/components/esp32/ld/esp32.ld +++ b/components/esp32/ld/esp32.ld @@ -134,3 +134,12 @@ REGION_ALIAS("rtc_data_location", rtc_data_seg ); #else REGION_ALIAS("default_rodata_seg", dram0_0_seg); #endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS + +/** + * If rodata default segment is placed in `drom0_0_seg`, then flash's first rodata section must + * also be first in the segment. + */ +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS + ASSERT(_rodata_start == ORIGIN(default_rodata_seg), + ".flash.appdesc section must be placed at the beginning of the rodata segment.") +#endif diff --git a/components/esp32/ld/esp32.project.ld.in b/components/esp32/ld/esp32.project.ld.in index 6c5d40b524..cc0370fee6 100644 --- a/components/esp32/ld/esp32.project.ld.in +++ b/components/esp32/ld/esp32.project.ld.in @@ -271,13 +271,21 @@ SECTIONS "DRAM segment data does not fit.") /* When modifying the alignment, update tls_section_alignment in pxPortInitialiseStack */ - .flash.rodata : ALIGN(0x10) + .flash.appdesc : ALIGN(0x10) { _rodata_start = ABSOLUTE(.); *(.rodata_desc .rodata_desc.*) /* Should be the first. App version info. DO NOT PUT ANYTHING BEFORE IT! */ *(.rodata_custom_desc .rodata_custom_desc.*) /* Should be the second. Custom app version info. DO NOT PUT ANYTHING BEFORE IT! */ + /* Create an empty gap within this section. Thanks to this, the end of this + * section will match .flah.rodata's begin address. Thus, both sections + * will be merged when creating the final bin image. */ + . = ALIGN(ALIGNOF(.flash.rodata)); + } >default_rodata_seg + + .flash.rodata : ALIGN(0x10) + { mapping[flash_rodata] diff --git a/components/esp32c3/ld/esp32c3.ld b/components/esp32c3/ld/esp32c3.ld index 7f897a1c91..63a26c4276 100644 --- a/components/esp32c3/ld/esp32c3.ld +++ b/components/esp32c3/ld/esp32c3.ld @@ -107,3 +107,12 @@ REGION_ALIAS("rtc_data_location", rtc_iram_seg ); #else REGION_ALIAS("default_rodata_seg", dram0_0_seg); #endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS + +/** + * If rodata default segment is placed in `drom0_0_seg`, then flash's first rodata section must + * also be first in the segment. + */ +#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS + ASSERT(_flash_rodata_dummy_start == ORIGIN(default_rodata_seg), + ".flash_rodata_dummy section must be placed at the beginning of the rodata segment.") +#endif diff --git a/components/esp32c3/ld/esp32c3.project.ld.in b/components/esp32c3/ld/esp32c3.project.ld.in index bcbbf1859e..0a59b20fc4 100644 --- a/components/esp32c3/ld/esp32c3.project.ld.in +++ b/components/esp32c3/ld/esp32c3.project.ld.in @@ -289,6 +289,7 @@ SECTIONS */ .flash_rodata_dummy (NOLOAD): { + _flash_rodata_dummy_start = .; /* Start at the same alignement constraint than .flash.text */ . = ALIGN(ALIGNOF(.flash.text)); /* Create an empty gap as big as .flash.text section */ @@ -300,13 +301,21 @@ SECTIONS } > default_rodata_seg /* When modifying the alignment, don't forget to update tls_section_alignment in pxPortInitialiseStack */ - .flash.rodata : ALIGN(0x10) + .flash.appdesc : ALIGN(0x10) { _rodata_start = ABSOLUTE(.); *(.rodata_desc .rodata_desc.*) /* Should be the first. App version info. DO NOT PUT ANYTHING BEFORE IT! */ *(.rodata_custom_desc .rodata_custom_desc.*) /* Should be the second. Custom app version info. DO NOT PUT ANYTHING BEFORE IT! */ + /* Create an empty gap within this section. Thanks to this, the end of this + * section will match .flash.rodata's begin address. Thus, both sections + * will be merged when creating the final bin image. */ + . = ALIGN(ALIGNOF(.flash.rodata)); + } >default_rodata_seg + + .flash.rodata : ALIGN(0x10) + { mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ diff --git a/components/esp32s2/ld/esp32s2.ld b/components/esp32s2/ld/esp32s2.ld index 2ae61149ff..668d6fce6a 100644 --- a/components/esp32s2/ld/esp32s2.ld +++ b/components/esp32s2/ld/esp32s2.ld @@ -136,3 +136,13 @@ REGION_ALIAS("rtc_data_location", rtc_data_seg ); #else REGION_ALIAS("default_rodata_seg", dram0_0_seg); #endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS + + +/** + * If rodata default segment is placed in `drom0_0_seg`, then flash's first rodata section must + * also be first in the segment. + */ +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS + ASSERT(_rodata_reserved_start == ORIGIN(default_rodata_seg), + ".flash.appdesc section must be placed at the beginning of the rodata segment.") +#endif diff --git a/components/esp32s2/ld/esp32s2.project.ld.in b/components/esp32s2/ld/esp32s2.project.ld.in index 06528a15b2..74564b0ebb 100644 --- a/components/esp32s2/ld/esp32s2.project.ld.in +++ b/components/esp32s2/ld/esp32s2.project.ld.in @@ -266,7 +266,7 @@ SECTIONS } > dram0_0_seg /* When modifying the alignment, update tls_section_alignment in pxPortInitialiseStack */ - .flash.rodata : ALIGN(0x10) + .flash.appdesc : ALIGN(0x10) { _rodata_reserved_start = ABSOLUTE(.); _rodata_start = ABSOLUTE(.); @@ -274,6 +274,14 @@ SECTIONS *(.rodata_desc .rodata_desc.*) /* Should be the first. App version info. DO NOT PUT ANYTHING BEFORE IT! */ *(.rodata_custom_desc .rodata_custom_desc.*) /* Should be the second. Custom app version info. DO NOT PUT ANYTHING BEFORE IT! */ + /* Create an empty gap within this section. Thanks to this, the end of this + * section will match .flah.rodata's begin address. Thus, both sections + * will be merged when creating the final bin image. */ + . = ALIGN(ALIGNOF(.flash.rodata)); + } >default_rodata_seg + + .flash.rodata : ALIGN(0x10) + { mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ diff --git a/components/esp32s3/ld/esp32s3.ld b/components/esp32s3/ld/esp32s3.ld index 1b7c3d1bb9..e6c3c7bce2 100644 --- a/components/esp32s3/ld/esp32s3.ld +++ b/components/esp32s3/ld/esp32s3.ld @@ -117,3 +117,12 @@ REGION_ALIAS("default_rodata_seg", drom0_0_seg); #else REGION_ALIAS("default_rodata_seg", dram0_0_seg); #endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS + +/** + * If rodata default segment is placed in `drom0_0_seg`, then flash's first rodata section must + * also be first in the segment. + */ +#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS + ASSERT(_flash_rodata_dummy_start == ORIGIN(default_rodata_seg), + ".flash_rodata_dummy section must be placed at the beginning of the rodata segment.") +#endif diff --git a/components/esp32s3/ld/esp32s3.project.ld.in b/components/esp32s3/ld/esp32s3.project.ld.in index 05e8f79e67..b65758ef3e 100644 --- a/components/esp32s3/ld/esp32s3.project.ld.in +++ b/components/esp32s3/ld/esp32s3.project.ld.in @@ -320,6 +320,7 @@ SECTIONS */ .flash_rodata_dummy (NOLOAD): { + _flash_rodata_dummy_start = .; /* Start at the same alignement constraint than .flash.text */ . = ALIGN(ALIGNOF(.flash.text)); /* Create an empty gap as big as .flash.text section */ @@ -331,13 +332,21 @@ SECTIONS } > default_rodata_seg /* When modifying the alignment, don't forget to update tls_section_alignment in pxPortInitialiseStack */ - .flash.rodata : ALIGN(0x10) + .flash.appdesc : ALIGN(0x10) { _rodata_start = ABSOLUTE(.); *(.rodata_desc .rodata_desc.*) /* Should be the first. App version info. DO NOT PUT ANYTHING BEFORE IT! */ *(.rodata_custom_desc .rodata_custom_desc.*) /* Should be the second. Custom app version info. DO NOT PUT ANYTHING BEFORE IT! */ + /* Create an empty gap within this section. Thanks to this, the end of this + * section will match .flah.rodata's begin address. Thus, both sections + * will be merged when creating the final bin image. */ + . = ALIGN(ALIGNOF(.flash.rodata)); + } >default_rodata_seg + + .flash.rodata : ALIGN(0x10) + { mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ diff --git a/tools/test_apps/build_system/ldalign_test/CMakeLists.txt b/tools/test_apps/build_system/ldalign_test/CMakeLists.txt new file mode 100644 index 0000000000..74eb8e8377 --- /dev/null +++ b/tools/test_apps/build_system/ldalign_test/CMakeLists.txt @@ -0,0 +1,18 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ldalign_test) + +idf_build_get_property(python PYTHON) +idf_build_get_property(elf EXECUTABLE) + +set(check_alignment "${CMAKE_CURRENT_LIST_DIR}/check_alignment.py") +set(readelf "${CMAKE_TOOLCHAIN_PREFIX}readelf") + +add_custom_command( + TARGET ${elf} + POST_BUILD + COMMAND ${python} ${check_alignment} ${readelf} $ +) diff --git a/tools/test_apps/build_system/ldalign_test/README.txt b/tools/test_apps/build_system/ldalign_test/README.txt new file mode 100644 index 0000000000..a6299c7fad --- /dev/null +++ b/tools/test_apps/build_system/ldalign_test/README.txt @@ -0,0 +1,7 @@ +Runs a build test to check alignment and position of `.flash.appdesc` and +`.flash.rodata` sections. Indeed, `.flash.appdesc` shall ALWAYS be aligned on +a 16-byte bounds, whereas `.flash.rodata` can have any alignment. In any case, +the end address of first one shall match the start address of the second one. +This will let both of them be merged when generating the final bin image. +The Python script that performs the checks, `check_alignment.py`, automatically +runs after the app is built. diff --git a/tools/test_apps/build_system/ldalign_test/check_alignment.py b/tools/test_apps/build_system/ldalign_test/check_alignment.py new file mode 100644 index 0000000000..1f988fd9c7 --- /dev/null +++ b/tools/test_apps/build_system/ldalign_test/check_alignment.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# Copyright 2020 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import re +import subprocess +from typing import Tuple + +argparser = argparse.ArgumentParser() + +argparser.add_argument('readelf') +argparser.add_argument('elf') + +args = argparser.parse_args() + +# Get the content of the readelf command +contents = subprocess.check_output([args.readelf, '-S', args.elf]).decode() + + +# Define a class for readelf parsing error +class ParsingError(Exception): + pass + + +# Look for the start address and size of any section +def find_partition_info(sectionname): # type: (str) -> Tuple[int, int, int] + match = re.search(sectionname + r'\s+PROGBITS\s+([a-f0-9]+) [a-f0-9]+ ([a-f0-9]+) \d+\s+[A-Z]+ 0 0 (\d+)', + contents) + if not match: + raise ParsingError('ELF header parsing error') + # Return the address of the section, the size and the alignment + address = match.group(1) + size = match.group(2) + alignment = match.group(3) + return (int(address, 16), int(size, 16), int(alignment, 10)) + + +# Get address and size for .flash.appdesc section +app_address, app_size, app_align = find_partition_info('.flash.appdesc') + +# Same goes for .flash.rodata section +rodata_address, _, rodata_align = find_partition_info('.flash.rodata') + +# Assert than everything is as expected: +# appdesc is aligned on 16 +# rodata is aligned on 64 +# appdesc ends where rodata starts +assert app_align == 16, '.flash.appdesc section should have been aligned on 16!' +assert rodata_align == 64, '.flash.rodata section should have been aligned on 64!' +assert app_address + app_size == rodata_address, ".flash.appdesc's end address and .flash.rodata's begin start must have no gap in between!" diff --git a/tools/test_apps/build_system/ldalign_test/main/CMakeLists.txt b/tools/test_apps/build_system/ldalign_test/main/CMakeLists.txt new file mode 100644 index 0000000000..1df31fac80 --- /dev/null +++ b/tools/test_apps/build_system/ldalign_test/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "test_main.c" + INCLUDE_DIRS ".") diff --git a/tools/test_apps/build_system/ldalign_test/main/test_main.c b/tools/test_apps/build_system/ldalign_test/main/test_main.c new file mode 100644 index 0000000000..160197ba59 --- /dev/null +++ b/tools/test_apps/build_system/ldalign_test/main/test_main.c @@ -0,0 +1,16 @@ +#include + +const static uint32_t __attribute__ ((aligned (64))) testTab[] = + { + 0xff445566, 0x44556677, 0x33221100, + 0x88997755, 0x99887755, 0x88997755, + 0x99546327, 0x7946fa9e, 0xa6b5f8ee, + 0x12345678 + }; + +void app_main(void) +{ + /* Do something with the array, in order to avoid it being discarded. */ + for (uint32_t i = 0; i < 10; i++) + printf ("%x\n", testTab[i]); +} From b7707c54ce73d5a56311dacdeaf4434d0d3c3673 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 21 Apr 2021 11:49:58 +0200 Subject: [PATCH 3/3] 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) */