diff --git a/CMakeLists.txt b/CMakeLists.txt index e00a17e2e98..c6c4fb23ff5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,10 @@ if(CONFIG_COMPILER_NO_MERGE_CONSTANTS) list(APPEND compile_options "-fno-merge-constants") endif() +if(CONFIG_COMPILER_ENABLE_TEXT_SECTION_LITERALS) + list(APPEND compile_options "-mtext-section-literals") +endif() + if(CONFIG_COMPILER_STACK_CHECK_MODE_NORM) list(APPEND compile_options "-fstack-protector") elseif(CONFIG_COMPILER_STACK_CHECK_MODE_STRONG) diff --git a/Kconfig b/Kconfig index dcb945a0285..ec506969854 100644 --- a/Kconfig +++ b/Kconfig @@ -576,6 +576,20 @@ mainmenu "Espressif IoT Development Framework Configuration" distribution is more uniform across libraries. On downside, it may increase the binary size and hence should be used during development phase only. + config COMPILER_ENABLE_TEXT_SECTION_LITERALS + bool + depends on IDF_TOOLCHAIN_GCC + depends on IDF_TARGET_ARCH_XTENSA + default y if ESPTOOLPY_FAST_REFLASHING + help + Intersperse Xtensa literals within the text section to keep + them as close as possible to their references. This prevents + literals from being placed into a separate section in the + output file and prevents the linker from combining literal + pools from different object files. Enabling this is necessary + for fast reflashing to prevent mixing code from mutable and + immutable libraries. + config COMPILER_WARN_WRITE_STRINGS bool "Enable -Wwrite-strings warning flag" default "n" @@ -784,3 +798,4 @@ mainmenu "Espressif IoT Development Framework Configuration" - CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION - CONFIG_I3C_MASTER_ENABLED - CONFIG_MBEDTLS_ESP_IDF_USE_PSA_CRYPTO + - CONFIG_ESPTOOLPY_FAST_REFLASHING diff --git a/components/esp_system/ld/esp32/sections.ld.in b/components/esp_system/ld/esp32/sections.ld.in index 22b76e32b2d..26966a50033 100644 --- a/components/esp_system/ld/esp32/sections.ld.in +++ b/components/esp_system/ld/esp32/sections.ld.in @@ -21,6 +21,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.literal .text .literal.* .text.*) _rtc_text_end = ABSOLUTE(.); @@ -51,6 +55,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -70,6 +78,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.*) _rtc_data_end = ABSOLUTE(.); @@ -85,6 +97,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > rtc_data_location @@ -224,6 +240,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > iram0_0_seg .dram0.data : @@ -239,6 +260,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > dram0_0_seg @@ -277,6 +303,10 @@ SECTIONS mapping[extern_ram] + /* mutable libs begin */ + mutable[extern_ram] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _ext_ram_bss_end) } > extern_ram_seg @@ -289,8 +319,13 @@ SECTIONS * ldgen places all bss-related data to mapping[dram0_bss] * (See components/esp_system/app.lf). */ + mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > dram0_0_seg @@ -377,6 +412,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); /* Literals are also RO data. */ @@ -411,6 +451,10 @@ SECTIONS _rodata_reserved_end = ABSOLUTE(.); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg .flash.text : @@ -425,6 +469,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.warning) *(.gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) @@ -466,6 +515,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > iram0_0_seg @@ -475,6 +528,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(4, _iram_end) } > iram0_0_seg diff --git a/components/esp_system/ld/esp32c2/sections.ld.in b/components/esp_system/ld/esp32c2/sections.ld.in index a905dde289f..044a96ca452 100644 --- a/components/esp_system/ld/esp32c2/sections.ld.in +++ b/components/esp_system/ld/esp32c2/sections.ld.in @@ -24,6 +24,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > iram0_0_seg /** @@ -42,7 +47,12 @@ SECTIONS .dram0.bt.data : { _data_start = ABSOLUTE(.); + mapping[dram0_bt_data] + + /* mutable libs begin */ + mutable[dram0_bt_data] + /* mutable libs end */ } > dram0_0_seg .dram0.bt.bss (NOLOAD) : @@ -51,6 +61,10 @@ SECTIONS mapping[dram0_bt_bss] + /* mutable libs begin */ + mutable[dram0_bt_bss] + /* mutable libs end */ + _bss_bt_end = ABSOLUTE(.); } > dram0_0_seg @@ -67,6 +81,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > dram0_0_seg @@ -94,8 +113,13 @@ SECTIONS * ldgen places all bss-related data to mapping[dram0_bss] * (See components/esp_system/app.lf). */ + mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > dram0_0_seg @@ -114,6 +138,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -216,6 +245,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -287,6 +321,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of IRAM code segment */ @@ -301,6 +339,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > iram0_0_seg @@ -310,6 +352,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); } > iram0_0_seg @@ -324,6 +370,10 @@ SECTIONS mapping[iram0_bt_text] + /* mutable libs begin */ + mutable[iram0_bt_text] + /* mutable libs end */ + ALIGNED_SYMBOL(16, _iram_end) } > iram0_0_seg diff --git a/components/esp_system/ld/esp32c3/sections.ld.in b/components/esp_system/ld/esp32c3/sections.ld.in index e18a528dd5a..fba4a6f40f9 100644 --- a/components/esp_system/ld/esp32c3/sections.ld.in +++ b/components/esp_system/ld/esp32c3/sections.ld.in @@ -23,6 +23,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.text .text.*) *(.rtc_text_end_test) @@ -46,6 +50,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -63,6 +71,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .srodata.*) _rtc_data_end = ABSOLUTE(.); @@ -78,6 +90,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > rtc_data_location @@ -164,6 +180,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > iram0_0_seg /** @@ -189,6 +210,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > dram0_0_seg @@ -216,8 +242,13 @@ SECTIONS * ldgen places all bss-related data to mapping[dram0_bss] * (See components/esp_system/app.lf). */ + mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > dram0_0_seg @@ -236,6 +267,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -338,6 +374,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -409,6 +450,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of IRAM code segment */ @@ -430,6 +475,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > iram0_0_seg @@ -439,6 +488,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) } > iram0_0_seg diff --git a/components/esp_system/ld/esp32c5/sections.ld.in b/components/esp_system/ld/esp32c5/sections.ld.in index 43be52efc72..1b74d282bc7 100644 --- a/components/esp_system/ld/esp32c5/sections.ld.in +++ b/components/esp_system/ld/esp32c5/sections.ld.in @@ -28,6 +28,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.text .text.*) *(.rtc_text_end_test) @@ -48,6 +52,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -65,6 +73,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .srodata.*) _rtc_data_end = ABSOLUTE(.); @@ -80,6 +92,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > lp_ram_seg @@ -179,6 +195,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > sram_seg /* Marks the end of IRAM code segment */ @@ -196,6 +217,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > sram_seg @@ -205,6 +230,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) } > sram_seg @@ -232,6 +261,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > sram_seg @@ -261,6 +295,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > sram_seg @@ -278,6 +316,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -387,6 +430,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -466,6 +514,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of data, bss and possibly rodata */ @@ -494,6 +546,10 @@ SECTIONS mapping[extern_ram] + /* mutable libs begin */ + mutable[extern_ram] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _ext_ram_bss_end) } > extern_ram_seg #endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY diff --git a/components/esp_system/ld/esp32c6/sections.ld.in b/components/esp_system/ld/esp32c6/sections.ld.in index 01edda27eea..b1384433e31 100644 --- a/components/esp_system/ld/esp32c6/sections.ld.in +++ b/components/esp_system/ld/esp32c6/sections.ld.in @@ -28,6 +28,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.text .text.*) *(.rtc_text_end_test) @@ -48,6 +52,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -65,6 +73,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .srodata.*) _rtc_data_end = ABSOLUTE(.); @@ -80,6 +92,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > lp_ram_seg @@ -192,6 +208,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > sram_seg /* Marks the end of IRAM code segment */ @@ -209,6 +230,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > sram_seg @@ -218,6 +243,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) @@ -237,6 +266,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > sram_seg @@ -266,6 +300,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > sram_seg @@ -281,6 +319,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -383,6 +426,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -454,6 +502,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of data, bss and possibly rodata */ diff --git a/components/esp_system/ld/esp32c61/sections.ld.in b/components/esp_system/ld/esp32c61/sections.ld.in index db2675780b5..628bb3b9001 100644 --- a/components/esp_system/ld/esp32c61/sections.ld.in +++ b/components/esp_system/ld/esp32c61/sections.ld.in @@ -37,6 +37,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > sram_seg /* Marks the end of IRAM code segment */ @@ -54,6 +59,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > sram_seg @@ -63,6 +72,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) } > sram_seg @@ -90,6 +103,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > sram_seg @@ -119,6 +137,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > sram_seg @@ -136,6 +158,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -246,6 +273,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -325,6 +357,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of data, bss and possibly rodata */ @@ -353,6 +389,10 @@ SECTIONS mapping[extern_ram] + /* mutable libs begin */ + mutable[extern_ram] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _ext_ram_bss_end) } > extern_ram_seg #endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY diff --git a/components/esp_system/ld/esp32h2/sections.ld.in b/components/esp_system/ld/esp32h2/sections.ld.in index 0fd235f04de..4d4c5293c8f 100644 --- a/components/esp_system/ld/esp32h2/sections.ld.in +++ b/components/esp_system/ld/esp32h2/sections.ld.in @@ -28,6 +28,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.text .text.*) *(.rtc_text_end_test) @@ -48,6 +52,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -65,6 +73,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .srodata.*) _rtc_data_end = ABSOLUTE(.); @@ -80,6 +92,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > lp_ram_seg @@ -194,6 +210,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > sram_seg /* Marks the end of IRAM code segment */ @@ -211,6 +232,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > sram_seg @@ -220,6 +245,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) @@ -239,6 +268,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > sram_seg @@ -268,6 +302,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > sram_seg @@ -283,6 +321,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -385,6 +428,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -456,6 +504,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of data, bss and possibly rodata */ diff --git a/components/esp_system/ld/esp32h21/sections.ld.in b/components/esp_system/ld/esp32h21/sections.ld.in index d2227359f60..1d6ec0fc2eb 100644 --- a/components/esp_system/ld/esp32h21/sections.ld.in +++ b/components/esp_system/ld/esp32h21/sections.ld.in @@ -30,6 +30,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.text .text.*) *(.rtc_text_end_test) @@ -50,6 +54,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -67,6 +75,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .srodata.*) _rtc_data_end = ABSOLUTE(.); @@ -82,6 +94,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > lp_ram_seg @@ -183,6 +199,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > sram_seg /* Marks the end of IRAM code segment */ @@ -200,6 +221,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > sram_seg @@ -209,6 +234,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) @@ -228,6 +257,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > sram_seg @@ -257,6 +291,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > sram_seg @@ -272,6 +310,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -374,6 +417,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -445,6 +493,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of data, bss and possibly rodata */ diff --git a/components/esp_system/ld/esp32h4/sections.ld.in b/components/esp_system/ld/esp32h4/sections.ld.in index fa715e3a23d..bc355709e33 100644 --- a/components/esp_system/ld/esp32h4/sections.ld.in +++ b/components/esp_system/ld/esp32h4/sections.ld.in @@ -27,6 +27,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > sram_seg /* Marks the end of IRAM code segment */ @@ -44,6 +49,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > sram_seg @@ -53,6 +62,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) } > sram_seg @@ -71,6 +84,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > sram_seg @@ -100,6 +118,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > sram_seg @@ -117,6 +139,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -219,6 +246,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > default_rodata_seg @@ -290,6 +322,10 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /* Marks the end of data, bss and possibly rodata */ diff --git a/components/esp_system/ld/esp32p4/sections.ld.in b/components/esp_system/ld/esp32p4/sections.ld.in index be51ae53183..2e3d85f6bf5 100644 --- a/components/esp_system/ld/esp32p4/sections.ld.in +++ b/components/esp_system/ld/esp32p4/sections.ld.in @@ -26,8 +26,13 @@ SECTIONS ALIGNED_SYMBOL(_esp_pmp_align_size, _rtc_text_start) arrays[rtc_text] + mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.text .text.*) *(.rtc_text_end_test) @@ -47,8 +52,13 @@ SECTIONS ALIGNED_SYMBOL(4, _rtc_force_fast_start) arrays[rtc_force_fast] + mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -65,8 +75,13 @@ SECTIONS _rtc_data_start = ABSOLUTE(.); arrays[rtc_data] + mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .srodata.*) _rtc_data_end = ABSOLUTE(.); @@ -81,8 +96,13 @@ SECTIONS *rtc_wake_stub*.*(COMMON) arrays[rtc_bss] + mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > lp_ram_seg @@ -163,8 +183,13 @@ SECTIONS _tcm_text_start = ABSOLUTE(.); arrays[tcm_text] + mapping[tcm_text] + /* mutable libs begin */ + mutable[tcm_text] + /* mutable libs end */ + _tcm_text_end = ABSOLUTE(.); } > tcm_idram_seg @@ -173,8 +198,13 @@ SECTIONS _tcm_data_start = ABSOLUTE(.); arrays[tcm_data] + mapping[tcm_data] + /* mutable libs begin */ + mutable[tcm_data] + /* mutable libs end */ + _tcm_data_end = ABSOLUTE(.); } > tcm_idram_seg @@ -190,8 +220,14 @@ SECTIONS _iram_text_start = ABSOLUTE(.); arrays[iram0_text] + mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > sram_low /* Marks the end of IRAM code segment */ @@ -208,8 +244,13 @@ SECTIONS ALIGNED_SYMBOL(16, _iram_data_start) arrays[iram0_data] + mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + _iram_data_end = ABSOLUTE(.); } > sram_low @@ -218,8 +259,13 @@ SECTIONS ALIGNED_SYMBOL(16, _iram_bss_start) arrays[iram0_bss] + mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(16, _iram_end) @@ -238,8 +284,14 @@ SECTIONS *(.jcr) arrays[dram0_data] + mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end_low = ABSOLUTE(.); } > sram_low @@ -249,6 +301,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end_high = ABSOLUTE(.); } > sram_high @@ -278,8 +335,14 @@ SECTIONS _text_start = ABSOLUTE(.); arrays[flash_text] + mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.linkonce.t.*) *(.gnu.warning) @@ -360,6 +423,7 @@ SECTIONS _flash_rodata_start = ABSOLUTE(.); arrays[flash_rodata] + mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ @@ -396,6 +460,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); . = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA)); } > rodata_seg_low @@ -475,7 +544,13 @@ SECTIONS _rodata_reserved_end = ADDR(.flash.tbss); arrays[rodata_noload] + mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ + } > rodata_seg_low #if CONFIG_SPIRAM_XIP_FROM_PSRAM @@ -495,8 +570,15 @@ SECTIONS .ext_ram.bss (NOLOAD) : { _ext_ram_bss_start = ABSOLUTE(.); + arrays[extern_ram] + mapping[extern_ram] + + /* mutable libs begin */ + mutable[extern_ram] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _ext_ram_bss_end) } > ext_ram_seg #endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY @@ -525,8 +607,13 @@ SECTIONS * (See components/esp_system/app.lf). */ arrays[dram0_bss] + mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _bss_end_low) } > sram_low @@ -540,6 +627,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _bss_end_high) } > sram_high diff --git a/components/esp_system/ld/esp32s2/sections.ld.in b/components/esp_system/ld/esp32s2/sections.ld.in index 1832712fd45..7f90596d340 100644 --- a/components/esp_system/ld/esp32s2/sections.ld.in +++ b/components/esp_system/ld/esp32s2/sections.ld.in @@ -30,6 +30,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.literal .text .literal.* .text.*) HIDDEN(_rtc_code_end = .); @@ -65,6 +69,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -84,6 +92,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.*) _rtc_data_end = ABSOLUTE(.); @@ -99,6 +111,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > rtc_data_location @@ -213,6 +229,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + /* Padding for possible CPU prefetch + alignment for PMS split lines */ . += _esp_memprot_prefetch_pad_size; . = ALIGN(_esp_memprot_align_size); @@ -241,6 +262,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > dram0_0_seg @@ -267,6 +293,10 @@ SECTIONS mapping[extern_ram] + /* mutable libs begin */ + mutable[extern_ram] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _ext_ram_bss_end) } > extern_ram_seg #endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY @@ -297,6 +327,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > dram0_0_seg @@ -380,6 +414,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); /* Literals are also RO data. */ @@ -414,6 +453,10 @@ SECTIONS _rodata_reserved_end = ABSOLUTE(.); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg .flash.text : @@ -428,6 +471,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.warning) *(.gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) diff --git a/components/esp_system/ld/esp32s3/sections.ld.in b/components/esp_system/ld/esp32s3/sections.ld.in index 26abf1fa661..52a6e7c460f 100644 --- a/components/esp_system/ld/esp32s3/sections.ld.in +++ b/components/esp_system/ld/esp32s3/sections.ld.in @@ -26,6 +26,10 @@ SECTIONS mapping[rtc_text] + /* mutable libs begin */ + mutable[rtc_text] + /* mutable libs end */ + *rtc_wake_stub*.*(.literal .text .literal.* .text.*) *(.rtc_text_end_test) @@ -49,6 +53,10 @@ SECTIONS mapping[rtc_force_fast] + /* mutable libs begin */ + mutable[rtc_force_fast] + /* mutable libs end */ + *(.rtc.force_fast .rtc.force_fast.*) ALIGNED_SYMBOL(4, _rtc_force_fast_end) @@ -68,6 +76,10 @@ SECTIONS mapping[rtc_data] + /* mutable libs begin */ + mutable[rtc_data] + /* mutable libs end */ + *rtc_wake_stub*.*(.data .rodata .data.* .rodata.*) _rtc_data_end = ABSOLUTE(.); @@ -83,6 +95,10 @@ SECTIONS mapping[rtc_bss] + /* mutable libs begin */ + mutable[rtc_bss] + /* mutable libs end */ + _rtc_bss_end = ABSOLUTE(.); } > rtc_data_location @@ -197,6 +213,11 @@ SECTIONS mapping[iram0_text] + /* mutable libs begin */ + mutable[iram0_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + } > iram0_0_seg /** @@ -221,6 +242,11 @@ SECTIONS mapping[dram0_data] + /* mutable libs begin */ + mutable[dram0_data] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _data_end = ABSOLUTE(.); } > dram0_0_seg @@ -250,6 +276,10 @@ SECTIONS */ mapping[dram0_bss] + /* mutable libs begin */ + mutable[dram0_bss] + /* mutable libs end */ + ALIGNED_SYMBOL(8, _bss_end) } > dram0_0_seg @@ -268,6 +298,11 @@ SECTIONS mapping[flash_text] + /* mutable libs begin */ + mutable[flash_text] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + *(.stub) *(.gnu.warning) *(.gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) @@ -391,6 +426,11 @@ SECTIONS KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); + /* mutable libs begin */ + mutable[flash_rodata] + FAST_REFLASHING_PADDING; + /* mutable libs end */ + _rodata_end = ABSOLUTE(.); /* Literals are also RO data. */ @@ -425,6 +465,10 @@ SECTIONS _rodata_reserved_end = ABSOLUTE(.); mapping[rodata_noload] + + /* mutable libs begin */ + mutable[rodata_noload] + /* mutable libs end */ } > default_rodata_seg /** @@ -446,6 +490,10 @@ SECTIONS mapping[extern_ram] + /* mutable libs begin */ + mutable[extern_ram] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _ext_ram_bss_end) } > extern_ram_seg #endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY @@ -484,6 +532,10 @@ SECTIONS mapping[iram0_data] + /* mutable libs begin */ + mutable[iram0_data] + /* mutable libs end */ + ALIGNED_SYMBOL(4, _iram_data_end) } > iram0_0_seg @@ -493,6 +545,10 @@ SECTIONS mapping[iram0_bss] + /* mutable libs begin */ + mutable[iram0_bss] + /* mutable libs end */ + _iram_bss_end = ABSOLUTE(.); ALIGNED_SYMBOL(4, _iram_end) } > iram0_0_seg diff --git a/components/esp_system/ld/ld.common b/components/esp_system/ld/ld.common index 04401fd24ba..58ac0fbb8da 100644 --- a/components/esp_system/ld/ld.common +++ b/components/esp_system/ld/ld.common @@ -94,3 +94,23 @@ ASSERT((ADDR(NEXT_SECTION) == ADDR(PREV_SECTION) + SIZEOF(PREV_SECTION)), \ #else #define SECTION_AFTER_FLASH_RODATA .flash.tdata #endif + +/* + * FAST_REFLASHING_PADDING + * + * Aligns the current location (.) to CONFIG_ESPTOOLPY_FAST_REFLASHING_PADDING. + * If more than half of the alignment block is already consumed, + * extra padding is applied to skip to the next boundary. + * For example, if the current location (.) is 0x1900 and the padding is 4 KB, + * the location will be moved to 0x3000 instead of 0x2000 because less than + * half of the current 4 KB block remains (0x2000 - 0x1900 < 2 KB). + */ +#if CONFIG_ESPTOOLPY_FAST_REFLASHING +#define FAST_REFLASHING_PADDING \ + . = ALIGN((. + ( \ + (CONFIG_ESPTOOLPY_FAST_REFLASHING_PADDING - (ALIGN(CONFIG_ESPTOOLPY_FAST_REFLASHING_PADDING) - .)) + \ + (CONFIG_ESPTOOLPY_FAST_REFLASHING_PADDING / 2) \ + )), CONFIG_ESPTOOLPY_FAST_REFLASHING_PADDING) +#else +#define FAST_REFLASHING_PADDING +#endif diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index bbaa954ff12..8e6c99d9085 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -202,4 +202,65 @@ menu "Serial flasher config" int default ESP_CONSOLE_UART_BAUDRATE if ESP_CONSOLE_UART default 115200 if !ESP_CONSOLE_UART + + menu "Fast Reflashing" + + config ESPTOOLPY_FAST_REFLASHING + bool "Enable fast reflashing (Experimental)" + depends on IDF_EXPERIMENTAL_FEATURES + select COMPILER_NO_MERGE_CONSTANTS if IDF_TOOLCHAIN_GCC + help + Enabling this option classifies component libraries into two + categories: mutable and immutable. Mutable libraries are + expected to change frequently during development, while + immutable libraries are considered stable. All project + component libraries are treated as mutable; all other libraries + are treated as immutable. + + In the generated linker script, input sections from immutable + libraries are placed before those from mutable libraries. This + layout helps localize changes in the output sections of the ELF + file, so that recompilation primarily affects a confined area + associated with the mutable libraries. + + This enables the generation of binary images with large + unmodified regions across recompilations, allowing to flash + only the parts that have changed. + + To support this, some compiler optimizations, such as constant + merging, are disabled, which helps minimize differences in the + binary image between builds. However, this may increase the + overall image size and flash usage. + + Additionally, this option inserts a padding, defined by the + ESPTOOLPY_FAST_REFLASHING_PADDING option, after the input + sections of mutable libraries. This allows further isolate + changes but also increases the size of the binary image and + flash consumption. + + choice ESPTOOLPY_FAST_REFLASHING_PADDING_SIZE + prompt "Padding size" + default ESPTOOLPY_FAST_REFLASHING_PADDING_4KB + depends on ESPTOOLPY_FAST_REFLASHING + help + Selects the amount of padding (in kilobytes) to add after input + sections of mutable libraries when fast reflashing is enabled. + + config ESPTOOLPY_FAST_REFLASHING_PADDING_2KB + bool "2 KB" + config ESPTOOLPY_FAST_REFLASHING_PADDING_4KB + bool "4 KB" + config ESPTOOLPY_FAST_REFLASHING_PADDING_8KB + bool "8 KB" + endchoice + + config ESPTOOLPY_FAST_REFLASHING_PADDING + int + depends on ESPTOOLPY_FAST_REFLASHING + default 2048 if ESPTOOLPY_FAST_REFLASHING_PADDING_2KB + default 4096 if ESPTOOLPY_FAST_REFLASHING_PADDING_4KB + default 8192 if ESPTOOLPY_FAST_REFLASHING_PADDING_8KB + + endmenu + endmenu diff --git a/tools/cmake/ldgen.cmake b/tools/cmake/ldgen.cmake index 2a354b7340d..0b0b5b6a929 100644 --- a/tools/cmake/ldgen.cmake +++ b/tools/cmake/ldgen.cmake @@ -88,6 +88,32 @@ function(__ldgen_get_lib_deps_of_target target out_list_var) set(${out_list_var} ${out_list} PARENT_SCOPE) endfunction() +# __ldgen_get_mutable_libs +# +# Helper function to get the list of library file name generator expressions +# for project components. These libraries are placed by ldgen into a separate +# location in the linker script, allowing the fast reflashing feature. +# +function(__ldgen_get_mutable_libs out_list_var) + set(mutable_libs) + idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS) + foreach(component_target ${build_component_targets}) + __component_get_property(component_source ${component_target} COMPONENT_SOURCE) + __component_get_property(component_lib ${component_target} COMPONENT_LIB) + __component_get_property(component_type ${component_target} COMPONENT_TYPE) + if("${component_type}" STREQUAL "CONFIG_ONLY") + # Configuration only component interface target without a library. + continue() + endif() + if(NOT "${component_source}" STREQUAL "project_components") + # Add only project components as mutable. + continue() + endif() + set(unstable_lib "$") + list(APPEND mutable_libs "${unstable_lib}") + endforeach() + set(${out_list_var} ${mutable_libs} PARENT_SCOPE) +endfunction() # __ldgen_create_target # @@ -177,6 +203,17 @@ function(__ldgen_create_target exe_target) message(STATUS "Mapping check enabled in ldgen") endif() + if(CONFIG_ESPTOOLPY_FAST_REFLASHING) + # Create a file containing a list of mutable libraries used by ldgen + # for fast reflashing. + set(mutable_libs_path "${build_dir}/ldgen_mutable_libraries") + __ldgen_get_mutable_libs(mutable_libs) + list(JOIN mutable_libs "\n" mutable_libs_str) + file(GENERATE OUTPUT "${mutable_libs_path}" + CONTENT "${mutable_libs_str}") + set(mutable_libs_option "--mutable-libraries-file" "${mutable_libs_path}") + endif() + add_custom_command( OUTPUT ${output} COMMAND ${python} "${idf_path}/tools/ldgen/ldgen.py" @@ -189,6 +226,7 @@ function(__ldgen_create_target exe_target) --libraries-file "${build_dir}/ldgen_libraries" --objdump "${CMAKE_OBJDUMP}" ${ldgen_check} + ${mutable_libs_option} DEPENDS ${template} ${ldgen_fragment_files} ${ldgen_deps} ${SDKCONFIG} VERBATIM ) diff --git a/tools/ldgen/ldgen.py b/tools/ldgen/ldgen.py index 681020fa36f..b07bfd09672 100755 --- a/tools/ldgen/ldgen.py +++ b/tools/ldgen/ldgen.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 # import argparse @@ -23,7 +23,7 @@ from pyparsing import ParseFatalException def _update_environment(args): - env = [(name, value) for (name,value) in (e.split('=',1) for e in args.env)] + env = [(name, value) for (name, value) in (e.split('=', 1) for e in args.env)] for name, value in env: value = ' '.join(value.split()) os.environ[name] = value @@ -34,76 +34,69 @@ def _update_environment(args): def main(): - argparser = argparse.ArgumentParser(description='ESP-IDF linker script generator') - argparser.add_argument( - '--input', '-i', - help='Linker template file', - type=argparse.FileType('r')) + argparser.add_argument('--input', '-i', help='Linker template file', type=argparse.FileType('r')) fragments_group = argparser.add_mutually_exclusive_group() fragments_group.add_argument( - '--fragments', '-f', - type=argparse.FileType('r'), - help='Input fragment files', - nargs='+' + '--fragments', '-f', type=argparse.FileType('r'), help='Input fragment files', nargs='+' ) fragments_group.add_argument( - '--fragments-list', - help='Input fragment files as a semicolon-separated list', - type=str + '--fragments-list', help='Input fragment files as a semicolon-separated list', type=str ) argparser.add_argument( - '--libraries-file', + '--libraries-file', type=argparse.FileType('r'), help='File that contains the list of libraries in the build' + ) + + argparser.add_argument( + '--mutable-libraries-file', type=argparse.FileType('r'), - help='File that contains the list of libraries in the build') + help='File that contains the list of mutable libraries in the build', + ) + + argparser.add_argument('--output', '-o', help='Output linker script', type=str) + + argparser.add_argument('--config', '-c', help='Project configuration') + + argparser.add_argument('--kconfig', '-k', help='IDF Kconfig file') argparser.add_argument( - '--output', '-o', - help='Output linker script', - type=str) - - argparser.add_argument( - '--config', '-c', - help='Project configuration') - - argparser.add_argument( - '--kconfig', '-k', - help='IDF Kconfig file') - - argparser.add_argument( - '--check-mapping', - help='Perform a check if a mapping (archive, obj, symbol) exists', - action='store_true' + '--check-mapping', help='Perform a check if a mapping (archive, obj, symbol) exists', action='store_true' ) argparser.add_argument( - '--check-mapping-exceptions', - help='Mappings exempted from check', - type=argparse.FileType('r') + '--check-mapping-exceptions', help='Mappings exempted from check', type=argparse.FileType('r') ) argparser.add_argument( - '--env', '-e', - action='append', default=[], - help='Environment to set when evaluating the config file', metavar='NAME=VAL') - - argparser.add_argument('--env-file', type=argparse.FileType('r'), - help='Optional file to load environment variables from. Contents ' - 'should be a JSON object where each key/value pair is a variable.') + '--env', + '-e', + action='append', + default=[], + help='Environment to set when evaluating the config file', + metavar='NAME=VAL', + ) argparser.add_argument( - '--objdump', - help='Path to toolchain objdump') + '--env-file', + type=argparse.FileType('r'), + help='Optional file to load environment variables from. Contents ' + 'should be a JSON object where each key/value pair is a variable.', + ) + + argparser.add_argument('--objdump', help='Path to toolchain objdump') + + argparser.add_argument('--debug', '-d', help='Print debugging information.', action='store_true') args = argparser.parse_args() input_file = args.input libraries_file = args.libraries_file + mutable_libraries_file = args.mutable_libraries_file or [] config_file = args.config output_path = args.output kconfig_file = args.kconfig @@ -132,7 +125,8 @@ def main(): dump.name = library sections_infos.add_sections_info(dump) - generation_model = Generation(check_mapping, check_mapping_exceptions) + mutable_libs = [lib.strip() for lib in mutable_libraries_file] + generation_model = Generation(check_mapping, check_mapping_exceptions, mutable_libs, args.debug) _update_environment(args) # assign args.env and args.env_file to os.environ @@ -145,7 +139,7 @@ def main(): # ParseException is raised on incorrect grammar # ParseFatalException is raised on correct grammar, but inconsistent contents (ex. duplicate # keys, key unsupported by fragment, unexpected number of values, etc.) - raise LdGenFailure('failed to parse %s\n%s' % (fragment_file, str(e))) + raise LdGenFailure(f'failed to parse {fragment_file}\n{e}') generation_model.add_fragments_from_file(fragment_file) non_contiguous_sram = sdkconfig.evaluate_expression('SOC_MEM_NON_CONTIGUOUS_SRAM') @@ -165,10 +159,12 @@ def main(): if exc.errno != errno.EEXIST: raise - with open(output_path, 'w', encoding='utf-8') as f: # only create output file after generation has succeeded + with open( + output_path, 'w', encoding='utf-8' + ) as f: # only create output file after generation has succeeded f.write(output.read()) except LdGenFailure as e: - print('linker script generation failed for %s\nERROR: %s' % (input_file.name, e)) + print(f'linker script generation failed for {input_file.name}\nERROR: {e}') sys.exit(1) diff --git a/tools/ldgen/ldgen/generation.py b/tools/ldgen/ldgen/generation.py index 945884981f4..6d6a5ad5da1 100644 --- a/tools/ldgen/ldgen/generation.py +++ b/tools/ldgen/ldgen/generation.py @@ -41,7 +41,7 @@ class Placement: for details). """ - def __init__(self, node, sections, target, flags, explicit, force=False, dryrun=False): + def __init__(self, node, sections, target, flags, explicit, force=False, dryrun=False, mutable=False): self.node = node self.sections = sections self.target = target @@ -57,6 +57,10 @@ class Placement: # fragment entry. self.explicit = explicit + # This placement is designated as mutable and should be assigned to a + # mutable target. + self.mutable = mutable + # Find basis placement. parent = node.parent candidate = None @@ -83,11 +87,19 @@ class Placement: # # Placement can also be a basis if it has flags # (self.flags) or its basis has flags (self.basis.flags) - significant = (not self.basis or - self.target != self.basis.target or - (self.flags and not self.basis.flags) or - (not self.flags and self.basis.flags) or - self.force) + # + # The mutable placement mark is used to place the placement within a + # designated area (MutableMarker) in the linker script. If the basis is + # also mutable, the placement is deemed insignificant to prevent adding + # unnecessary EXCLUDE_FILE and placement rules to the linker script. + significant = ( + not self.basis + or self.target != self.basis.target + or (self.flags and not self.basis.flags) + or (not self.flags and self.basis.flags) + or (self.mutable and not self.basis.mutable) + or self.force + ) if significant and not self.explicit and not self.sections: # The placement is significant, but it is an intermediate placement @@ -112,6 +124,15 @@ class Placement: def add_subplacement(self, subplacement): self.subplacements.add(subplacement) + def __str__(self): + sections_str = ', '.join(self.sections) + basis_str = self.basis.node.name if self.basis else 'NONE' + return ( + f'({sections_str} -> {self.target}), basis: {basis_str}, ' + f'force: {self.force}, stable: {self.stable}, ' + f'explicit: {self.explicit}, flags: {self.flags}' + ) + class EntityNode: """ @@ -155,7 +176,7 @@ class EntityNode: assert name and name != Entity.ALL child = [c for c in self.children if c.name == name] - assert (len(child) <= 1) + assert len(child) <= 1 if not child: child = self.child_t(self, name) @@ -169,7 +190,7 @@ class EntityNode: commands = collections.defaultdict(list) def process_commands(cmds): - for (target, commands_list) in cmds.items(): + for target, commands_list in cmds.items(): commands[target].extend(commands_list) # Process the commands generated from this node @@ -196,6 +217,7 @@ class EntityNode: keep = False sort = None surround_type = [] + mutable = placement.mutable placement_flags = placement.flags if placement.flags is not None else [] @@ -212,52 +234,70 @@ class EntityNode: for flag in surround_type: if flag.pre: if isinstance(flag, Surround): - commands[placement.target].append(SymbolAtAddress(f'_{flag.symbol}_start', tied)) + commands[placement.target].append( + SymbolAtAddress(f'_{flag.symbol}_start', tied, mutable=mutable) + ) else: # ALIGN - commands[placement.target].append(AlignAtAddress(flag.alignment, tied)) + commands[placement.target].append(AlignAtAddress(flag.alignment, tied, mutable=mutable)) # This is for expanded object node and symbol node placements without checking for # the type. placement_sections = frozenset(placement.sections) command_sections = sections if sections == placement_sections else placement_sections - command = InputSectionDesc(placement.node.entity, command_sections, - [e.node.entity for e in placement.exclusions], keep, sort, tied) + command = InputSectionDesc( + placement.node.entity, + command_sections, + [e.node.entity for e in placement.exclusions], + keep, + sort, + tied, + mutable=mutable, + ) commands[placement.target].append(command) # Generate commands for intermediate, non-explicit exclusion placements here, # so that they can be enclosed by flags that affect the parent placement. for subplacement in placement.subplacements: if not subplacement.flags and not subplacement.explicit: - command = InputSectionDesc(subplacement.node.entity, subplacement.sections, - [e.node.entity for e in subplacement.exclusions], keep, sort, tied) + command = InputSectionDesc( + subplacement.node.entity, + subplacement.sections, + [e.node.entity for e in subplacement.exclusions], + keep, + sort, + tied, + mutable=mutable, + ) commands[placement.target].append(command) for flag in surround_type: if flag.post: if isinstance(flag, Surround): - commands[placement.target].append(SymbolAtAddress(f'_{flag.symbol}_end', tied)) + commands[placement.target].append( + SymbolAtAddress(f'_{flag.symbol}_end', tied, mutable=mutable) + ) else: # ALIGN - commands[placement.target].append(AlignAtAddress(flag.alignment, tied)) + commands[placement.target].append(AlignAtAddress(flag.alignment, tied, mutable=mutable)) return commands - def self_placement(self, sections, target, flags, explicit=True, force=False): - placement = Placement(self, sections, target, flags, explicit, force) + def self_placement(self, sections, target, flags, explicit=True, force=False, mutable=False): + placement = Placement(self, sections, target, flags, explicit, force=force, mutable=mutable) self.placements[sections] = placement return placement - def child_placement(self, entity, sections, target, flags, sections_db): + def child_placement(self, entity, sections, target, flags, mutable, sections_db): child = self.add_child(entity) - child.insert(entity, sections, target, flags, sections_db) + child.insert(entity, sections, target, flags, mutable, sections_db) - def insert(self, entity, sections, target, flags, sections_db): + def insert(self, entity, sections, target, flags, mutable, sections_db): if self.entity.specificity == entity.specificity: # Since specificities match, create the placement in this node. - self.self_placement(sections, target, flags) + self.self_placement(sections, target, flags, mutable=mutable) else: # If not, create a child node and try to create the placement there. - self.child_placement(entity, sections, target, flags, sections_db) + self.child_placement(entity, sections, target, flags, mutable, sections_db) def get_output_sections(self): return sorted(self.placements.keys(), key=lambda x: sorted(x)) # pylint: disable=W0108 @@ -298,9 +338,9 @@ class ObjectNode(EntityNode): self.entity = Entity(self.parent.name, self.name) self.subplacements = list() - def child_placement(self, entity, sections, target, flags, sections_db): + def child_placement(self, entity, sections, target, flags, mutable, sections_db): child = self.add_child(entity) - sym_placement = Placement(child, sections, target, flags, True, dryrun=True) + sym_placement = Placement(child, sections, target, flags, True, dryrun=True, mutable=mutable) # The basis placement for sym_placement can either be # an existing placement on this node, or nonexistent. @@ -319,7 +359,7 @@ class ObjectNode(EntityNode): if obj_sections: symbol = entity.symbol - remove_sections = [s.replace('.*', '.%s' % symbol) for s in sections if '.*' in s] + remove_sections = [s.replace('.*', f'.{symbol}') for s in sections if '.*' in s] filtered_sections = [s for s in obj_sections if s not in remove_sections] if set(filtered_sections) != set(obj_sections): @@ -331,7 +371,9 @@ class ObjectNode(EntityNode): obj_placement = self.placements[sections] except KeyError: # Create intermediate placement. - obj_placement = self.self_placement(sections, sym_placement.basis.target, None, False) + obj_placement = self.self_placement( + sections, sym_placement.basis.target, None, False, mutable=mutable + ) if obj_placement.basis.flags: subplace = True @@ -383,12 +425,13 @@ class Generation: """ # Processed mapping, scheme and section entries - EntityMapping = namedtuple('EntityMapping', 'entity sections_group target flags') + EntityMapping = namedtuple('EntityMapping', 'entity sections_group target flags mutable') - def __init__(self, check_mappings=False, check_mapping_exceptions=None): + def __init__(self, check_mappings=False, check_mapping_exceptions=None, mutable_libs=None, debug=False): self.schemes = {} self.placements = {} self.mappings = {} + self.debug = debug self.check_mappings = check_mappings @@ -397,6 +440,11 @@ class Generation: else: self.check_mapping_exceptions = [] + if mutable_libs is None: + self.mutable_libs = [] + else: + self.mutable_libs = mutable_libs + def _prepare_scheme_dictionary(self): scheme_dictionary = collections.defaultdict(dict) @@ -404,7 +452,7 @@ class Generation: for scheme in self.schemes.values(): sections_bucket = collections.defaultdict(list) - for (sections_name, target_name) in scheme.entries: + for sections_name, target_name in scheme.entries: # Get the sections under the bucket 'target_name'. If this bucket does not exist # is created automatically sections_in_bucket = sections_bucket[target_name] @@ -420,7 +468,7 @@ class Generation: scheme_dictionary[scheme.name] = sections_bucket # Search for and raise exception on first instance of sections mapped to multiple targets - for (scheme_name, sections_bucket) in scheme_dictionary.items(): + for scheme_name, sections_bucket in scheme_dictionary.items(): for sections_a, sections_b in itertools.combinations(sections_bucket.values(), 2): set_a = set() set_b = set() @@ -442,26 +490,59 @@ class Generation: return scheme_dictionary + def _get_section_strs(self, section): + s_list = [Sections.get_section_data_from_entry(s) for s in section.entries] + return frozenset([item for sublist in s_list for item in sublist]) + + def _prepare_mutable_entity_mappings(self, entity_mappings, scheme_dictionary, entities): + # Go through all mutable libraries, if any, and add entity mapping for + # each "default" section set for which the entity mapping does not yet + # exist. This adds mapping for every mutable library as if specified in + # the mapping fragment. All newly added or existing entity mappings + # related to mutable libraries are marked as mutable. This flag is used + # when the placement for it is created in the entity tree, ensuring the + # placement is forced to be emitted in the linker script and not + # considered non-significant. + + for archive in self.mutable_libs: + entity = Entity(archive, '*') + for target, sections in scheme_dictionary['default'].items(): + for section in sections: + sections_str = self._get_section_strs(section) + key = (entity, sections_str) + if entity_mappings.get(key): + # Mutable library already has entity mapping. + continue + + entity_mappings[key] = Generation.EntityMapping(entity, sections_str, target, [], True) + + for key, mapping in entity_mappings.items(): + (entity, sections, target, flags, mutable) = mapping + if entity.archive not in self.mutable_libs or mutable: + # The entity either does not belong to a mutable library or is + # an archive entity that has already been marked as mutable. + continue + # Set entity as mutable. + entity_mappings[key] = Generation.EntityMapping(entity, sections, target, flags, True) + def _prepare_entity_mappings(self, scheme_dictionary, entities): # Prepare entity mappings processed from mapping fragment entries. - def get_section_strs(section): - s_list = [Sections.get_section_data_from_entry(s) for s in section.entries] - return frozenset([item for sublist in s_list for item in sublist]) - entity_mappings = dict() for mapping in self.mappings.values(): archive = mapping.archive - for (obj, symbol, scheme_name) in mapping.entries: + for obj, symbol, scheme_name in mapping.entries: entity = Entity(archive, obj, symbol) # Check the entity exists - if (self.check_mappings - and entity.specificity.value > Entity.Specificity.ARCHIVE.value - and mapping.name not in self.check_mapping_exceptions): + if ( + self.check_mappings + and entity.specificity.value > Entity.Specificity.ARCHIVE.value + and mapping.name not in self.check_mapping_exceptions + ): if not entities.check_exists(entity): - message = "'%s' not found" % str(entity) + message = f"'{entity}' not found" raise GenerationException(message, mapping) if (obj, symbol, scheme_name) in mapping.flags.keys(): @@ -469,16 +550,16 @@ class Generation: # Check if all section->target defined in the current # scheme. for flag in flags: - if (flag.target not in scheme_dictionary[scheme_name].keys() - or flag.section not in - [_s.name for _s in scheme_dictionary[scheme_name][flag.target]]): - message = "%s->%s not defined in scheme '%s'" % (flag.section, flag.target, scheme_name) + if flag.target not in scheme_dictionary[scheme_name].keys() or flag.section not in [ + _s.name for _s in scheme_dictionary[scheme_name][flag.target] + ]: + message = f"{flag.section}->{flag.target} not defined in scheme '{scheme_name}'" raise GenerationException(message, mapping) else: flags = None # Create placement for each 'section -> target' in the scheme. - for (target, sections) in scheme_dictionary[scheme_name].items(): + for target, sections in scheme_dictionary[scheme_name].items(): for section in sections: # Find the applicable flags _flags = [] @@ -488,7 +569,7 @@ class Generation: if (flag.section, flag.target) == (section.name, target): _flags.extend(flag.flags) - sections_str = get_section_strs(section) + sections_str = self._get_section_strs(section) key = (entity, sections_str) @@ -498,7 +579,7 @@ class Generation: existing = None if not existing: - entity_mappings[key] = Generation.EntityMapping(entity, sections_str, target, _flags) + entity_mappings[key] = Generation.EntityMapping(entity, sections_str, target, _flags, False) else: # Check for conflicts. if target != existing.target: @@ -509,31 +590,46 @@ class Generation: if _flags or existing.flags: if (_flags and not existing.flags) or (not _flags and existing.flags): _flags.extend(existing.flags) - entity_mappings[key] = Generation.EntityMapping(entity, - sections_str, - target, _flags) + entity_mappings[key] = Generation.EntityMapping( + entity, sections_str, target, _flags, False + ) elif _flags == existing.flags: pass else: raise GenerationException('Conflicting flags specified.', mapping) + # Add new mappings for mutable libraries and modify existing ones if + # they pertain to them. + self._prepare_mutable_entity_mappings(entity_mappings, scheme_dictionary, entities) + # Sort the mappings by specificity, so as to simplify # insertion logic. res = list(entity_mappings.values()) res.sort(key=lambda m: m.entity) return res + def _dump_entity_tree(self, node, indent=''): + print(f'{indent}-> {node.name}') + for placement in node.placements.values(): + print(f'{indent} {placement}') + print() + for child in node.children: + self._dump_entity_tree(child, ' ' * 8 + indent) + def generate(self, entities, non_contiguous_sram): scheme_dictionary = self._prepare_scheme_dictionary() entity_mappings = self._prepare_entity_mappings(scheme_dictionary, entities) root_node = RootNode() for mapping in entity_mappings: - (entity, sections, target, flags) = mapping + (entity, sections, target, flags, mutable) = mapping try: - root_node.insert(entity, sections, target, flags, entities) + root_node.insert(entity, sections, target, flags, mutable, entities) except ValueError as e: raise GenerationException(str(e)) + if self.debug: + self._dump_entity_tree(root_node) + # Traverse the tree, creating the placements commands = root_node.get_output_commands(non_contiguous_sram) @@ -552,8 +648,7 @@ class Generation: if fragment.name in dict_to_append_to: stored = dict_to_append_to[fragment.name].path new = fragment.path - message = "Duplicate definition of fragment '%s' found in %s and %s." % ( - fragment.name, stored, new) + message = f"Duplicate definition of fragment '{fragment.name}' found in {stored} and {new}." raise GenerationException(message) dict_to_append_to[fragment.name] = fragment @@ -573,6 +668,6 @@ class GenerationException(LdGenFailure): def __str__(self): if self.fragment: - return "%s\nIn fragment '%s' defined in '%s'." % (self.message, self.fragment.name, self.fragment.path) + return f"{self.message}\nIn fragment '{self.fragment.name}' defined in '{self.fragment.path}'." else: return self.message diff --git a/tools/ldgen/ldgen/linker_script.py b/tools/ldgen/ldgen/linker_script.py index c57188f4181..571d925b669 100644 --- a/tools/ldgen/ldgen/linker_script.py +++ b/tools/ldgen/ldgen/linker_script.py @@ -24,6 +24,7 @@ class LinkerScript: MappingMarker = collections.namedtuple('MappingMarker', 'target indent rules') ArraysMarker = collections.namedtuple('ArraysMarker', 'target indent rules') + MutableMarker = collections.namedtuple('MutableMarker', 'target indent rules') def __init__(self, template_file): self.members = [] @@ -37,16 +38,19 @@ class LinkerScript: target = Fragment.IDENTIFIER pattern_mapping = White(' \t') + Suppress('mapping') + Suppress('[') + target + Suppress(']') pattern_arrays = White(' \t') + Suppress('arrays') + Suppress('[') + target + Suppress(']') + pattern_mutable = White(' \t') + Suppress('mutable') + Suppress('[') + target + Suppress(']') # Find the markers in the template file line by line. If line does not match marker grammar, # set it as a literal to be copied as is to the output file. for line in lines: parsed = False - for pattern in (pattern_arrays, pattern_mapping): + for pattern in (pattern_arrays, pattern_mapping, pattern_mutable): try: indent, target = pattern.parse_string(line) if pattern is pattern_arrays: marker = LinkerScript.ArraysMarker(target, indent, []) + elif pattern is pattern_mutable: + marker = LinkerScript.MutableMarker(target, indent, []) else: marker = LinkerScript.MappingMarker(target, indent, []) self.members.append(marker) @@ -65,8 +69,10 @@ class LinkerScript: if isinstance(member, self.ArraysMarker): rules = [x for x in mapping_rules[target] if x.tied] + elif isinstance(member, self.MutableMarker): + rules = [x for x in mapping_rules[target] if x.mutable and not x.tied] else: - rules = [x for x in mapping_rules[target] if not x.tied] + rules = [x for x in mapping_rules[target] if not x.tied and not x.mutable] member.rules.extend(rules) except KeyError: message = GenerationException.UNDEFINED_REFERENCE + " to target '" + target + "'." @@ -78,7 +84,7 @@ class LinkerScript: # Add information that this is a generated file. output_file.write('/* Automatically generated file; DO NOT EDIT */\n') output_file.write('/* Espressif IoT Development Framework Linker Script */\n') - output_file.write('/* Generated from: %s */\n' % self.file) + output_file.write(f'/* Generated from: {self.file} */\n') output_file.write('\n') # Do the text replacement diff --git a/tools/ldgen/ldgen/output_commands.py b/tools/ldgen/ldgen/output_commands.py index a5c58b7a8cf..a8332a9432b 100644 --- a/tools/ldgen/ldgen/output_commands.py +++ b/tools/ldgen/ldgen/output_commands.py @@ -1,5 +1,5 @@ # -# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 # from .entity import Entity @@ -19,16 +19,16 @@ class AlignAtAddress: command to be emitted. """ - def __init__(self, alignment, tied=False): + def __init__(self, alignment, tied=False, mutable=False): self.alignment = alignment self.tied = tied + self.mutable = mutable def __str__(self): - return ('. = ALIGN(%d);' % self.alignment) + return f'. = ALIGN({self.alignment});' def __eq__(self, other): - return (isinstance(other, AlignAtAddress) and - self.alignment == other.alignment) + return isinstance(other, AlignAtAddress) and self.alignment == other.alignment class SymbolAtAddress: @@ -43,16 +43,16 @@ class SymbolAtAddress: an InputSectionDesc. """ - def __init__(self, symbol, tied=False): + def __init__(self, symbol, tied=False, mutable=False): self.symbol = symbol self.tied = tied + self.mutable = mutable def __str__(self): - return ('%s = ABSOLUTE(.);' % self.symbol) + return f'{self.symbol} = ABSOLUTE(.);' def __eq__(self, other): - return (isinstance(other, SymbolAtAddress) and - self.symbol == other.symbol) + return isinstance(other, SymbolAtAddress) and self.symbol == other.symbol class InputSectionDesc: @@ -65,7 +65,7 @@ class InputSectionDesc: the emitted input section description. """ - def __init__(self, entity, sections, exclusions=None, keep=False, sort=None, tied=False): + def __init__(self, entity, sections, exclusions=None, keep=False, sort=None, tied=False, mutable=False): assert entity.specificity != Entity.Specificity.SYMBOL self.entity = entity @@ -74,8 +74,11 @@ class InputSectionDesc: self.exclusions = set() if exclusions: - assert not [e for e in exclusions if e.specificity == Entity.Specificity.SYMBOL or - e.specificity == Entity.Specificity.NONE] + assert not [ + e + for e in exclusions + if e.specificity == Entity.Specificity.SYMBOL or e.specificity == Entity.Specificity.NONE + ] self.exclusions = set(exclusions) else: self.exclusions = set() @@ -83,6 +86,7 @@ class InputSectionDesc: self.keep = keep self.sort = sort self.tied = tied + self.mutable = mutable def __str__(self): sections_string = '' @@ -93,65 +97,67 @@ class InputSectionDesc: for exc in sorted(self.exclusions): if exc.specificity == Entity.Specificity.ARCHIVE: - exc_string = '*%s' % (exc.archive) + exc_string = f'*{exc.archive}' else: - exc_string = '*%s:%s.*' % (exc.archive, exc.obj) + exc_string = f'*{exc.archive}:{exc.obj}.*' exclusion_strings.append(exc_string) section_strings = [] if exclusion_strings: - exclusion_string = 'EXCLUDE_FILE(%s)' % ' '.join(exclusion_strings) + exclusion_string = 'EXCLUDE_FILE({})'.format(' '.join(exclusion_strings)) for section in sorted(self.sections): - section_strings.append('%s %s' % (exclusion_string, section)) + section_strings.append(f'{exclusion_string} {section}') else: for section in sorted(self.sections): section_strings.append(section) if self.sort: if self.sort == (None, None): - pattern = 'SORT(%s)' + pattern = 'SORT({})' elif self.sort == ('name', None): - pattern = 'SORT_BY_NAME(%s)' + pattern = 'SORT_BY_NAME({})' elif self.sort == ('alignment', None): - pattern = 'SORT_BY_ALIGNMENT(%s)' + pattern = 'SORT_BY_ALIGNMENT({})' elif self.sort == ('init_priority', None): - pattern = 'SORT_BY_INIT_PRIORITY(%s)' + pattern = 'SORT_BY_INIT_PRIORITY({})' elif self.sort == ('name', 'alignment'): - pattern = 'SORT_BY_NAME(SORT_BY_ALIGNMENT(%s))' + pattern = 'SORT_BY_NAME(SORT_BY_ALIGNMENT({}))' elif self.sort == ('alignment', 'name'): - pattern = 'SORT_BY_ALIGNMENT(SORT_BY_NAME(%s))' + pattern = 'SORT_BY_ALIGNMENT(SORT_BY_NAME({}))' elif self.sort == ('name', 'name'): - pattern = 'SORT_BY_NAME(SORT_BY_NAME(%s))' + pattern = 'SORT_BY_NAME(SORT_BY_NAME({}))' elif self.sort == ('alignment', 'alignment'): - pattern = 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(%s))' + pattern = 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT({}))' else: raise Exception('Invalid sort arguments') - section_strings = [(pattern % s) for s in section_strings] + section_strings = [pattern.format(s) for s in section_strings] - sections_string = '(%s)' % ' '.join(section_strings) + sections_string = '({})'.format(' '.join(section_strings)) if self.entity.specificity == Entity.Specificity.NONE: - entry = '*%s' % (sections_string) + entry = f'*{sections_string}' elif self.entity.specificity == Entity.Specificity.ARCHIVE: - entry = '*%s:%s' % (self.entity.archive, sections_string) + entry = f'*{self.entity.archive}:{sections_string}' else: - entry = '*%s:%s.*%s' % (self.entity.archive, self.entity.obj, sections_string) + entry = f'*{self.entity.archive}:{self.entity.obj}.*{sections_string}' if self.keep: - res = 'KEEP(%s)' % entry + res = f'KEEP({entry})' else: res = entry return res def __eq__(self, other): - return (isinstance(other, InputSectionDesc) and - self.entity == other.entity and - self.sections == other.sections and - self.exclusions == other.exclusions and - self.keep == other.keep and - self.sort == other.sort) + return ( + isinstance(other, InputSectionDesc) + and self.entity == other.entity + and self.sections == other.sections + and self.exclusions == other.exclusions + and self.keep == other.keep + and self.sort == other.sort + ) diff --git a/tools/ldgen/test/test_generation.py b/tools/ldgen/test/test_generation.py index 209621d83de..a14ffc90310 100755 --- a/tools/ldgen/test/test_generation.py +++ b/tools/ldgen/test/test_generation.py @@ -12,19 +12,27 @@ import unittest from io import StringIO try: - from ldgen.entity import Entity, EntityDB + from ldgen.entity import Entity + from ldgen.entity import EntityDB from ldgen.fragments import parse_fragment_file - from ldgen.generation import Generation, GenerationException + from ldgen.generation import Generation + from ldgen.generation import GenerationException from ldgen.linker_script import LinkerScript - from ldgen.output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress + from ldgen.output_commands import AlignAtAddress + from ldgen.output_commands import InputSectionDesc + from ldgen.output_commands import SymbolAtAddress from ldgen.sdkconfig import SDKConfig except ImportError: sys.path.append(os.path.dirname(os.path.dirname(__file__))) - from ldgen.entity import Entity, EntityDB + from ldgen.entity import Entity + from ldgen.entity import EntityDB from ldgen.fragments import parse_fragment_file - from ldgen.generation import Generation, GenerationException + from ldgen.generation import Generation + from ldgen.generation import GenerationException from ldgen.linker_script import LinkerScript - from ldgen.output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress + from ldgen.output_commands import AlignAtAddress + from ldgen.output_commands import InputSectionDesc + from ldgen.output_commands import SymbolAtAddress from ldgen.sdkconfig import SDKConfig ROOT = Entity('*') @@ -38,7 +46,6 @@ FREERTOS2 = Entity('libfreertos2.a') class GenerationTest(unittest.TestCase): - def setUp(self): self.generation = Generation() self.entities = None @@ -121,7 +128,6 @@ class GenerationTest(unittest.TestCase): class DefaultMappingTest(GenerationTest): - def test_rule_generation_default(self): # Checks that default rules are generated from # the default scheme properly and even if no mappings @@ -134,7 +140,7 @@ class DefaultMappingTest(GenerationTest): def test_default_mapping_lib(self): # Mapping a library with default mapping. This should not emit additional rules, # other than the default ones. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -146,7 +152,7 @@ entries: def test_default_mapping_obj(self): # Mapping an object with default mapping. This should not emit additional rules, # other than the default ones. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -158,7 +164,7 @@ entries: def test_default_mapping_symbol(self): # Mapping a symbol with default mapping. This should not emit additional rules, # other than the default ones. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -170,7 +176,7 @@ entries: def test_default_mapping_all(self): # Mapping a library, object, and symbol with default mapping. This should not emit additional rules, # other than the default ones. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -187,7 +193,7 @@ entries: # # This is a check needed to make sure generation does not generate # intermediate commands due to presence of symbol mapping. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -203,7 +209,7 @@ entries: # # This is a check needed to make sure generation does not generate # intermediate commands due to presence of symbol mapping. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -214,6 +220,90 @@ entries: self.test_rule_generation_default() +class MutableMappingTest(GenerationTest): + # Collection of tests for mutable library mappings + + def dump_rules(self, rules): + # A simple helper for displaying the rules. It can be used to manually + # compare the actual and expected rules to identify any issues while + # preparing tests. + print() + for target, section_descs in rules.items(): + print(f'target: {target}') + for section_desc in section_descs: + print(f' {section_desc.entity}: {section_desc.sections}') + for exclusion in section_desc.exclusions: + print(f' {exclusion}') + + def add_mutable_libs_to_rules(self, libs, default_rules): + # Insert mutable lib rules and exclusions into default test rules. + rules = collections.defaultdict(list) + for target, section_descs in default_rules.items(): + for section_desc in section_descs: + # Duplicate the current section description. + section_desc_copy = InputSectionDesc(section_desc.entity, section_desc.sections) + for lib in libs: + # Exclude each mutable library from ROOT entity rule. + section_desc_copy.exclusions.add(Entity(lib)) + rules[target].append(section_desc_copy) + + # Add a rule for each mutable library and each ROOT entity rule section. + # This is done separately because the order is important. + for lib in libs: + for section_desc in section_descs: + rules[target].append(InputSectionDesc(Entity(lib), section_desc.sections)) + return rules + + def test_rule_generation_default(self): + # Verifies that the default rules are correctly generated from the + # default scheme and that mutable libraries are included in the + # generated rules. + mutable_libs = ['libmutable.a', 'libmutable2.a'] + + self.generation.mutable_libs = mutable_libs + + actual = self.generation.generate(self.entities, False) + default_rules = self.generate_default_rules() + expected = self.add_mutable_libs_to_rules(mutable_libs, default_rules) + + self.compare_rules(expected, actual) + self.generation.mutable_libs = [] + + def test_default_mapping_obj(self): + # Verifies that libfreertos.a:croutine entity is correctly excluded + # from the libfreertos.a entity and moved to iram and dram. + mapping = """ +[mapping:test] +archive: libfreertos.a +entries: + croutine (noflash) +""" + mutable_libs = ['libfreertos.a'] + + self.generation.mutable_libs = mutable_libs + + self.add_fragments(mapping) + actual = self.generation.generate(self.entities, False) + default_rules = self.generate_default_rules() + expected = self.add_mutable_libs_to_rules(mutable_libs, default_rules) + + flash_text = expected['flash_text'] + flash_rodata = expected['flash_rodata'] + iram0_text = expected['iram0_text'] + dram0_data = expected['dram0_data'] + + # Add exclusions for the libfreertos.a:croutine entity into + # libfreertos.a entity in test and rodata. + flash_text[1].exclusions.add(CROUTINE) + flash_rodata[1].exclusions.add(CROUTINE) + + # Add rules for the libfreertos.a:croutine entity in iram and dram. + iram0_text.append(InputSectionDesc(CROUTINE, flash_text[1].sections)) + dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[1].sections)) + + self.compare_rules(expected, actual) + + class BasicTest(GenerationTest): # Test basic and fundamental interactions between typical # entries. @@ -230,7 +320,7 @@ class BasicTest(GenerationTest): # iram0_text # *(.iram ...) # *libfreertos.a(.literal ...) B - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -260,14 +350,14 @@ entries: # There should be exclusions in the default commands for flash_text and flash_rodata: # # flash_text - # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A + # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A # # Commands placing the entire library in iram, dram should be generated: # # iram0_text # *(.iram ...) - # *libfreertos.a:croutine(.literal ...) B - mapping = u""" + # *libfreertos.a:croutine(.literal ...) B + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -295,19 +385,20 @@ entries: def test_nondefault_mapping_symbol(self): # Test mapping entry different from default for symbol. - # There should be exclusions in the default commands for flash_text, as well as the implicit intermediate object command + # There should be exclusions in the default commands for flash_text, as + # well as the implicit intermediate object command # with an exclusion from default: # # flash_text - # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A - # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) B + # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A + # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) B # # Commands placing the entire library in iram should be generated: # # iram0_text # *(.iram ...) - # *libfreertos.a:croutine(.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) C - mapping = u""" + # *libfreertos.a:croutine(.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) C + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -336,7 +427,9 @@ entries: flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) # Input section commands in iram_text for #1 C - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -351,14 +444,14 @@ entries: # object file, including .debug, .comment and other input section. # # flash.rodata - # *((EXCLUDE_FILE(*libsoc.a:temperature_sensor_periph.*)) .rodata.* ...) A - # # *libsoc.a:temperature_sensor_periph.* X + # *((EXCLUDE_FILE(*libsoc.a:temperature_sensor_periph.*)) .rodata.* ...) A + # # *libsoc.a:temperature_sensor_periph.* X # # Commands placing the entire library in iram should be generated: # # dram0_data - # *libsoc.a:temperature_sensor_periph.*(.rodata.temperature_sensor_attribute) B - mapping = u""" + # *libsoc.a:temperature_sensor_periph.*(.rodata.temperature_sensor_attribute) B + mapping = """ [mapping:test] archive: libsoc.a entries: @@ -376,14 +469,15 @@ entries: flash_rodata[0].exclusions.add(TEMPERATURE_SENSOR_PERIPH) # Input section commands in dram0_data for #1 B - dram0_data.append(InputSectionDesc(TEMPERATURE_SENSOR_PERIPH, - set(['.rodata.temperature_sensor_attributes']), - [])) + dram0_data.append( + InputSectionDesc(TEMPERATURE_SENSOR_PERIPH, set(['.rodata.temperature_sensor_attributes']), []) + ) self.compare_rules(expected, actual) def test_default_symbol_nondefault_lib(self): - # Test default symbol mapping with different lib mapping. This should create an implicit intermediate object command. + # Test default symbol mapping with different lib mapping. This should + # create an implicit intermediate object command. # The significant targets are flash_text, flash_rodata, iram0_text, dram0_data. # # flash_text @@ -403,7 +497,7 @@ entries: # libfreertos.a ( .rodata ...) C.2 # # Only default commands are in the other targets. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -440,7 +534,9 @@ entries: iram0_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) # Command for #2 B - flash_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + flash_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -465,7 +561,7 @@ entries: # *libfreertos.a:croutine(.rodata ....) C.2 # # Only default commands are in the other targets - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -499,7 +595,9 @@ entries: dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) # Command for #2 B - flash_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + flash_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -532,7 +630,7 @@ entries: # libfreertos.a (EXCLUDE_FILE(libfreertos:croutine) .rodata ...) C # # For the other targets only the default commands should be present. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -575,14 +673,16 @@ entries: flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) # Command for #3 D - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) def test_nondefault_but_same_lib_and_obj(self): # Extension of DefaultMappingTest. Commands should not be generated for #2, since it does similar mapping # to #1. Output is similar to test_different_mapping_lib. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -594,7 +694,7 @@ entries: def test_nondefault_but_same_lib_and_sym(self): # Extension of DefaultMappingTest. Commands should not be generated for #2, since it does similar mapping # to #1. Output is similar to test_different_mapping_lib. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -606,7 +706,7 @@ entries: def test_nondefault_but_same_obj_and_sym(self): # Commands should not be generated for #2, since it does similar mapping # to #1. Output is similar to test_different_mapping_obj. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -627,7 +727,7 @@ entries: # iram0_text # # - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -659,8 +759,12 @@ entries: flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) # Commands for #1 & 2 - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), [])) - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), []) + ) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -671,7 +775,7 @@ entries: # iram0_text # * (.custom_section) A # * (.iram .iram.*) - mapping = u""" + mapping = """ [sections:custom_section] entries: .custom_section @@ -700,7 +804,6 @@ entries: class AdvancedTest(GenerationTest): - # Test valid but quirky cases, corner cases, failure cases, and # cases involving interaction between schemes, other mapping # fragments. @@ -727,7 +830,7 @@ class AdvancedTest(GenerationTest): # *(.data ..) # *(.dram ...) # *libfreertos.a:croutine(.rodata .rodata.*) D - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -768,20 +871,20 @@ entries: # This operation should succeed with the following commands: # # flash_text - # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A + # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A # # flash_rodata - # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) B + # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) B # # iram0_text # *(.iram ...) - # *libfreertos.a:croutine(.text .text.* ...) C + # *libfreertos.a:croutine(.text .text.* ...) C # # dram0_data # *(.data ..) # *(.dram ...) - # *libfreertos.a:croutine(.rodata .rodata.*) D - mapping = u""" + # *libfreertos.a:croutine(.rodata .rodata.*) D + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -818,7 +921,7 @@ entries: # noflash = text -> iram0_text, rodata -> dram0_data # # This operation should fail. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -837,7 +940,7 @@ entries: # noflash = .text -> iram0_text, .rodata -> dram0_data # # This operation should fail. - mapping = u""" + mapping = """ [sections:custom_text] entries: .text+ @@ -861,42 +964,42 @@ entries: # using another. Another object and symbol is mapped the other way around. # # flash_text - # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .text ...) A, B + # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .text ...) A, B # # flash_rodata - # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .rodata ...) A, B + # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .rodata ...) A, B # # dram0_data - # *(EXCLUDE_FILES(libfreertos.a:timers) .data ..) B + # *(EXCLUDE_FILES(libfreertos.a:timers) .data ..) B # *(.dram ...) - # *libfreertos.a:croutine(.rodata .rodata.*) C - # *libfreertos.a:timers(.rodata.prvProcessReceivedCommands ...) E + # *libfreertos.a:croutine(.rodata .rodata.*) C + # *libfreertos.a:timers(.rodata.prvProcessReceivedCommands ...) E # # dram0_bss - # *(EXCLUDE_FILE(libfreertos.a:timers) .bss .bss.* ...) B - # *(EXCLUDE_FILE(libfreertos.a:timers) COMMON) B + # *(EXCLUDE_FILE(libfreertos.a:timers) .bss .bss.* ...) B + # *(EXCLUDE_FILE(libfreertos.a:timers) COMMON) B # # iram0_text # *(.iram ...) - # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) C - # *libfreertos.a:timers(.literal .literal.prvProcessReceivedCommands ...) E + # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) C + # *libfreertos.a:timers(.literal .literal.prvProcessReceivedCommands ...) E # # rtc_text # *(rtc.text .rtc.literal) - # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) F - # libfreertos.a:timers (.text .text.prvCheckForValidListAndQueue ...) D.2 + # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) F + # libfreertos.a:timers (.text .text.prvCheckForValidListAndQueue ...) D.2 # # rtc_data # *(rtc.data) # *(rtc.rodata) - # libfreertos.a:timers (.data .data.*) D - # libfreertos.a:timers (.rodata ...) D.2 + # libfreertos.a:timers (.data .data.*) D + # libfreertos.a:timers (.rodata ...) D.2 # # rtc_bss # *(rtc.bss .rtc.bss) - # libfreertos.a:timers (.bss .bss.*) D - # libfreertos.a:timers (COMMON) D - mapping = u""" + # libfreertos.a:timers (.bss .bss.*) D + # libfreertos.a:timers (COMMON) D + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -944,7 +1047,9 @@ entries: # Commands for #4 F # Processed first due to alphabetical ordering - rtc_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + rtc_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) # Commands for #2 D # List all relevant sections excluding #3 for text -> rtc_text and D.2 @@ -966,7 +1071,11 @@ entries: rtc_bss.append(InputSectionDesc(TIMERS, dram0_bss[1].sections, [])) # Commands for #3 E - iram0_text.append(InputSectionDesc(TIMERS, set(['.text.prvProcessReceivedCommands', '.literal.prvProcessReceivedCommands']), [])) + iram0_text.append( + InputSectionDesc( + TIMERS, set(['.text.prvProcessReceivedCommands', '.literal.prvProcessReceivedCommands']), [] + ) + ) dram0_data.append(InputSectionDesc(TIMERS, set(['.rodata.prvProcessReceivedCommands']), [])) self.compare_rules(expected, actual) @@ -983,7 +1092,7 @@ entries: # * (EXCLUDE_FILE(libfreertos.a libfreertos.a:croutine) .text ...) # # iram0_text - mapping = u""" + mapping = """ [mapping:test_1] archive: libfreertos.a entries: @@ -1028,7 +1137,7 @@ entries: # # Uses the same entries as C_05 but spreads them across # two fragments. The output should still be the same. - mapping = u""" + mapping = """ [mapping:test_1] archive: libfreertos.a entries: @@ -1046,7 +1155,7 @@ entries: def test_mapping_same_lib_in_multiple_fragments_conflict(self): # Test mapping fragments operating on the same archive # with conflicting mappings. - mapping = u""" + mapping = """ [mapping:test_1] archive: libfreertos.a entries: @@ -1078,7 +1187,7 @@ entries: # libfreertos:croutine(.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) G # libfreertos2:croutine(.text .literal ...) D # libfreertos2:croutine2(.text .literal ...) E - mapping = u""" + mapping = """ [mapping:freertos2] archive: libfreertos2.a entries: @@ -1116,8 +1225,12 @@ entries: flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) # Command for - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), [])) - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), []) + ) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) iram0_text.append(InputSectionDesc(Entity(FREERTOS2.archive, 'croutine'), flash_text[0].sections, [])) iram0_text.append(InputSectionDesc(Entity(FREERTOS2.archive, 'croutine2'), flash_text[0].sections, [])) @@ -1126,7 +1239,7 @@ entries: def test_ambigious_obj(self): # Command generation for ambiguous entry should fail. - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -1143,7 +1256,7 @@ entries: # # 'custom_scheme' entries conflict the 'default' scheme # entries. - mapping = u""" + mapping = """ [scheme:custom_scheme] entries: flash_text -> iram0_text @@ -1163,7 +1276,7 @@ entries: # # custom_scheme has the 'iram -> iram0_text' in common with # default scheme - mapping = u""" + mapping = """ [sections:custom_section] entries: .custom_section @@ -1198,7 +1311,7 @@ class ConfigTest(GenerationTest): def _test_conditional_on_scheme(self, perf, alt=None): # Test that proper commands are generated if using # schemes with conditional entries. - scheme = u""" + scheme = """ [sections:cond_text_data] entries: if PERFORMANCE_LEVEL >= 1: @@ -1215,7 +1328,7 @@ entries: cond_text_data -> dram0_data """ - mapping = u""" + mapping = """ [mapping:test] archive: lib.a entries: @@ -1250,7 +1363,7 @@ entries: def test_conditional_mapping(self, alt=None): # Test that proper commands are generated # in conditional mapping entries. - mapping = u""" + mapping = """ [mapping:default] archive: * entries: @@ -1299,7 +1412,7 @@ entries: def test_multiple_fragment_same_lib_conditional(self): # Test conditional entries on new mapping fragment grammar. # across multiple fragments. - mapping = u""" + mapping = """ [mapping:default] archive: * entries: @@ -1331,7 +1444,6 @@ entries: class FlagTest(GenerationTest): - # Test correct generation of mapping fragment entries # with flags. @@ -1362,7 +1474,7 @@ class FlagTest(GenerationTest): # libfreertos.a:croutine(.text .literal ...) I # . = ALIGN(4) G.2 # _sym1_end H.2 - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -1422,7 +1534,7 @@ entries: # iram0_text # *(.iram .iram.*) # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D - mapping = u""" + mapping = """ [mapping:default] archive: * entries: @@ -1466,7 +1578,9 @@ entries: flash_text.append(SymbolAtAddress('_sym1_end')) # Command for #3 D - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -1484,7 +1598,7 @@ entries: # iram0_text # *(.iram .iram.*) # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -1523,7 +1637,9 @@ entries: flash_text.append(SymbolAtAddress('_sym1_end')) # Command for #3 C - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -1540,7 +1656,7 @@ entries: # iram0_text # *(.iram .iram.*) # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) C - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -1575,7 +1691,9 @@ entries: flash_text.append(SymbolAtAddress('_sym1_end')) # Command for #3 C - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -1591,7 +1709,7 @@ entries: # iram0_text # *(.iram .iram.*) # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D - mapping = u""" + mapping = """ [mapping:default] archive: * entries: @@ -1636,7 +1754,9 @@ entries: flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) # Command for #4 D - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) @@ -1653,7 +1773,7 @@ entries: # iram0_text # *(.iram .iram.*) # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D - mapping = u""" + mapping = """ [mapping:test] archive: libfreertos.a entries: @@ -1693,14 +1813,16 @@ entries: flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) # Command for #3 C - iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + iram0_text.append( + InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []) + ) self.compare_rules(expected, actual) def test_flag_additions(self): # Test ability to add flags as long as no other mapping fragments # does the same thing. - mapping = u""" + mapping = """ [mapping:default_add_flag] archive: * entries: @@ -1721,7 +1843,7 @@ entries: def test_flags_flag_additions_duplicate(self): # Test same flags added to same entity - these # are ignored. - mapping = u""" + mapping = """ [mapping:default_add_flag_1] archive: * entries: @@ -1748,7 +1870,7 @@ entries: def test_flags_flag_additions_conflict(self): # Test condition where multiple fragments specifies flags # to same entity - should generate exception. - mapping = u""" + mapping = """ [mapping:default_add_flag_1] archive: * entries: