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 7e57064792..cc6538c423 100644 --- a/components/esp32/ld/esp32.project.ld.in +++ b/components/esp32/ld/esp32.project.ld.in @@ -233,13 +233,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 a34486c8e3..86eb34d2d8 100644 --- a/components/esp32c3/ld/esp32c3.project.ld.in +++ b/components/esp32c3/ld/esp32c3.project.ld.in @@ -251,6 +251,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 */ @@ -262,13 +263,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 248cb8c39e..0928694458 100644 --- a/components/esp32s2/ld/esp32s2.project.ld.in +++ b/components/esp32s2/ld/esp32s2.project.ld.in @@ -252,7 +252,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(.); @@ -260,6 +260,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 f9efc14628..b3d0d25177 100644 --- a/components/esp32s3/ld/esp32s3.project.ld.in +++ b/components/esp32s3/ld/esp32s3.project.ld.in @@ -282,6 +282,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 */ @@ -293,13 +294,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]); +}