diff --git a/components/bt/component.mk b/components/bt/component.mk index 7d5019f242..12d98e6ff2 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -16,6 +16,7 @@ COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/controller/lib/esp32 \ COMPONENT_ADD_LINKER_DEPS := $(patsubst %,$(COMPONENT_PATH)/controller/lib/esp32/lib%.a,$(LIBS)) COMPONENT_SUBMODULES += controller/lib +COMPONENT_ADD_LDFRAGMENTS += linker.lf # TODO: annotate fallthroughs in Bluedroid code with comments diff --git a/components/bt/linker.lf b/components/bt/linker.lf index e8cbc14628..eeab598215 100644 --- a/components/bt/linker.lf +++ b/components/bt/linker.lf @@ -1,5 +1,40 @@ +[sections:bss_common] +entries: + .bss+ + COMMON + +[scheme:bt_start_end] +entries: + bss_common -> dram0_bss + data -> dram0_data + +# For the following fragments, order matters for +# 'ALIGN(4, post) SURROUND(sym)', which generates: +# +# _sym_start +# ... +# . = ALIGN(4) +# _sym_end + [mapping:bt] archive: libbt.a entries: + * (bt_start_end); + bss_common -> dram0_bss ALIGN(4, post) SURROUND(bt_bss), + data -> dram0_data ALIGN(4, post) SURROUND(bt_data) if ESP_ALLOW_BSS_SEG_EXTERNAL_MEMORY = y: * (extram_bss) + +[mapping:btdm] +archive: libbtdm_app.a +entries: + * (bt_start_end); + bss_common -> dram0_bss ALIGN(4, post) SURROUND(btdm_bss), + data -> dram0_data ALIGN(4, post) SURROUND(btdm_data) + +[mapping:nimble] +archive: libnimble.a +entries: + * (bt_start_end); + bss_common -> dram0_bss ALIGN(4, post) SURROUND(nimble_bss), + data -> dram0_data ALIGN(4, post) SURROUND(nimble_data) diff --git a/components/esp32/ld/esp32.project.ld.in b/components/esp32/ld/esp32.project.ld.in index 84ff5fc672..3cd0d7e8f4 100644 --- a/components/esp32/ld/esp32.project.ld.in +++ b/components/esp32/ld/esp32.project.ld.in @@ -37,9 +37,7 @@ SECTIONS . = ALIGN(4); _rtc_force_fast_start = ABSOLUTE(.); - _coredump_rtc_fast_start = ABSOLUTE(.); - mapping[rtc_fast_coredump] - _coredump_rtc_fast_end = ABSOLUTE(.); + mapping[rtc_force_fast] *(.rtc.force_fast .rtc.force_fast.*) . = ALIGN(4) ; @@ -58,12 +56,6 @@ SECTIONS { _rtc_data_start = ABSOLUTE(.); - /* coredump mapping */ - _coredump_rtc_start = ABSOLUTE(.); - mapping[rtc_coredump] - _coredump_rtc_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ mapping[rtc_data] *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*) @@ -180,18 +172,6 @@ SECTIONS .dram0.data : { _data_start = ABSOLUTE(.); - _bt_data_start = ABSOLUTE(.); - *libbt.a:(.data .data.*) - . = ALIGN (4); - _bt_data_end = ABSOLUTE(.); - _btdm_data_start = ABSOLUTE(.); - *libbtdm_app.a:(.data .data.*) - . = ALIGN (4); - _btdm_data_end = ABSOLUTE(.); - _nimble_data_start = ABSOLUTE(.); - *libnimble.a:(.data .data.*) - . = ALIGN (4); - _nimble_data_end = ABSOLUTE(.); *(.gnu.linkonce.d.*) *(.data1) *(.sdata) @@ -202,12 +182,6 @@ SECTIONS *(.gnu.linkonce.s2.*) *(.jcr) - /* coredump mapping */ - _coredump_dram_start = ABSOLUTE(.); - mapping[dram_coredump] - _coredump_dram_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ _esp_system_init_fn_array_start = ABSOLUTE(.); KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); @@ -248,18 +222,6 @@ SECTIONS { . = ALIGN (8); _bss_start = ABSOLUTE(.); - _bt_bss_start = ABSOLUTE(.); - *libbt.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _bt_bss_end = ABSOLUTE(.); - _btdm_bss_start = ABSOLUTE(.); - *libbtdm_app.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _btdm_bss_end = ABSOLUTE(.); - _nimble_bss_start = ABSOLUTE(.); - *libnimble.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _nimble_bss_end = ABSOLUTE(.); mapping[dram0_bss] @@ -369,12 +331,6 @@ SECTIONS . = ALIGN(4); _iram_data_start = ABSOLUTE(.); - /* coredump mapping */ - _coredump_iram_start = ABSOLUTE(.); - mapping[iram_coredump] - _coredump_iram_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ mapping[iram0_data] _iram_data_end = ABSOLUTE(.); diff --git a/components/esp32/ld/esp32_fragments.lf b/components/esp32/ld/esp32_fragments.lf index 9e7d77fd25..48e15baca5 100644 --- a/components/esp32/ld/esp32_fragments.lf +++ b/components/esp32/ld/esp32_fragments.lf @@ -48,22 +48,6 @@ entries: entries: .rtc.bss -[sections:rtc_fast_coredump] -entries: - .rtc.fast.coredump+ - -[sections:rtc_coredump] -entries: - .rtc.coredump+ - -[sections:dram_coredump] -entries: - .dram1.coredump+ - -[sections:iram_coredump] -entries: - .iram.data.coredump+ - [sections:iram] entries: .iram1+ @@ -84,30 +68,6 @@ entries: entries: .dram1+ -[sections:wifi_iram] -entries: - .wifi0iram+ - -[sections:wifi_rx_iram] -entries: - .wifirxiram+ - -[sections:phy_iram] -entries: - .phyiram+ - -[sections:wifi_slp_iram] -entries: - .wifislpiram+ - -[sections:wifi_or_slp_iram] -entries: - .wifiorslpiram+ - -[sections:wifi_slp_rx_iram] -entries: - .wifislprxiram+ - [scheme:default] entries: if APP_BUILD_USE_FLASH_SECTIONS = y: @@ -132,16 +92,6 @@ entries: rtc_data -> rtc_data rtc_rodata -> rtc_data rtc_bss -> rtc_bss - wifi_iram -> flash_text - wifi_rx_iram -> flash_text - phy_iram -> flash_text - dram_coredump -> dram_coredump - iram_coredump -> iram_coredump - rtc_coredump -> rtc_coredump - rtc_fast_coredump -> rtc_fast_coredump - wifi_slp_iram -> flash_text - wifi_or_slp_iram -> flash_text - wifi_slp_rx_iram -> flash_text [scheme:rtc] entries: @@ -164,31 +114,7 @@ entries: entries: text -> iram0_text -[scheme:wifi_iram] +[mapping:default] +archive: * entries: - wifi_iram -> iram0_text - -[scheme:wifi_rx_iram] -entries: - wifi_rx_iram -> iram0_text - -[scheme:phy_iram] -entries: - phy_iram -> iram0_text - -[scheme:extram_bss] -entries: - bss -> extern_ram - common -> extern_ram - -[scheme:wifi_slp_iram] -entries: - wifi_slp_iram -> iram0_text - -[scheme:wifi_or_slp_iram] -entries: - wifi_or_slp_iram -> iram0_text - -[scheme:wifi_slp_rx_iram] -entries: - wifi_slp_rx_iram -> iram0_text + * (default) diff --git a/components/esp32c3/ld/esp32c3.project.ld.in b/components/esp32c3/ld/esp32c3.project.ld.in index fdf6be2881..de18365153 100644 --- a/components/esp32c3/ld/esp32c3.project.ld.in +++ b/components/esp32c3/ld/esp32c3.project.ld.in @@ -39,9 +39,7 @@ SECTIONS . = ALIGN(4); _rtc_force_fast_start = ABSOLUTE(.); - _coredump_rtc_fast_start = ABSOLUTE(.); - mapping[rtc_fast_coredump] - _coredump_rtc_fast_end = ABSOLUTE(.); + mapping[rtc_force_fast] *(.rtc.force_fast .rtc.force_fast.*) . = ALIGN(4) ; @@ -60,12 +58,6 @@ SECTIONS { _rtc_data_start = ABSOLUTE(.); - /* coredump mapping */ - _coredump_rtc_start = ABSOLUTE(.); - mapping[rtc_coredump] - _coredump_rtc_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ mapping[rtc_data] *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*) @@ -158,18 +150,6 @@ SECTIONS .dram0.data : { _data_start = ABSOLUTE(.); - _bt_data_start = ABSOLUTE(.); - *libbt.a:(.data .data.*) - . = ALIGN (4); - _bt_data_end = ABSOLUTE(.); - _btdm_data_start = ABSOLUTE(.); - *libbtdm_app.a:(.data .data.*) - . = ALIGN (4); - _btdm_data_end = ABSOLUTE(.); - _nimble_data_start = ABSOLUTE(.); - *libnimble.a:(.data .data.*) - . = ALIGN (4); - _nimble_data_end = ABSOLUTE(.); *(.gnu.linkonce.d.*) *(.data1) __global_pointer$ = . + 0x800; @@ -181,12 +161,6 @@ SECTIONS *(.gnu.linkonce.s2.*) *(.jcr) - /* coredump mapping */ - _coredump_dram_start = ABSOLUTE(.); - mapping[dram_coredump] - _coredump_dram_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ _esp_system_init_fn_array_start = ABSOLUTE(.); KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); @@ -217,18 +191,6 @@ SECTIONS { . = ALIGN (8); _bss_start = ABSOLUTE(.); - _bt_bss_start = ABSOLUTE(.); - *libbt.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _bt_bss_end = ABSOLUTE(.); - _btdm_bss_start = ABSOLUTE(.); - *libbtdm_app.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _btdm_bss_end = ABSOLUTE(.); - _nimble_bss_start = ABSOLUTE(.); - *libnimble.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _nimble_bss_end = ABSOLUTE(.); mapping[dram0_bss] @@ -369,12 +331,6 @@ SECTIONS . = ALIGN(16); _iram_data_start = ABSOLUTE(.); - /* coredump mapping */ - _coredump_iram_start = ABSOLUTE(.); - mapping[iram_coredump] - _coredump_iram_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ mapping[iram0_data] _iram_data_end = ABSOLUTE(.); diff --git a/components/esp32c3/ld/esp32c3_fragments.lf b/components/esp32c3/ld/esp32c3_fragments.lf index 252f77e3b1..feea1ec538 100644 --- a/components/esp32c3/ld/esp32c3_fragments.lf +++ b/components/esp32c3/ld/esp32c3_fragments.lf @@ -36,22 +36,6 @@ entries: entries: .rtc.bss -[sections:rtc_fast_coredump] -entries: - .rtc.fast.coredump+ - -[sections:rtc_coredump] -entries: - .rtc.coredump+ - -[sections:dram_coredump] -entries: - .dram1.coredump+ - -[sections:iram_coredump] -entries: - .iram.data.coredump+ - [sections:iram] entries: .iram1+ @@ -68,26 +52,6 @@ entries: entries: .dram1+ -[sections:wifi_iram] -entries: - .wifi0iram+ - -[sections:wifi_rx_iram] -entries: - .wifirxiram+ - -[sections:wifi_slp_iram] -entries: - .wifislpiram+ - -[sections:wifi_or_slp_iram] -entries: - .wifiorslpiram+ - -[sections:wifi_slp_rx_iram] -entries: - .wifislprxiram+ - [scheme:default] entries: if APP_BUILD_USE_FLASH_SECTIONS = y: @@ -107,15 +71,6 @@ entries: rtc_data -> rtc_data rtc_rodata -> rtc_data rtc_bss -> rtc_bss - wifi_iram -> flash_text - wifi_rx_iram -> flash_text - dram_coredump -> dram_coredump - iram_coredump -> iram_coredump - rtc_coredump -> rtc_coredump - rtc_fast_coredump -> rtc_fast_coredump - wifi_slp_iram -> flash_text - wifi_or_slp_iram -> flash_text - wifi_slp_rx_iram -> flash_text [scheme:rtc] entries: @@ -138,22 +93,7 @@ entries: entries: text -> iram0_text -[scheme:wifi_iram] +[mapping:default] +archive: * entries: - wifi_iram -> iram0_text - -[scheme:wifi_rx_iram] -entries: - wifi_rx_iram -> iram0_text - -[scheme:wifi_slp_iram] -entries: - wifi_slp_iram -> iram0_text - -[scheme:wifi_or_slp_iram] -entries: - wifi_or_slp_iram -> iram0_text - -[scheme:wifi_slp_rx_iram] -entries: - wifi_slp_rx_iram -> iram0_text + * (default) diff --git a/components/esp32s2/ld/esp32s2.project.ld.in b/components/esp32s2/ld/esp32s2.project.ld.in index c60d29b665..307de69c58 100644 --- a/components/esp32s2/ld/esp32s2.project.ld.in +++ b/components/esp32s2/ld/esp32s2.project.ld.in @@ -45,9 +45,7 @@ SECTIONS . = ALIGN(4); _rtc_force_fast_start = ABSOLUTE(.); - _coredump_rtc_fast_start = ABSOLUTE(.); - mapping[rtc_fast_coredump] - _coredump_rtc_fast_end = ABSOLUTE(.); + mapping[rtc_force_fast] *(.rtc.force_fast .rtc.force_fast.*) . = ALIGN(4) ; @@ -65,12 +63,6 @@ SECTIONS { _rtc_data_start = ABSOLUTE(.); - /* coredump mapping */ - _coredump_rtc_start = ABSOLUTE(.); - mapping[rtc_coredump] - _coredump_rtc_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ mapping[rtc_data] *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*) @@ -208,12 +200,6 @@ SECTIONS *(.gnu.linkonce.s2.*) *(.jcr) - /* coredump mapping */ - _coredump_dram_start = ABSOLUTE(.); - mapping[dram_coredump] - _coredump_dram_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ _esp_system_init_fn_array_start = ABSOLUTE(.); KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); diff --git a/components/esp32s2/ld/esp32s2_fragments.lf b/components/esp32s2/ld/esp32s2_fragments.lf index 5747f62765..65f93737a9 100644 --- a/components/esp32s2/ld/esp32s2_fragments.lf +++ b/components/esp32s2/ld/esp32s2_fragments.lf @@ -36,18 +36,6 @@ entries: entries: .rtc.bss -[sections:rtc_fast_coredump] -entries: - .rtc.fast.coredump+ - -[sections:rtc_coredump] -entries: - .rtc.coredump+ - -[sections:dram_coredump] -entries: - .dram1.coredump+ - [sections:iram] entries: .iram1+ @@ -56,26 +44,6 @@ entries: entries: .dram1+ -[sections:wifi_iram] -entries: - .wifi0iram+ - -[sections:wifi_rx_iram] -entries: - .wifirxiram+ - -[sections:wifi_slp_iram] -entries: - .wifislpiram+ - -[sections:wifi_or_slp_iram] -entries: - .wifiorslpiram+ - -[sections:wifi_slp_rx_iram] -entries: - .wifislprxiram+ - [scheme:default] entries: if APP_BUILD_USE_FLASH_SECTIONS = y: @@ -93,14 +61,6 @@ entries: rtc_data -> rtc_data rtc_rodata -> rtc_data rtc_bss -> rtc_bss - wifi_iram -> flash_text - wifi_rx_iram -> flash_text - dram_coredump -> dram_coredump - rtc_coredump -> rtc_coredump - rtc_fast_coredump -> rtc_fast_coredump - wifi_slp_iram -> flash_text - wifi_or_slp_iram -> flash_text - wifi_slp_rx_iram -> flash_text [scheme:rtc] entries: @@ -123,22 +83,7 @@ entries: entries: text -> iram0_text -[scheme:wifi_iram] +[mapping:default] +archive: * entries: - wifi_iram -> iram0_text - -[scheme:wifi_rx_iram] -entries: - wifi_rx_iram -> iram0_text - -[scheme:wifi_slp_iram] -entries: - wifi_slp_iram -> iram0_text - -[scheme:wifi_or_slp_iram] -entries: - wifi_or_slp_iram -> iram0_text - -[scheme:wifi_slp_rx_iram] -entries: - wifi_slp_rx_iram -> iram0_text + * (default) diff --git a/components/esp32s3/ld/esp32s3.project.ld.in b/components/esp32s3/ld/esp32s3.project.ld.in index b1d19cae40..38c58180b9 100644 --- a/components/esp32s3/ld/esp32s3.project.ld.in +++ b/components/esp32s3/ld/esp32s3.project.ld.in @@ -39,9 +39,7 @@ SECTIONS . = ALIGN(4); _rtc_force_fast_start = ABSOLUTE(.); - _coredump_rtc_fast_start = ABSOLUTE(.); - mapping[rtc_fast_coredump] - _coredump_rtc_fast_end = ABSOLUTE(.); + mapping[rtc_force_fast] *(.rtc.force_fast .rtc.force_fast.*) . = ALIGN(4) ; @@ -60,12 +58,6 @@ SECTIONS { _rtc_data_start = ABSOLUTE(.); - /* coredump mapping */ - _coredump_rtc_start = ABSOLUTE(.); - mapping[rtc_coredump] - _coredump_rtc_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ mapping[rtc_data] *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*) @@ -189,18 +181,6 @@ SECTIONS .dram0.data : { _data_start = ABSOLUTE(.); - _bt_data_start = ABSOLUTE(.); - *libbt.a:(.data .data.*) - . = ALIGN (4); - _bt_data_end = ABSOLUTE(.); - _btdm_data_start = ABSOLUTE(.); - *libbtdm_app.a:(.data .data.*) - . = ALIGN (4); - _btdm_data_end = ABSOLUTE(.); - _nimble_data_start = ABSOLUTE(.); - *libnimble.a:(.data .data.*) - . = ALIGN (4); - _nimble_data_end = ABSOLUTE(.); *(.gnu.linkonce.d.*) *(.data1) *(.sdata) @@ -211,12 +191,6 @@ SECTIONS *(.gnu.linkonce.s2.*) *(.jcr) - /* coredump mapping */ - _coredump_dram_start = ABSOLUTE(.); - mapping[dram_coredump] - _coredump_dram_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ _esp_system_init_fn_array_start = ABSOLUTE(.); KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*))) _esp_system_init_fn_array_end = ABSOLUTE(.); @@ -248,18 +222,6 @@ SECTIONS . = ALIGN (8); _bss_start = ABSOLUTE(.); *(.ext_ram.bss*) - _bt_bss_start = ABSOLUTE(.); - *libbt.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _bt_bss_end = ABSOLUTE(.); - _btdm_bss_start = ABSOLUTE(.); - *libbtdm_app.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _btdm_bss_end = ABSOLUTE(.); - _nimble_bss_start = ABSOLUTE(.); - *libnimble.a:(.bss .bss.* COMMON) - . = ALIGN (4); - _nimble_bss_end = ABSOLUTE(.); mapping[dram0_bss] @@ -386,12 +348,6 @@ SECTIONS . = ALIGN(4); _iram_data_start = ABSOLUTE(.); - /* coredump mapping */ - _coredump_iram_start = ABSOLUTE(.); - mapping[iram_coredump] - _coredump_iram_end = ABSOLUTE(.); - - /* should be placed after coredump mapping */ mapping[iram0_data] _iram_data_end = ABSOLUTE(.); diff --git a/components/esp32s3/ld/esp32s3_fragments.lf b/components/esp32s3/ld/esp32s3_fragments.lf index 252f77e3b1..feea1ec538 100644 --- a/components/esp32s3/ld/esp32s3_fragments.lf +++ b/components/esp32s3/ld/esp32s3_fragments.lf @@ -36,22 +36,6 @@ entries: entries: .rtc.bss -[sections:rtc_fast_coredump] -entries: - .rtc.fast.coredump+ - -[sections:rtc_coredump] -entries: - .rtc.coredump+ - -[sections:dram_coredump] -entries: - .dram1.coredump+ - -[sections:iram_coredump] -entries: - .iram.data.coredump+ - [sections:iram] entries: .iram1+ @@ -68,26 +52,6 @@ entries: entries: .dram1+ -[sections:wifi_iram] -entries: - .wifi0iram+ - -[sections:wifi_rx_iram] -entries: - .wifirxiram+ - -[sections:wifi_slp_iram] -entries: - .wifislpiram+ - -[sections:wifi_or_slp_iram] -entries: - .wifiorslpiram+ - -[sections:wifi_slp_rx_iram] -entries: - .wifislprxiram+ - [scheme:default] entries: if APP_BUILD_USE_FLASH_SECTIONS = y: @@ -107,15 +71,6 @@ entries: rtc_data -> rtc_data rtc_rodata -> rtc_data rtc_bss -> rtc_bss - wifi_iram -> flash_text - wifi_rx_iram -> flash_text - dram_coredump -> dram_coredump - iram_coredump -> iram_coredump - rtc_coredump -> rtc_coredump - rtc_fast_coredump -> rtc_fast_coredump - wifi_slp_iram -> flash_text - wifi_or_slp_iram -> flash_text - wifi_slp_rx_iram -> flash_text [scheme:rtc] entries: @@ -138,22 +93,7 @@ entries: entries: text -> iram0_text -[scheme:wifi_iram] +[mapping:default] +archive: * entries: - wifi_iram -> iram0_text - -[scheme:wifi_rx_iram] -entries: - wifi_rx_iram -> iram0_text - -[scheme:wifi_slp_iram] -entries: - wifi_slp_iram -> iram0_text - -[scheme:wifi_or_slp_iram] -entries: - wifi_or_slp_iram -> iram0_text - -[scheme:wifi_slp_rx_iram] -entries: - wifi_slp_rx_iram -> iram0_text + * (default) diff --git a/components/esp_wifi/linker.lf b/components/esp_wifi/linker.lf index d955938bce..05444cbcd1 100644 --- a/components/esp_wifi/linker.lf +++ b/components/esp_wifi/linker.lf @@ -1,3 +1,72 @@ +[sections:wifi_iram] +entries: + .wifi0iram+ + +[sections:wifi_rx_iram] +entries: + .wifirxiram+ + +[sections:wifi_slp_iram] +entries: + .wifislpiram+ + +[sections:wifi_or_slp_iram] +entries: + .wifiorslpiram+ + +[sections:wifi_slp_rx_iram] +entries: + .wifislprxiram+ + +[scheme:wifi_iram] +entries: + wifi_iram -> iram0_text + +[scheme:wifi_rx_iram] +entries: + wifi_rx_iram -> iram0_text + +[scheme:extram_bss] +entries: + bss -> extern_ram + common -> extern_ram + +[scheme:wifi_slp_iram] +entries: + wifi_slp_iram -> iram0_text + +[scheme:wifi_or_slp_iram] +entries: + wifi_or_slp_iram -> iram0_text + +[scheme:wifi_slp_rx_iram] +entries: + wifi_slp_rx_iram -> iram0_text + +if IDF_TARGET_ESP32 = y: + [scheme:phy_iram] + entries: + phy_iram -> iram0_text + + [sections:phy_iram] + entries: + .phyiram+ + +[scheme:wifi_default] +entries: + wifi_slp_iram -> flash_text + wifi_or_slp_iram -> flash_text + wifi_slp_rx_iram -> flash_text + wifi_iram -> flash_text + wifi_rx_iram -> flash_text + if IDF_TARGET_ESP32 = y: + phy_iram -> flash_text + +[mapping:wifi_default] +archive: * +entries: + * (wifi_default) + [mapping:phy] archive: libphy.a entries: diff --git a/components/espcoredump/linker.lf b/components/espcoredump/linker.lf index 690a9d9aef..f4af6d7c64 100644 --- a/components/espcoredump/linker.lf +++ b/components/espcoredump/linker.lf @@ -1,3 +1,39 @@ +[sections:rtc_fast_coredump] +entries: + .rtc.fast.coredump+ + +[sections:rtc_coredump] +entries: + .rtc.coredump+ + +[sections:dram_coredump] +entries: + .dram1.coredump+ + +if IDF_TARGET_ESP32S2 = n: + [sections:iram_coredump] + entries: + .iram.data.coredump+ + +[scheme:coredump_default] +entries: + dram_coredump -> dram0_data + rtc_coredump -> rtc_data + rtc_fast_coredump -> rtc_force_fast + if IDF_TARGET_ESP32S2 = n: + iram_coredump -> iram0_data + +[mapping:coredump_default] +archive: * +entries: + * (coredump_default); + rtc_fast_coredump -> rtc_force_fast SURROUND(coredump_rtc_fast), + rtc_coredump -> rtc_data SURROUND(coredump_rtc), + dram_coredump -> dram0_data SURROUND(coredump_dram) + if IDF_TARGET_ESP32S2 = n: + * (coredump_default); + iram_coredump -> iram0_data SURROUND(coredump_iram) + [mapping:espcoredump] archive: libespcoredump.a entries: diff --git a/components/lwip/linker.lf b/components/lwip/linker.lf index 55972ecae5..28c6a14c67 100644 --- a/components/lwip/linker.lf +++ b/components/lwip/linker.lf @@ -81,9 +81,6 @@ entries: ethernetif:ethernetif_input (noflash_text) wlanif:low_level_output (noflash_text) wlanif:wlanif_input (noflash_text) - else: - - * (default) if ESP_ALLOW_BSS_SEG_EXTERNAL_MEMORY = y: * (extram_bss) diff --git a/tools/ldgen/fragments.py b/tools/ldgen/fragments.py index adfff653c9..d3e2a2e761 100644 --- a/tools/ldgen/fragments.py +++ b/tools/ldgen/fragments.py @@ -17,9 +17,12 @@ import abc import os import re from collections import namedtuple +from enum import Enum -from pyparsing import (Combine, Forward, Group, Literal, OneOrMore, Optional, ParseFatalException, Suppress, Word, - ZeroOrMore, alphanums, alphas, indentedBlock, originalTextFor, restOfLine) +from entity import Entity +from pyparsing import (Combine, Forward, Group, Keyword, Literal, OneOrMore, Optional, Or, ParseFatalException, + Suppress, Word, ZeroOrMore, alphanums, alphas, delimitedList, indentedBlock, nums, + originalTextFor, restOfLine) from sdkconfig import SDKConfig KeyGrammar = namedtuple('KeyGrammar', 'grammar min max required') @@ -267,11 +270,117 @@ class Mapping(Fragment): Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under. """ - MAPPING_ALL_OBJECTS = '*' + class Flag(): + PRE_POST = (Optional(Suppress(',') + Suppress('pre').setParseAction(lambda: True).setResultsName('pre')) + + Optional(Suppress(',') + Suppress('post').setParseAction(lambda: True).setResultsName('post'))) + + class Surround(Flag): + + def __init__(self, symbol): + self.symbol = symbol + self.pre = True + self.post = True + + @staticmethod + def get_grammar(): + # SURROUND(symbol) + # + # __symbol_start, __symbol_end is generated before and after + # the corresponding input section description, respectively. + grammar = (Keyword('SURROUND').suppress() + + Suppress('(') + + Fragment.IDENTIFIER.setResultsName('symbol') + + Suppress(')')) + + grammar.setParseAction(lambda tok: Mapping.Surround(tok.symbol)) + return grammar + + def __eq__(self, other): + return (isinstance(other, Mapping.Surround) and + self.symbol == other.symbol) + + class Align(Flag): + + def __init__(self, alignment, pre=True, post=False): + self.alignment = alignment + self.pre = pre + self.post = post + + @staticmethod + def get_grammar(): + # ALIGN(alignment, [, pre, post]) + grammar = (Keyword('ALIGN').suppress() + + Suppress('(') + + Word(nums).setResultsName('alignment') + + Mapping.Flag.PRE_POST + + Suppress(')')) + + def on_parse(tok): + alignment = int(tok.alignment) + if tok.pre == '' and tok.post == '': + res = Mapping.Align(alignment) + elif tok.pre != '' and tok.post == '': + res = Mapping.Align(alignment, tok.pre) + elif tok.pre == '' and tok.post != '': + res = Mapping.Align(alignment, False, tok.post) + else: + res = Mapping.Align(alignment, tok.pre, tok.post) + return res + + grammar.setParseAction(on_parse) + return grammar + + def __eq__(self, other): + return (isinstance(other, Mapping.Align) and + self.alignment == other.alignment and + self.pre == other.pre and + self.post == other.post) + + class Keep(Flag): + + def __init__(self): + pass + + @staticmethod + def get_grammar(): + grammar = Keyword('KEEP').setParseAction(Mapping.Keep) + return grammar + + def __eq__(self, other): + return isinstance(other, Mapping.Keep) + + class Sort(Flag): + class Type(Enum): + NAME = 0 + ALIGNMENT = 1 + INIT_PRIORITY = 2 + + def __init__(self, first, second=None): + self.first = first + self.second = second + + @staticmethod + def get_grammar(): + # SORT(sort_by_first [, sort_by_second]) + keywords = Keyword('name') | Keyword('alignment') | Keyword('init_priority') + grammar = (Keyword('SORT').suppress() + Suppress('(') + + keywords.setResultsName('first') + + Optional(Suppress(',') + keywords.setResultsName('second')) + Suppress(')')) + + grammar.setParseAction(lambda tok: Mapping.Sort(tok.first, tok.second if tok.second != '' else None)) + return grammar + + def __eq__(self, other): + return (isinstance(other, Mapping.Sort) and + self.first == other.first and + self.second == other.second) def __init__(self): Fragment.__init__(self) self.entries = set() + # k = (obj, symbol, scheme) + # v = list((section, target), Mapping.Flag)) + self.flags = dict() self.deprecated = False def set_key_value(self, key, parse_results): @@ -283,40 +392,63 @@ class Mapping(Fragment): symbol = None scheme = None - try: - obj = result['object'] - except KeyError: - pass + obj = result['object'] try: symbol = result['symbol'] except KeyError: pass - try: - scheme = result['scheme'] - except KeyError: - pass + scheme = result['scheme'] - self.entries.add((obj, symbol, scheme)) + mapping = (obj, symbol, scheme) + self.entries.add(mapping) + + try: + parsed_flags = result['sections_target_flags'] + except KeyError: + parsed_flags = [] + + if parsed_flags: + entry_flags = [] + for pf in parsed_flags: + entry_flags.append((pf.sections, pf.target, list(pf.flags))) + + try: + existing_flags = self.flags[mapping] + except KeyError: + existing_flags = list() + self.flags[mapping] = existing_flags + + existing_flags.extend(entry_flags) def get_key_grammars(self): # There are three possible patterns for mapping entries: # obj:symbol (scheme) # obj (scheme) # * (scheme) + # Flags can be specified for section->target in the scheme specified, ex: + # obj (scheme); section->target SURROUND(symbol), section2->target2 ALIGN(4) obj = Fragment.ENTITY.setResultsName('object') symbol = Suppress(':') + Fragment.IDENTIFIER.setResultsName('symbol') scheme = Suppress('(') + Fragment.IDENTIFIER.setResultsName('scheme') + Suppress(')') - pattern1 = obj + symbol + scheme - pattern2 = obj + scheme - pattern3 = Literal(Mapping.MAPPING_ALL_OBJECTS).setResultsName('object') + scheme + # The flags are specified for section->target in the scheme specified + sections_target = Scheme.grammars['entries'].grammar - entry = pattern1 | pattern2 | pattern3 + flag = Or([f.get_grammar() for f in [Mapping.Keep, Mapping.Align, Mapping.Surround, Mapping.Sort]]) + + section_target_flags = Group(sections_target + Group(OneOrMore(flag)).setResultsName('flags')) + + pattern1 = obj + symbol + pattern2 = obj + pattern3 = Literal(Entity.ALL).setResultsName('object') + + entry = ((pattern1 | pattern2 | pattern3) + scheme + + Optional(Suppress(';') + delimitedList(section_target_flags).setResultsName('sections_target_flags'))) grammars = { - 'archive': KeyGrammar(Fragment.ENTITY.setResultsName('archive'), 1, 1, True), + 'archive': KeyGrammar(Or([Fragment.ENTITY, Word(Entity.ALL)]).setResultsName('archive'), 1, 1, True), 'entries': KeyGrammar(entry, 0, None, True) } @@ -330,7 +462,6 @@ class DeprecatedMapping(): # Name of the default condition entry DEFAULT_CONDITION = 'default' - MAPPING_ALL_OBJECTS = '*' @staticmethod def get_fragment_grammar(sdkconfig, fragment_file): @@ -348,7 +479,7 @@ class DeprecatedMapping(): pattern1 = Group(obj + symbol + scheme) pattern2 = Group(obj + scheme) - pattern3 = Group(Literal(Mapping.MAPPING_ALL_OBJECTS).setResultsName('object') + scheme) + pattern3 = Group(Literal(Entity.ALL).setResultsName('object') + scheme) mapping_entry = pattern1 | pattern2 | pattern3 diff --git a/tools/ldgen/generation.py b/tools/ldgen/generation.py index 7c3c81bc34..71fbf48339 100644 --- a/tools/ldgen/generation.py +++ b/tools/ldgen/generation.py @@ -22,72 +22,83 @@ from collections import namedtuple from entity import Entity from fragments import Mapping, Scheme, Sections from ldgen_common import LdGenFailure -from output_commands import InputSectionDesc +from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress -class RuleNode(): +class Placement(): - class Section(): + def __init__(self, node, sections, target, flags, explicit, force=False, dryrun=False): + self.node = node + self.sections = sections + self.target = target + self.flags = flags - def __init__(self, target, exclusions, explicit=False): - self.target = target - self.exclusions = set(exclusions) + self.exclusions = set() + self.subplacements = set() - # Indicate whether this node has been created explicitly from a mapping, - # or simply just to create a path to the explicitly created node. - # - # For example, - # - # lib.a - # obj:sym (scheme) - # - # Nodes for lib.a and obj will be created, but only the node for - # sym will have been created explicitly. - # - # This is used in deciding whether or not an output command should - # be emitted for this node, or for exclusion rule generation. - self.explicit = explicit + # Force this placement to be output + self.force = force - def __init__(self, parent, name, sections): + # This placement was created from a mapping + # fragment entry. + self.explicit = explicit + + # Find basis placement. A basis placement is a placement + # on the parent (or parent's parent and so on and so forth) + # that operates on the same section as this one. + parent = node.parent + candidate = None + while parent: + try: + candidate = parent.placements[sections] + except KeyError: + pass + + if candidate and candidate.is_significant(): + break + else: + parent = parent.parent + + self.basis = candidate + + if self.is_significant() and not dryrun and self.basis: + self.basis.add_exclusion(self) + + def is_significant(self): + # Check if the placement is significant. Significant placements + # are the end of a basis chain (not self.basis) or a change + # in target (self.target != self.basis.target) + # + # Placement can also be a basis if it has flags + # (self.flags) or its basis has flags (self.basis.flags) + return (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) + + def force_significant(self): + if not self.is_significant(): + self.force = True + if self.basis: + self.basis.add_exclusion(self) + + def add_exclusion(self, exclusion): + self.exclusions.add(exclusion) + + def add_subplacement(self, subplacement): + self.subplacements.add(subplacement) + + +class EntityNode(): + + def __init__(self, parent, name): self.children = [] self.parent = parent self.name = name - self.child_node = None + self.child_t = EntityNode self.entity = None - - self.sections = dict() - - # A node inherits the section -> target entries from - # its parent. This is to simplify logic, avoiding - # going up the parental chain to try a 'basis' rule - # in creating exclusions. This relies on the fact that - # the mappings must be inserted from least to most specific. - # This sort is done in generate_rules(). - if sections: - for (s, v) in sections.items(): - self.sections[s] = RuleNode.Section(v.target, [], []) - - def add_exclusion(self, sections, exclusion): - self.sections[sections].exclusions.add(exclusion) - - # Recursively create exclusions in parents - if self.parent: - self.exclude_from_parent(sections) - - def add_sections(self, sections, target): - try: - _sections = self.sections[sections] - if not _sections.explicit: - _sections.target = target - _sections.explicit = True - else: - if target != _sections.target: - raise GenerationException('Sections mapped to multiple targets') - except KeyError: - self.sections[sections] = RuleNode.Section(target, [], True) - - def exclude_from_parent(self, sections): - self.parent.add_exclusion(sections, self.entity) + self.placements = dict() def add_child(self, entity): child_specificity = self.entity.specificity.value + 1 @@ -99,7 +110,7 @@ class RuleNode(): assert(len(child) <= 1) if not child: - child = self.child_node(self, name, self.sections) + child = self.child_t(self, name) self.children.append(child) else: child = child[0] @@ -125,151 +136,176 @@ class RuleNode(): return commands - def add_node_child(self, entity, sections, target, sections_db): + def get_node_output_commands(self): + commands = collections.defaultdict(list) + + for sections in self.get_output_sections(): + placement = self.placements[sections] + if placement.is_significant(): + assert(placement.node == self) + + keep = False + sort = None + surround_type = [] + + placement_flags = placement.flags if placement.flags is not None else [] + + for flag in placement_flags: + if isinstance(flag, Mapping.Keep): + keep = True + elif isinstance(flag, Mapping.Sort): + sort = (flag.first, flag.second) + else: # SURROUND or ALIGN + surround_type.append(flag) + + for flag in surround_type: + if flag.pre: + if isinstance(flag, Mapping.Surround): + commands[placement.target].append(SymbolAtAddress('_%s_start' % flag.symbol)) + else: # ALIGN + commands[placement.target].append(AlignAtAddress(flag.alignment)) + + # 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) + 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) + commands[placement.target].append(command) + + for flag in surround_type: + if flag.post: + if isinstance(flag, Mapping.Surround): + commands[placement.target].append(SymbolAtAddress('_%s_end' % flag.symbol)) + else: # ALIGN + commands[placement.target].append(AlignAtAddress(flag.alignment)) + + return commands + + def self_placement(self, sections, target, flags, explicit=True, force=False): + placement = Placement(self, sections, target, flags, explicit, force) + self.placements[sections] = placement + return placement + + def child_placement(self, entity, sections, target, flags, sections_db): child = self.add_child(entity) - child.insert(entity, sections, target, sections_db) + child.insert(entity, sections, target, flags, sections_db) - def get_node_output_commands(self): - commands = collections.defaultdict(list) - - for sections in self.get_section_keys(): - info = self.sections[sections] - if info.exclusions or info.explicit: - command = InputSectionDesc(self.entity, sections, info.exclusions) - commands[info.target].append(command) - - return commands - - def insert(self, entity, sections, target, sections_db): + def insert(self, entity, sections, target, flags, sections_db): if self.entity.specificity == entity.specificity: - if self.parent.sections[sections].target != target: - self.add_sections(sections, target) - self.exclude_from_parent(sections) + # Since specificities match, create the placement in this node. + self.self_placement(sections, target, flags) else: - self.add_node_child(entity, sections, target, sections_db) + # If not, create a child node and try to create the placement there. + self.child_placement(entity, sections, target, flags, sections_db) - def get_section_keys(self): - return sorted(self.sections.keys(), key=' '.join) + def get_output_sections(self): + return sorted(self.placements.keys(), key=' '.join) -class SymbolNode(RuleNode): +class SymbolNode(EntityNode): - def __init__(self, parent, name, sections): - RuleNode.__init__(self, parent, name, sections) - self.entity = Entity(self.parent.parent.name, self.parent.name, self.name) - - def insert(self, entity, sections, target, sections_db): - self.add_sections(sections, target) - - def get_node_output_commands(self): - commands = collections.defaultdict(list) - - for sections in self.get_section_keys(): - info = self.sections[sections] - if info.explicit: - command = InputSectionDesc(Entity(self.parent.parent.name, self.parent.name), sections, []) - commands[info.target].append(command) - - return commands + def __init__(self, parent, name): + EntityNode.__init__(self, parent, name) + self.entity = Entity(self.parent.parent.name, self.parent.name) -class ObjectNode(RuleNode): +class ObjectNode(EntityNode): - def __init__(self, parent, name, sections): - RuleNode.__init__(self, parent, name, sections) - self.child_node = SymbolNode - self.expanded_sections = dict() + def __init__(self, parent, name): + EntityNode.__init__(self, parent, name) + self.child_t = SymbolNode self.entity = Entity(self.parent.name, self.name) + self.subplacements = list() - def add_node_child(self, entity, sections, target, sections_db): - if self.sections[sections].target != target: - symbol = entity.symbol - match_sections = None - - obj_sections = sections_db.get_sections(self.parent.name, self.name) + def child_placement(self, entity, sections, target, flags, sections_db): + child = self.add_child(entity) + sym_placement = Placement(child, sections, target, flags, True, dryrun=True) + # The basis placement for sym_placement can either be + # an existing placement on this node, or nonexistent. + if sym_placement.is_significant(): try: - match_sections = self.expanded_sections[sections] + obj_sections = self.placements[sections].sections except KeyError: - match_sections = [] + obj_sections = None + + if not obj_sections or obj_sections == sections: + # Expand this section for the first time + found_sections = sections_db.get_sections(self.parent.name, self.name) + obj_sections = [] for s in sections: - match_sections.extend(fnmatch.filter(obj_sections, s)) + obj_sections.extend(fnmatch.filter(found_sections, s)) - if match_sections: + if obj_sections: + symbol = entity.symbol remove_sections = [s.replace('.*', '.%s' % symbol) for s in sections if '.*' in s] - filtered_sections = [s for s in match_sections if s not in remove_sections] + filtered_sections = [s for s in obj_sections if s not in remove_sections] - if set(filtered_sections) != set(match_sections): # some sections removed - child = self.add_child(entity) - child.insert(entity, frozenset(remove_sections), target, obj_sections) + if set(filtered_sections) != set(obj_sections): + if sym_placement.basis: + subplace = False + try: + # If existing placement exists, make sure that + # it is emitted. + obj_placement = self.placements[sections] + except KeyError: + # Create intermediate placement. + obj_placement = self.self_placement(sections, sym_placement.basis.target, None, False) + if obj_placement.basis.flags: + subplace = True - # Remember the result for node command generation - self.expanded_sections[sections] = filtered_sections - self.exclude_from_parent(sections) + if subplace: + obj_placement.basis.add_subplacement(obj_placement) + self.subplacements.append(sections) + else: + obj_placement.force_significant() - def get_node_output_commands(self): - commands = collections.defaultdict(list) + obj_placement.sections = filtered_sections + sym_placement.basis = obj_placement - for sections in self.get_section_keys(): - info = self.sections[sections] + sym_placement.sections = remove_sections + child.placements[sections] = sym_placement - try: - match_sections = self.expanded_sections[sections] - except KeyError: - match_sections = [] - - if match_sections or info.explicit: - command_sections = match_sections if match_sections else sections - command = InputSectionDesc(self.entity, command_sections, []) - commands[info.target].append(command) - - return commands - - def exclude_from_parent(self, sections): - # Check if there is an explicit emmission for the parent node, which is an archive node. - # If there is, make the exclusion there. If not, make the exclusion on the root node. - # This is to avoid emitting unecessary command and exclusions for the archive node and - # from the root node, respectively. - if self.parent.sections[sections].explicit: - self.parent.add_exclusion(sections, self.entity) - else: - self.parent.parent.add_exclusion(sections, self.entity) + def get_output_sections(self): + output_sections = [key for key in self.placements if key not in self.subplacements] + return sorted(output_sections, key=' '.join) -class ArchiveNode(RuleNode): +class ArchiveNode(EntityNode): - def __init__(self, parent, name, sections): - RuleNode.__init__(self, parent, name, sections) - self.child_node = ObjectNode + def __init__(self, parent, name): + EntityNode.__init__(self, parent, name) + self.child_t = ObjectNode self.entity = Entity(self.name) -class RootNode(RuleNode): +class RootNode(EntityNode): def __init__(self): - RuleNode.__init__(self, None, Entity.ALL, None) - self.child_node = ArchiveNode + EntityNode.__init__(self, None, Entity.ALL) + self.child_t = ArchiveNode self.entity = Entity('*') - def insert(self, entity, sections, target, sections_db): - if self.entity.specificity == entity.specificity: - self.add_sections(sections, target) - else: - self.add_node_child(entity, sections, target, sections_db) - class Generation: """ - Implements generation of placement rules based on collected sections, scheme and mapping fragment. + Implements generation of placement based on collected sections, scheme and mapping fragment. """ - DEFAULT_SCHEME = 'default' - # Processed mapping, scheme and section entries - EntityMapping = namedtuple('EntityMapping', 'entity sections_group target') + EntityMapping = namedtuple('EntityMapping', 'entity sections_group target flags') def __init__(self, check_mappings=False, check_mapping_exceptions=None): self.schemes = {} - self.sections = {} + self.placements = {} self.mappings = {} self.check_mappings = check_mappings @@ -279,7 +315,7 @@ class Generation: else: self.check_mapping_exceptions = [] - def _build_scheme_dictionary(self): + def _prepare_scheme_dictionary(self): scheme_dictionary = collections.defaultdict(dict) # Collect sections into buckets based on target name @@ -292,7 +328,7 @@ class Generation: sections_in_bucket = sections_bucket[target_name] try: - sections = self.sections[sections_name] + sections = self.placements[sections_name] except KeyError: message = GenerationException.UNDEFINED_REFERENCE + " to sections '" + sections_name + "'." raise GenerationException(message, scheme) @@ -324,12 +360,13 @@ 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_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]) - def _generate_entity_mappings(self, scheme_dictionary, entities): - entity_mappings = [] + entity_mappings = dict() for mapping in self.mappings.values(): archive = mapping.archive @@ -345,45 +382,77 @@ class Generation: message = "'%s' not found" % str(entity) raise GenerationException(message, mapping) - # Create placement rule for each 'section -> target' in the scheme. - # - # For example. for the mapping entry: - # - # obj (scheme) - # - # The enumrated to: - # - # obj (section1 -> target1) - # obj (section2 -> target2) - # ... + if (obj, symbol, scheme_name) in mapping.flags.keys(): + flags = mapping.flags[(obj, symbol, scheme_name)] + # Check if all section->target defined in the current + # scheme. + for (s, t, f) in flags: + if (t not in scheme_dictionary[scheme_name].keys() or + s not in [_s.name for _s in scheme_dictionary[scheme_name][t]]): + + message = "%s->%s not defined in scheme '%s'" % (s, t, 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 section in sections: - entity_mappings.append(Generation.EntityMapping(entity, self.get_section_strs(section), target)) + # Find the applicable flags + _flags = [] - return entity_mappings + if flags: + for (s, t, f) in flags: + if (s, t) == (section.name, target): + _flags.extend(f) - def generate_rules(self, entities): - scheme_dictionary = self._build_scheme_dictionary() + sections_str = get_section_strs(section) - entity_mappings = self._generate_entity_mappings(scheme_dictionary, entities) + key = (entity, section.name) - entity_mappings.sort(key=lambda m: m.entity) + try: + existing = entity_mappings[key] + except KeyError: + existing = None - # Create root nodes dictionary for the default scheme, whose - # key is the target name and value is a list of the root nodes for that target. + if not existing: + entity_mappings[key] = Generation.EntityMapping(entity, sections_str, target, _flags) + else: + # Check for conflicts. + if (target != existing.target): + raise GenerationException('Sections mapped to multiple targets.', mapping) + + # Combine flags here if applicable, to simplify + # insertion logic. + 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) + elif (_flags == existing.flags): + pass + else: + raise GenerationException('Conflicting flags specified.', mapping) + + # 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 generate(self, entities): + scheme_dictionary = self._prepare_scheme_dictionary() + entity_mappings = self._prepare_entity_mappings(scheme_dictionary, entities) root_node = RootNode() - for (target, sections) in scheme_dictionary['default'].items(): - for section in sections: - root_node.insert(Entity(), self.get_section_strs(section), target, entities) - for mapping in entity_mappings: - (entity, sections, target) = mapping + (entity, sections, target, flags) = mapping try: - root_node.insert(entity, sections, target, entities) + root_node.insert(entity, sections, target, flags, entities) except ValueError as e: raise GenerationException(str(e)) - # Traverse the tree, creating the rules + # Traverse the tree, creating the placements commands = root_node.get_output_commands() return commands @@ -398,7 +467,7 @@ class Generation: if isinstance(fragment, Scheme): dict_to_append_to = self.schemes elif isinstance(fragment, Sections): - dict_to_append_to = self.sections + dict_to_append_to = self.placements else: dict_to_append_to = self.mappings diff --git a/tools/ldgen/ldgen.py b/tools/ldgen/ldgen.py index 381d7f53b9..a0a8ca4b5b 100755 --- a/tools/ldgen/ldgen.py +++ b/tools/ldgen/ldgen.py @@ -151,7 +151,7 @@ def main(): raise LdGenFailure('failed to parse %s\n%s' % (fragment_file.name, str(e))) generation_model.add_fragments_from_file(fragment_file) - mapping_rules = generation_model.generate_rules(sections_infos) + mapping_rules = generation_model.generate(sections_infos) script_model = LinkerScript(input_file) script_model.fill(mapping_rules) diff --git a/tools/ldgen/output_commands.py b/tools/ldgen/output_commands.py index effc94c106..4fb07485e3 100644 --- a/tools/ldgen/output_commands.py +++ b/tools/ldgen/output_commands.py @@ -17,9 +17,35 @@ from entity import Entity +class AlignAtAddress(): + + def __init__(self, alignment): + self.alignment = alignment + + def __str__(self): + return ('. = ALIGN(%d);' % self.alignment) + + def __eq__(self, other): + return (isinstance(other, AlignAtAddress) and + self.alignment == other.alignment) + + +class SymbolAtAddress(): + + def __init__(self, symbol): + self.symbol = symbol + + def __str__(self): + return ('%s = ABSOLUTE(.);' % self.symbol) + + def __eq__(self, other): + return (isinstance(other, SymbolAtAddress) and + self.symbol == other.symbol) + + class InputSectionDesc(): - def __init__(self, entity, sections, exclusions=None): + def __init__(self, entity, sections, exclusions=None, keep=False, sort=None): assert(entity.specificity != Entity.Specificity.SYMBOL) self.entity = entity @@ -34,7 +60,12 @@ class InputSectionDesc(): else: self.exclusions = set() + self.keep = keep + self.sort = sort + def __str__(self): + sections_string = '( )' + if self.sections: exclusion_strings = [] @@ -57,22 +88,48 @@ class InputSectionDesc(): for section in sorted(self.sections): section_strings.append(section) - sections_string = '(%s)' % ' '.join(section_strings) - else: - sections_string = '( )' + if self.sort: + if self.sort == (None, None): + pattern = 'SORT(%s)' + elif self.sort == ('name', None): + pattern = 'SORT_BY_NAME(%s)' + elif self.sort == ('alignment', None): + pattern = 'SORT_BY_ALIGNMENT(%s)' + elif self.sort == ('init_priority', None): + pattern = 'SORT_BY_INIT_PRIORITY(%s)' + elif self.sort == ('name', 'alignment'): + pattern = 'SORT_BY_NAME(SORT_BY_ALIGNMENT(%s))' + elif self.sort == ('alignment', 'name'): + pattern = 'SORT_BY_ALIGNMENT(SORT_BY_NAME(%s))' + elif self.sort == ('name', 'name'): + pattern = 'SORT_BY_NAME(SORT_BY_NAME(%s))' + elif self.sort == ('alignment', 'alignment'): + pattern = 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(%s))' + else: + raise Exception('Invalid sort arguments') - command = None + section_strings = [(pattern % s) for s in section_strings] + + sections_string = '(%s)' % ' '.join(section_strings) if self.entity.specificity == Entity.Specificity.NONE: - command = '*%s' % (sections_string) + entry = '*%s' % (sections_string) elif self.entity.specificity == Entity.Specificity.ARCHIVE: - command = '*%s:%s' % (self.entity.archive, sections_string) + entry = '*%s:%s' % (self.entity.archive, sections_string) else: - command = '*%s:%s.*%s' % (self.entity.archive, self.entity.obj, sections_string) + entry = '*%s:%s.*%s' % (self.entity.archive, self.entity.obj, sections_string) - return command + if self.keep: + res = 'KEEP(%s)' % entry + else: + res = entry + + return res def __eq__(self, other): - return (self.entity == other.entity and + return (isinstance(other, InputSectionDesc) and + self.entity == other.entity and self.sections == other.sections and - self.exclusions == other.exclusions) + self.exclusions == other.exclusions and + self.keep == other.keep and + self.sort == other.sort) diff --git a/tools/ldgen/test/data/base.lf b/tools/ldgen/test/data/base.lf index 4d9bc48b73..4d2b71d006 100644 --- a/tools/ldgen/test/data/base.lf +++ b/tools/ldgen/test/data/base.lf @@ -82,3 +82,8 @@ entries: [scheme:noflash_data] entries: rodata -> dram0_data + +[mapping:default] +archive: * +entries: + * (default) diff --git a/tools/ldgen/test/test_fragments.py b/tools/ldgen/test/test_fragments.py index f9f19ce4a7..d844ae5fb4 100755 --- a/tools/ldgen/test/test_fragments.py +++ b/tools/ldgen/test/test_fragments.py @@ -23,11 +23,11 @@ from io import StringIO from pyparsing import ParseException, ParseFatalException, Word, alphanums try: - from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar + from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar, Mapping from sdkconfig import SDKConfig except ImportError: sys.path.append('../') - from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar + from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar, Mapping from sdkconfig import SDKConfig @@ -811,6 +811,200 @@ entries: with self.assertRaises(ParseException): FragmentFile(test_fragment, self.sdkconfig) + def test_keep_flag(self): + # Test parsing combinations and orders of flags + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default); + text->flash_text KEEP, + rodata->flash_rodata KEEP KEEP +""") + fragment_file = FragmentFile(test_fragment, self.sdkconfig) + + fragment = fragment_file.fragments[0] + + expected = [('text', 'flash_text', [Mapping.Keep()]), + ('rodata', 'flash_rodata', [Mapping.Keep(), Mapping.Keep()])] + actual = fragment.flags[('obj1', None, 'default')] + + self.assertEqual(expected, actual) + + def test_align_flag(self): + # Test parsing combinations and orders of flags + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default); + text->flash_text ALIGN(8), + rodata->flash_rodata ALIGN(8, pre), + data->dram0_data ALIGN(8, pre, post), + bss->dram0_bss ALIGN(8, post), + common->dram0_bss ALIGN(8, pre, post) ALIGN(8) +""") + + fragment_file = FragmentFile(test_fragment, self.sdkconfig) + fragment = fragment_file.fragments[0] + + expected = [('text', 'flash_text', [Mapping.Align(8, True, False)]), + ('rodata', 'flash_rodata', [Mapping.Align(8, True, False)]), + ('data', 'dram0_data', [Mapping.Align(8, True, True)]), + ('bss', 'dram0_bss', [Mapping.Align(8, False, True)]), + ('common', 'dram0_bss', [Mapping.Align(8, True, True), Mapping.Align(8, True, False)])] + actual = fragment.flags[('obj1', None, 'default')] + + self.assertEqual(expected, actual) + + # Wrong post, pre order + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (noflash) + text->iram0_text ALIGN(8, post, pre) +""") + + with self.assertRaises(ParseFatalException): + FragmentFile(test_fragment, self.sdkconfig) + + def test_sort_flag(self): + # Test parsing combinations and orders of flags + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default); + text->flash_text SORT(name), + rodata->flash_rodata SORT(alignment), + data->dram0_data SORT(init_priority), + bss->dram0_bss SORT(name, alignment), + common->dram0_bss SORT(alignment, name), + iram->iram0_text SORT(name, name), + dram->dram0_data SORT(alignment, alignment) +""") + + fragment_file = FragmentFile(test_fragment, self.sdkconfig) + fragment = fragment_file.fragments[0] + + expected = [('text', 'flash_text', [Mapping.Sort('name')]), + ('rodata', 'flash_rodata', [Mapping.Sort('alignment')]), + ('data', 'dram0_data', [Mapping.Sort('init_priority')]), + ('bss', 'dram0_bss', [Mapping.Sort('name', 'alignment')]), + ('common', 'dram0_bss', [Mapping.Sort('alignment', 'name')]), + ('iram', 'iram0_text', [Mapping.Sort('name', 'name')]), + ('dram', 'dram0_data', [Mapping.Sort('alignment', 'alignment')])] + actual = fragment.flags[('obj1', None, 'default')] + self.assertEqual(expected, actual) + + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default) + text->iram0_text SORT(name) SORT(alignment) +""") + + def test_surround_flag(self): + # Test parsing combinations and orders of flags + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default); + text->flash_text SURROUND(sym1) +""") + + fragment_file = FragmentFile(test_fragment, self.sdkconfig) + fragment = fragment_file.fragments[0] + + expected = [('text', 'flash_text', [Mapping.Surround('sym1')])] + actual = fragment.flags[('obj1', None, 'default')] + self.assertEqual(expected, actual) + + def test_flag_order(self): + # Test that the order in which the flags are specified is retained + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default); + text->flash_text ALIGN(4) KEEP SURROUND(sym1) ALIGN(8) SORT(name), + rodata->flash_rodata KEEP ALIGN(4) KEEP SURROUND(sym1) ALIGN(8) ALIGN(4) SORT(name) +""") + fragment_file = FragmentFile(test_fragment, self.sdkconfig) + fragment = fragment_file.fragments[0] + + expected = [('text', 'flash_text', [Mapping.Align(4, True, False), + Mapping.Keep(), + Mapping.Surround('sym1'), + Mapping.Align(8, True, False), + Mapping.Sort('name')]), + ('rodata', 'flash_rodata', [Mapping.Keep(), + Mapping.Align(4, True, False), + Mapping.Keep(), + Mapping.Surround('sym1'), + Mapping.Align(8, True, False), + Mapping.Align(4, True, False), + Mapping.Sort('name')])] + actual = fragment.flags[('obj1', None, 'default')] + self.assertEqual(expected, actual) + + def test_flags_entries_multiple_flags(self): + # Not an error, generation step handles this, since + # it that step has a more complete information + # about all mappings. + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default); + text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name), + text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name) +""") + fragment_file = FragmentFile(test_fragment, self.sdkconfig) + fragment = fragment_file.fragments[0] + + expected = [('text', 'flash_text', [Mapping.Align(4, True, False), + Mapping.Keep(), + Mapping.Surround('sym1'), + Mapping.Sort('name')]), + ('text', 'flash_text', [Mapping.Align(4, True, False), + Mapping.Keep(), + Mapping.Surround('sym1'), + Mapping.Sort('name')])] + actual = fragment.flags[('obj1', None, 'default')] + self.assertEqual(expected, actual) + + def test_flags_entries_multiple_flags_and_entries(self): + # Not an error, generation step handles this, since + # it that step has a more complete information + # about all mappings. This can happen across multiple + # mapping fragments. + test_fragment = self.create_fragment_file(u""" +[mapping:map] +archive: libmain.a +entries: + obj1 (default); + text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name) + obj1 (default); + text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name) +""") + fragment_file = FragmentFile(test_fragment, self.sdkconfig) + fragment = fragment_file.fragments[0] + + expected = [('text', 'flash_text', [Mapping.Align(4, True, False), + Mapping.Keep(), + Mapping.Surround('sym1'), + Mapping.Sort('name')]), + ('text', 'flash_text', [Mapping.Align(4, True, False), + Mapping.Keep(), + Mapping.Surround('sym1'), + Mapping.Sort('name')])] + actual = fragment.flags[('obj1', None, 'default')] + self.assertEqual(expected, actual) + class DeprecatedMappingTest(FragmentTest): diff --git a/tools/ldgen/test/test_generation.py b/tools/ldgen/test/test_generation.py index 575105a848..5a2fd2e920 100755 --- a/tools/ldgen/test/test_generation.py +++ b/tools/ldgen/test/test_generation.py @@ -33,7 +33,7 @@ from io import StringIO from entity import Entity, EntityDB from fragments import FragmentFile from linker_script import LinkerScript -from output_commands import InputSectionDesc +from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress from sdkconfig import SDKConfig ROOT = Entity('*') @@ -120,29 +120,30 @@ class GenerationTest(unittest.TestCase): self.assertEqual(set(expected.keys()), set(actual.keys())) for target in sorted(actual.keys()): + message = 'failed target %s' % target a_cmds = actual[target] e_cmds = expected[target] - self.assertEqual(len(a_cmds), len(e_cmds)) + self.assertEqual(len(a_cmds), len(e_cmds), message) for a, e in zip(a_cmds, e_cmds): - self.assertEqual(a, e) + self.assertEqual(a, e, message) def get_default(self, target, rules): return rules[target][0] + +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 # are defined. - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() self.compare_rules(expected, actual) - -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. @@ -249,7 +250,7 @@ entries: * (noflash) #1 """ self.add_fragments(alt if alt else mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -287,7 +288,7 @@ entries: """ self.add_fragments(alt if alt else mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -326,7 +327,7 @@ entries: croutine:prvCheckPendingReadyList (noflash) #1 """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -382,7 +383,7 @@ entries: """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -443,7 +444,7 @@ entries: croutine:prvCheckPendingReadyList (default) #2 """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -512,7 +513,7 @@ entries: """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -606,7 +607,7 @@ entries: """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -634,6 +635,40 @@ entries: self.compare_rules(expected, actual) + def test_root_mapping_fragment(self): + # Test creation of a mapping fragment that maps '*'. + # This should generate another default command in iram0_text: + # + # iram0_text + # * (.custom_section) A + # * (.iram .iram.*) + mapping = u""" +[sections:custom_section] +entries: + .custom_section + +[scheme:custom_scheme] +entries: + custom_section -> iram0_text + +[mapping:default2] +archive: * +entries: + * (custom_scheme) #1 +""" + + self.add_fragments(mapping) + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + # Generate default command A + # Since these are the same 'specificity', the commands + # are arranged alphabetically. + expected['iram0_text'].append(expected['iram0_text'][0]) + expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], []) + + self.compare_rules(expected, actual) + class AdvancedTest(GenerationTest): @@ -671,7 +706,7 @@ entries: croutine (noflash_data) #2 """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -725,7 +760,7 @@ entries: croutine (noflash_data) #2 """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -764,7 +799,7 @@ entries: self.add_fragments(alt if alt else mapping) with self.assertRaises(GenerationException): - self.generation.generate_rules(self.entities) + self.generation.generate(self.entities) def test_complex_mapping_case(self, alt=None): # Test a complex case where an object is mapped using @@ -818,7 +853,7 @@ entries: """ self.add_fragments(alt if alt else mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -907,7 +942,7 @@ entries: """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -1004,7 +1039,7 @@ entries: """ self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -1046,7 +1081,7 @@ entries: self.add_fragments(mapping) with self.assertRaises(GenerationException): - self.generation.generate_rules(self.entities) + self.generation.generate(self.entities) def test_disambiguated_obj(self): # Test command generation for disambiguated entry. Should produce similar @@ -1059,7 +1094,7 @@ entries: """ port = Entity('libfreertos.a', 'port.c') self.add_fragments(mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() flash_text = expected['flash_text'] @@ -1085,6 +1120,60 @@ entries: self.compare_rules(expected, actual) + def test_root_mapping_fragment_conflict(self): + # Test that root mapping fragments are also checked for + # conflicts. + # + # 'custom_scheme' entries conflict the 'default' scheme + # entries. + mapping = u""" +[scheme:custom_scheme] +entries: + flash_text -> iram0_text + +[mapping:default2] +archive: * +entries: + * (custom_scheme) +""" + + self.add_fragments(mapping) + with self.assertRaises(GenerationException): + self.generation.generate(self.entities) + + def test_root_mapping_fragment_duplicate(self): + # Same root mappings have no effect. + # + # custom_scheme has the 'iram -> iram0_text' in common with + # default scheme + mapping = u""" +[sections:custom_section] +entries: + .custom_section + +[scheme:custom_scheme] +entries: + iram -> iram0_text + custom_section -> iram0_text + +[mapping:default2] +archive: * +entries: + * (custom_scheme) +""" + + self.add_fragments(mapping) + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + # Generate default command A + # Since these are the same 'specificity', the commands + # are arranged alphabetically. + expected['iram0_text'].append(expected['iram0_text'][0]) + expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], []) + + self.compare_rules(expected, actual) + class ConfigTest(GenerationTest): # Test command generation with conditions @@ -1119,7 +1208,7 @@ entries: self.add_fragments(scheme) self.add_fragments(alt if alt else mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() if perf >= 1: @@ -1145,6 +1234,11 @@ entries: # Test that proper commands are generated # in conditional mapping entries. mapping = u""" +[mapping:default] +archive: * +entries: + * (default) + [mapping:test] archive: lib.a entries: @@ -1165,7 +1259,7 @@ entries: self.generation.mappings = {} self.add_fragments(alt if alt else mapping) - actual = self.generation.generate_rules(self.entities) + actual = self.generation.generate(self.entities) expected = self.generate_default_rules() if perf_level < 4 and perf_level > 0: @@ -1208,6 +1302,11 @@ entries: def test_conditional_entries_legacy_mapping_fragment(self): # Test conditional entries on legacy mapping fragment grammar. mapping = u""" +[mapping:default] +archive: * +entries: + * (default) + [mapping] archive: lib.a entries: @@ -1228,6 +1327,11 @@ entries: # Test conditional entries on legacy mapping fragment grammar # across multiple fragments. mapping = u""" +[mapping:default] +archive: * +entries: + * (default) + [mapping] archive: lib.a entries: @@ -1257,6 +1361,11 @@ entries: # Test conditional entries on new mapping fragment grammar. # across multiple fragments. mapping = u""" +[mapping:default] +archive: * +entries: + * (default) + [mapping:base] archive: lib.a entries: @@ -1282,5 +1391,442 @@ entries: self.test_conditional_mapping(mapping) +class FlagTest(GenerationTest): + + # Test correct generation of mapping fragment entries + # with flags. + + def test_flags_basics(self): + # Test that input section commands additions are done (KEEP, SORT). + # Test that order dependent commands are properly generated (ALIGN, SURROUND) + # Normally, if an entry has the same mapping as parent, commands. + # are not emitted for them. However, if there are flags, they should be - + # only for the scheme entries that have flags, though. + # Flag entries split across multiple entries work. + # + # flash_text + # *((EXCLUDE_FILE(libfreertos:timers libfreertos:croutine).text ...) A + # KEEP(* (SORT_BY_NAME(EXCLUDE_FILE(libfreertos:timers).text) ...) B + # + # flash_rodata + # *((EXCLUDE_FILE(libfreertos:timers) .rodata ...) C + # _sym2_start D.1 + # . = ALIGN(4) E.1 + # KEEP(* (EXCLUDE_FILE(libfreertos:timers) .rodata ...) F + # _sym2_end D.2 + # . = ALIGN(4) E.2 + # + # iram0_text + # *(.iram .iram.*) + # . = ALIGN(4) G.1 + # _sym1_start H.1 + # libfreertos.a:croutine(.text .literal ...) I + # . = ALIGN(4) G.2 + # _sym1_end H.2 + mapping = u""" +[mapping:test] +archive: libfreertos.a +entries: + croutine (noflash_text); + text->iram0_text ALIGN(4, pre, post) SURROUND(sym1) #1 + timers (default); + text->flash_text KEEP SORT(name) #2 + timers (default); + rodata->flash_rodata SURROUND(sym2) ALIGN(4, pre, post) #3 +""" + + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + iram0_text = expected['iram0_text'] + flash_rodata = expected['flash_rodata'] + + # Exclusions in flash_text for timers and croutine A + flash_text[0].exclusions.add(CROUTINE) + flash_text[0].exclusions.add(TIMERS) + + # Command for #3 B + flash_text.append(InputSectionDesc(TIMERS, flash_text[0].sections, [], keep=True, sort=('name', None))) + + # Exclusions in flash_rodata for timers C + flash_rodata[0].exclusions.add(TIMERS) + + # Commands for #3 D.1, E.1, F, D.2, E.2 + flash_rodata.append(SymbolAtAddress('_sym2_start')) + flash_rodata.append(AlignAtAddress(4)) + flash_rodata.append(InputSectionDesc(TIMERS, flash_rodata[0].sections, [])) + flash_rodata.append(SymbolAtAddress('_sym2_end')) + flash_rodata.append(AlignAtAddress(4)) + + # Commands for # G.1, H.1, I, G.2, H.2 + iram0_text.append(AlignAtAddress(4)) + iram0_text.append(SymbolAtAddress('_sym1_start')) + iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, [])) + iram0_text.append(AlignAtAddress(4)) + iram0_text.append(SymbolAtAddress('_sym1_end')) + + self.compare_rules(expected, actual) + + def test_flags_intermediate_exclusion_command_root(self): + # Test that intermediate exclusion commands from root-level commands + # are included in the flags. + # + # flash_text + # _sym1_start A.1 + # KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B + # KEEP(libfreertos.a:croutine(...))) C + # _sym1_end A.2 + # + # iram0_text + # *(.iram .iram.*) + # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D + mapping = u""" +[mapping:default] +archive: * +entries: + # 1 + * (default); + text->flash_text SURROUND(sym1) KEEP #2 + +[mapping:test] +archive: libfreertos.a +entries: + croutine:prvCheckPendingReadyList (noflash_text) #3 +""" + + self.generation.mappings = {} + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + iram0_text = expected['iram0_text'] + + # Command for #2, pre A.1 + flash_text.insert(0, SymbolAtAddress('_sym1_start')) + + # Command for #1 with KEEP B + # and exclusion for #3 + flash_text[1].keep = True + flash_text[1].exclusions.add(CROUTINE) + + # Implicit exclusion command for #3 C + croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') + filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') + filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) + + filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] + filtered_sections.append('.text') + flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True)) + + # Command for #2, post A.2 + flash_text.append(SymbolAtAddress('_sym1_end')) + + # Command for #3 D + iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + + self.compare_rules(expected, actual) + + def test_flags_intermediate_exclusion_command_lib(self): + # Test that intermediate exclusion commands from lib-level commands + # are included in the flags. + # + # flash_text + # *(EXCLUDE_FILE(libfreertos.a).text ...) + # _sym1_start A.1 + # KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B + # KEEP(libfreertos.a:croutine(...))) C + # _sym1_end A.2 + # + # iram0_text + # *(.iram .iram.*) + # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D + mapping = u""" +[mapping:test] +archive: libfreertos.a +entries: + # 1 + * (default); + text->flash_text SURROUND(sym1) KEEP #2 + croutine:prvCheckPendingReadyList (noflash_text) #3 +""" + + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + iram0_text = expected['iram0_text'] + + # Command for #2, pre A.1 + flash_text.append(SymbolAtAddress('_sym1_start')) + flash_text[0].exclusions.add(FREERTOS) + + # Command for #1 with KEEP B + # and exclusion for #3 + flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True)) + + # Implicit exclusion command for #3 C + croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') + filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') + filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) + + filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] + filtered_sections.append('.text') + flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True)) + + # Command for #2, post A.2 + flash_text.append(SymbolAtAddress('_sym1_end')) + + # Command for #3 C + iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + + self.compare_rules(expected, actual) + + def test_flags_intermediate_exclusion_command_obj(self): + # Test that intermediate exclusion commands from obj-level commands + # are included in the flags. + # + # flash_text + # *(EXCLUDE_FILE(libfreertos.a).text ...) + # _sym1_start A.1 + # KEEP(libfreertos.a:croutine(...))) B + # _sym1_end A.2 + # + # iram0_text + # *(.iram .iram.*) + # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) C + mapping = u""" +[mapping:test] +archive: libfreertos.a +entries: + # 1 + croutine (default); + text->flash_text SURROUND(sym1) KEEP #2 + croutine:prvCheckPendingReadyList (noflash_text) #3 +""" + + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + iram0_text = expected['iram0_text'] + + # Command for #2, pre A.1 + flash_text.append(SymbolAtAddress('_sym1_start')) + flash_text[0].exclusions.add(CROUTINE) + + # Implicit exclusion command for #3 B + croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') + filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') + filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) + + filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] + filtered_sections.append('.text') + flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True)) + + # Command for #2, post A.2 + flash_text.append(SymbolAtAddress('_sym1_end')) + + # Command for #3 C + iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + + self.compare_rules(expected, actual) + + def test_flags_separate_exclusion_command_if_explicit_root(self): + # Explicit commands are separated from the parent's flags. + # + # flash_text + # _sym1_start A.1 + # KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B + # _sym1_end A.2 + # KEEP(libfreertos.a:croutine(...))) C + # + # iram0_text + # *(.iram .iram.*) + # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D + mapping = u""" +[mapping:default] +archive: * +entries: + # 1 + * (default); + text->flash_text SURROUND(sym1) KEEP #2 + +[mapping:test] +archive: libfreertos.a +entries: + croutine (default) #3 + croutine:prvCheckPendingReadyList (noflash_text) #4 +""" + + self.generation.mappings = {} + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + iram0_text = expected['iram0_text'] + + # Command for #2, pre A.1 + flash_text.insert(0, SymbolAtAddress('_sym1_start')) + + # Command for #1 with KEEP B + # and exclusion for #3 + flash_text[1].keep = True + flash_text[1].exclusions.add(CROUTINE) + + # Command for #2, post A.2 + flash_text.append(SymbolAtAddress('_sym1_end')) + + # Command for #3 C + croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') + filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') + filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) + + filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] + filtered_sections.append('.text') + flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) + + # Command for #4 D + iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) + + self.compare_rules(expected, actual) + + def test_flags_separate_exclusion_command_if_explicit_lib(self): + # Explicit commands are separated from the parent's flags. + # + # flash_text + # *(EXCLUDE_FILE(libfreertos.a).text ...) + # _sym1_start A.1 + # KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B + # _sym1_end A.2 + # KEEP(libfreertos.a:croutine(...))) C + # + # iram0_text + # *(.iram .iram.*) + # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D + mapping = u""" +[mapping:test] +archive: libfreertos.a +entries: + # 1 + * (default); + text->flash_text SURROUND(sym1) KEEP + croutine (default) #2 + croutine:prvCheckPendingReadyList (noflash_text) #3 +""" + + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + iram0_text = expected['iram0_text'] + + # Command for #2, pre A.1 + flash_text.append(SymbolAtAddress('_sym1_start')) + flash_text[0].exclusions.add(FREERTOS) + + # Command for #1 with KEEP B + # and exclusion for #3 + flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True)) + + # Command for #2, post A.2 + flash_text.append(SymbolAtAddress('_sym1_end')) + + # Implicit exclusion command for #3 C + croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') + filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') + filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) + + filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] + filtered_sections.append('.text') + flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) + + # Command for #3 C + 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:default_add_flag] +archive: * +entries: + * (default); + text->flash_text KEEP +""" + + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + flash_text[0].keep = True + + self.compare_rules(expected, actual) + + def test_flags_flag_additions_duplicate(self): + # Test same flags added to same entity - these + # are ignored. + mapping = u""" +[mapping:default_add_flag_1] +archive: * +entries: + * (default); + text->flash_text KEEP + +[mapping:default_add_flag_2] +archive: * +entries: + * (default); + text->flash_text KEEP +""" + + self.add_fragments(mapping) + + actual = self.generation.generate(self.entities) + expected = self.generate_default_rules() + + flash_text = expected['flash_text'] + flash_text[0].keep = True + + self.compare_rules(expected, actual) + + def test_flags_flag_additions_conflict(self): + # Test condition where multiple fragments specifies flags + # to same entity - should generate exception. + mapping = u""" +[mapping:default_add_flag_1] +archive: * +entries: + * (default); + text->flash_text ALIGN(2) + +[mapping:default_add_flag_2] +archive: * +entries: + * (default); + text->flash_text SURROUND(sym1) +""" + self.add_fragments(mapping) + + with self.assertRaises(GenerationException): + self.generation.generate(self.entities) + + if __name__ == '__main__': unittest.main() diff --git a/tools/ldgen/test/test_output_commands.py b/tools/ldgen/test/test_output_commands.py index cf4cf97ec0..96effe52ec 100755 --- a/tools/ldgen/test/test_output_commands.py +++ b/tools/ldgen/test/test_output_commands.py @@ -19,10 +19,10 @@ import sys import unittest try: - from output_commands import InputSectionDesc + from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress except ImportError: sys.path.append('../') - from output_commands import InputSectionDesc + from output_commands import InputSectionDesc, SymbolAtAddress, AlignAtAddress from entity import Entity @@ -34,7 +34,7 @@ CROUTINE = Entity('libfreertos.a', 'croutine') class InputSectionDescTest(unittest.TestCase): - def test_output_00(self): + def test_catch_all_placement(self): # Test default (catch-all) command expected = '*(.literal .literal.* .text .text.*)' @@ -44,7 +44,7 @@ class InputSectionDescTest(unittest.TestCase): desc = InputSectionDesc(Entity(Entity.ALL), SECTIONS) self.assertEqual(expected, str(desc)) - def test_output_01(self): + def test_lib_placement(self): # Test library placement command expected = '*libfreertos.a:(.literal .literal.* .text .text.*)' @@ -60,7 +60,7 @@ class InputSectionDescTest(unittest.TestCase): desc = InputSectionDesc(Entity('libfreertos.a', Entity.ALL, Entity.ALL), SECTIONS) self.assertEqual(expected, str(desc)) - def test_output_02(self): + def test_obj_placement(self): # Test object placement command expected = '*libfreertos.a:croutine.*(.literal .literal.* .text .text.*)' @@ -79,7 +79,7 @@ class InputSectionDescTest(unittest.TestCase): desc = InputSectionDesc(Entity('libfreertos.a', 'croutine.c'), SECTIONS) self.assertEqual(expected, str(desc)) - def test_output_03(self): + def test_invalid_entity(self): # Invalid entity specification with self.assertRaises(AssertionError): InputSectionDesc(Entity('libfreertos.a', 'croutine', 'prvCheckPendingReadyList'), SECTIONS) @@ -90,7 +90,7 @@ class InputSectionDescTest(unittest.TestCase): with self.assertRaises(AssertionError): InputSectionDesc(Entity('libfreertos.a', 'croutine'), SECTIONS, [Entity('libfreertos.a', 'croutine', 'prvCheckPendingReadyList')]) - def test_output_04(self): + def test_exclusions(self): # Test exclusions # Library @@ -129,13 +129,114 @@ class InputSectionDescTest(unittest.TestCase): desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')]) self.assertEqual(expected, str(desc)) - def test_output_05(self): + def test_empty_sections(self): # Test empty sections expected = '*libfreertos.a:croutine.*( )' desc = InputSectionDesc(Entity('libfreertos.a', 'croutine'), []) self.assertEqual(expected, str(desc)) + def test_keep(self): + # Test KEEP + expected = 'KEEP(*libfreertos.a:croutine.*( ))' + + desc = InputSectionDesc(Entity('libfreertos.a', 'croutine'), [], keep=True) + self.assertEqual(expected, str(desc)) + + def test_sort(self): + # Test sort + expected = ('*libfreertos.a:croutine.*(' + 'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) ' + 'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) ' + 'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) ' + 'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=(None, None)) + self.assertEqual(expected, str(desc)) + + expected = ('*libfreertos.a:croutine.*(' + 'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) ' + 'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) ' + 'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) ' + 'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('name', None)) + self.assertEqual(expected, str(desc)) + + expected = ('*libfreertos.a:croutine.*(' + 'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) ' + 'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) ' + 'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) ' + 'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('alignment', None)) + self.assertEqual(expected, str(desc)) + + expected = ('*libfreertos.a:croutine.*(' + 'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) ' + 'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) ' + 'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) ' + 'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('init_priority', None)) + self.assertEqual(expected, str(desc)) + + expected = ('*libfreertos.a:croutine.*(' + 'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) ' + 'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) ' + 'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) ' + 'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('name', 'alignment')) + self.assertEqual(expected, str(desc)) + + expected = ('*libfreertos.a:croutine.*(' + 'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) ' + 'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) ' + 'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) ' + 'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('name', 'name')) + self.assertEqual(expected, str(desc)) + + expected = ('*libfreertos.a:croutine.*(' + 'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) ' + 'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) ' + 'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) ' + 'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('alignment', 'name')) + self.assertEqual(expected, str(desc)) + + expected = ('*libfreertos.a:croutine.*(' + 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) ' + 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) ' + 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) ' + 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))' + ')') + desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('alignment', 'alignment')) + self.assertEqual(expected, str(desc)) + + +class SymbolAtAddressTest(unittest.TestCase): + + def test_symbol(self): + symbol = 'test_symbol' + expected = '%s = ABSOLUTE(.);' % symbol + + desc = SymbolAtAddress(symbol) + self.assertEqual(expected, str(desc)) + + +class AlignAtAddressTest(unittest.TestCase): + + def test_align(self): + align = 8 + expected = '. = ALIGN(%d);' % 8 + + desc = AlignAtAddress(align) + self.assertEqual(expected, str(desc)) + if __name__ == '__main__': unittest.main() diff --git a/tools/test_apps/build_system/ldgen_test/check_placements.py b/tools/test_apps/build_system/ldgen_test/check_placements.py index 21e7ab7606..49577aea8f 100644 --- a/tools/test_apps/build_system/ldgen_test/check_placements.py +++ b/tools/test_apps/build_system/ldgen_test/check_placements.py @@ -21,7 +21,7 @@ import argparse import subprocess -from pyparsing import LineEnd, Literal, ParseException, SkipTo, Word, alphanums, hexnums +from pyparsing import LineEnd, LineStart, Literal, Optional, Word, alphanums, hexnums argparser = argparse.ArgumentParser() @@ -34,24 +34,38 @@ contents = subprocess.check_output([args.objdump, '-t', args.elf]).decode() def check_location(symbol, expected): - pattern = Word(alphanums + '._').setResultsName('actual') + Word(hexnums) + Literal(symbol) + LineEnd() - pattern = SkipTo(pattern) + pattern + pattern = (LineStart() + Word(hexnums).setResultsName('address') + + Optional(Word(alphanums, exact=1)) + + Optional(Word(alphanums,exact=1)) + + Word(alphanums + '._*').setResultsName('actual') + + Word(hexnums) + + Literal(symbol) + + LineEnd()) try: - results = pattern.parseString(contents) - except ParseException: - print("check placement fail: '%s' was not found" % (symbol)) - exit(1) + results = pattern.searchString(contents)[0] + except IndexError: + raise Exception("check placement fail: '%s' was not found" % (symbol)) if results.actual != expected: - print("check placement fail: '%s' was placed in '%s', not in '%s'" % (symbol, results.actual, expected)) - exit(1) + raise Exception("check placement fail: '%s' was placed in '%s', not in '%s'" % (symbol, results.actual, expected)) print("check placement pass: '%s' was successfully placed in '%s'" % (symbol, results.actual)) + return int(results.address, 16) # src1:func1 (noflash) - explicit mapping for func2 using 'rtc' scheme -check_location('func1', '.iram0.text') +# should have been dropped since it is unreferenced. +func1 = check_location('func1', '.iram0.text') + +sym1_start = check_location('_sym1_start', '*ABS*') +sym1_end = check_location('_sym1_end', '*ABS*') + +assert func1 >= sym1_start, 'check placement fail: func1 comes before __sym1_start' +assert func1 < sym1_end, 'check placement fail: func1 comes after __sym1_end' +assert sym1_start % 9 == 0, '_sym1_start is not aligned as specified in linker fragment' +assert sym1_end % 12 == 0, '_sym1_end is not aligned as specified in linker fragment' +print('check placement pass: _sym1_start < func1 < __sym1_end and alignments checked') # src1:func2 (rtc) - explicit mapping for func2 using 'rtc' scheme check_location('func2', '.rtc.text') @@ -60,5 +74,4 @@ check_location('func2', '.rtc.text') # mapped using a different scheme check_location('func3', '.flash.text') -# * (noflash) - no explicit mapping for src2 check_location('func4', '.iram0.text') diff --git a/tools/test_apps/build_system/ldgen_test/main/linker.lf b/tools/test_apps/build_system/ldgen_test/main/linker.lf index b467f814ae..6b9d0e32c2 100644 --- a/tools/test_apps/build_system/ldgen_test/main/linker.lf +++ b/tools/test_apps/build_system/ldgen_test/main/linker.lf @@ -3,5 +3,6 @@ archive: libmain.a entries: * (noflash) src1 (default) - src1:func1 (noflash) + src1:func1 (noflash); + text->iram0_text KEEP ALIGN(9) ALIGN(12, post) SURROUND(sym1) src1:func2 (rtc) diff --git a/tools/test_apps/build_system/ldgen_test/main/test_main.c b/tools/test_apps/build_system/ldgen_test/main/test_main.c index 37aaa6aecd..9ebb9689c6 100644 --- a/tools/test_apps/build_system/ldgen_test/main/test_main.c +++ b/tools/test_apps/build_system/ldgen_test/main/test_main.c @@ -1,13 +1,10 @@ -extern void func1(void); extern void func2(void); extern void func3(void); extern void func4(void); - void app_main(void) { - func1(); func2(); func3(); func4();