Skip to content

Cortex-M non-XIP binary includes bss/noinit sections #72307

@gramsay0

Description

@gramsay0

This results in a larger binary than is actually necessary, which impacts bootloaders and firmware upgrade.

I tested with a Cortex-M board (nucleo_f756zg), although the same might occur on other architectures etc too.


To Reproduce

  • append CONFIG_XIP=n to "samples/hello_world/prj.conf"
  • build west build -b nucleo_f756zg zephyr/samples/hello_world

Off-topic...

This fails with a pretty horrific error message:

/tmp/ccseYthy.s: Assembler messages:
/tmp/ccseYthy.s:49: Error: missing expression
/tmp/ccseYthy.s:55: Error: missing expression

😕

I stumbled on this discussion #42459 which IMO incorrectly states this as "not a bug"...

Some boards/SoCs do this:

if !XIP
config FLASH_SIZE
	default 0
config FLASH_BASE_ADDRESS
	default 0
endif

which probably should just be the default in zephyr/arch/Kconfig if !XIP


Back on topic...

  • append CONFIG_FLASH_SIZE=0 and CONFIG_FLASH_BASE_ADDRESS=0 to "samples/hello_world/prj.conf"
  • build west build -b nucleo_f756zg zephyr/samples/hello_world
  • check the binary size: ls -l build/zephyr/zephyr.bin -> 20228
  • dump the elf segments:
/opt/zephyr-sdk-0.16.4/arm-zephyr-eabi/bin/arm-zephyr-eabi-readelf build/zephyr/zephyr.elf --segments

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x00341c 0x20013348 0x20013348 0x00008 0x00008 R   0x4
  LOAD           0x0000d4 0x20010000 0x20010000 0x03cd0 0x03ce0 RWE 0x4
  LOAD           0x003de0 0x20013ce0 0x20013ce0 0x000a2 0x01220 RW  0x40
  LOAD           0x003e82 0x20014f00 0x20014f00 0x00004 0x00004 R   0x1
  TLS            0x003938 0x20013864 0x20013864 0x00000 0x00004 R   0x4

 Section to Segment mapping:
  Segment Sections...
   00     .ARM.exidx 
   01     rom_start text .ARM.exidx initlevel device_area sw_isr_table rodata .ramfunc 
   02     datas device_states bss noinit 
   03     .last_section 
   04     tbss 

Note that .last_section is after bss noinit.

After digging through the linker script I found CONFIG_LINKER_LAST_SECTION_ID which can be disabled to remove this section.

  • append CONFIG_LINKER_LAST_SECTION_ID=n to "samples/hello_world/prj.conf"
  • build west build -b nucleo_f756zg zephyr/samples/hello_world
  • check the binary size: ls -l build/zephyr/zephyr.bin -> 15746

Now the binary is 4482 bytes smaller, as bss/noinit are correctly excluded from the binary.


Now try another sample for iterable sections.

  • append the previous learnings to "tests/misc/iterable_sections/prj.conf"
CONFIG_XIP=n
CONFIG_FLASH_SIZE=0
CONFIG_FLASH_BASE_ADDRESS=0
CONFIG_LINKER_LAST_SECTION_ID=n
  • build west build -b nucleo_f756zg zephyr/tests/misc/iterable_sections
  • check the binary size: ls -l build/zephyr/zephyr.bin -> 36540
  • dump the elf segments:
/opt/zephyr-sdk-0.16.4/arm-zephyr-eabi/bin/arm-zephyr-eabi-readelf build/zephyr/zephyr.elf --segments

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x005394 0x200152c0 0x200152c0 0x00008 0x00008 R   0x4
  LOAD           0x0000d4 0x20010000 0x20010000 0x076b0 0x076c0 RWE 0x4
  LOAD           0x0077c0 0x200176c0 0x200176c0 0x000e0 0x017c0 RW  0x40
  LOAD           0x0078a0 0x20018e80 0x20018e80 0x0003c 0x0003c R   0x4
  TLS            0x005914 0x20015840 0x20015840 0x00000 0x00004 R   0x4

 Section to Segment mapping:
  Segment Sections...
   00     .ARM.exidx 
   01     rom_start text .ARM.exidx initlevel device_area sw_isr_table ztest log_const_area rodata .ramfunc 
   02     datas device_states test_ram_area test_ram2_area test_ram_named_area test_ram_numeric_area ramn_alt_area bss noinit 
   03     test_rom_area test_rom2_area test_rom_named_area test_rom_numeric_area romn_alt_area 
   04     tbss

Note that all the test_rom_* sections are after after bss noinit.

The reason is that zephyr_linker_sources(SECTIONS ...) (as recommended in the iterable sections documentation) is after bss/noinit. IMO there should be a RODATA_SECTIONS for things like this.

  • for testing purposes just move #include <snippets-sections.ld> up in the linker file ("include/zephyr/arch/arm/cortex_m/scripts/linker.ld") to above bss/noinit
  • build west build -b nucleo_f756zg zephyr/tests/misc/iterable_sections
  • check the binary size: ls -l build/zephyr/zephyr.bin -> 30684

Now the binary is 5856 bytes smaller, as bss/noinit are correctly excluded from the binary.

Expected behavior

  • Setting CONFIG_XIP=n builds without error
  • bss/noinit are not included in the binary by default
  • bss/noinit are not included in the binary when using iterable sections

Impact
Minor

Environment

  • OS: Linux
  • Toolchain: Zephyr SDK 0.16.4
  • Commit SHA: 55c8a16 (current main)

BTW I'm happy to help with fixes etc if people agree that this should be tidied up

Metadata

Metadata

Labels

area: ARMARM (32-bit) ArchitecturebugThe issue is a bug, or the PR is fixing a bugpriority: mediumMedium impact/importance bug

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions