Skip to content

fix(linker): correct cross-toolchain memory map inconsistencies#38

Open
94xhn wants to merge 1 commit into
STMicroelectronics:mainfrom
94xhn:fix/linker-cross-toolchain-audit
Open

fix(linker): correct cross-toolchain memory map inconsistencies#38
94xhn wants to merge 1 commit into
STMicroelectronics:mainfrom
94xhn:fix/linker-cross-toolchain-audit

Conversation

@94xhn

@94xhn 94xhn commented Jul 11, 2026

Copy link
Copy Markdown

Summary

This PR cross-checks the three toolchain-specific memory descriptions that exist side by side for every project in this repo — GCC (STM32CubeIDE/*.ld), IAR (EWARM/*.icf) and Keil (MDK-ARM/*.uvprojx, OCR_RVCTx fields) — against each other and against each board's real Flash/RAM capacity (cross-referenced from that board's own Templates/TrustZoneDisabled project). Because these three descriptions are maintained independently, they can drift apart; this PR fixes 8 confirmed drift bugs across 6 boards where the declared memory region for at least one toolchain either exceeded the device's physical capacity or otherwise disagreed with the other two toolchains and with sibling examples on the same board.

Findings and fixes

1. Projects/STM32H5F5J-DK/Applications/OpenBootloader
GCC's STM32CubeIDE/STM32H5F5LJHXQ_FLASH.ld reserved 90K Flash for the bootloader. IAR's EWARM/stm32h5f5xx_flash.icf and Keil's enabled OCR_RVCT4 both reserve 70K (0x11800). 70K also matches the OpenBootloader footprint convention used elsewhere in ST's Cube ecosystem (e.g. STM32CubeU5/Projects/B-U585I-IOT02A/Applications/OpenBootloader also uses 70K on both GCC and IAR). Fixed GCC to 70K.

2. Projects/NUCLEO-H563ZI/Examples/FLASH/FLASH_SwapBanks
GCC's .ld declared the full 2048K (2MB) Flash region for a single-bank swap image. IAR's .icf and Keil's enabled OCR_RVCT4 both correctly reserve 1024K (one bank of this 2MB dual-bank device). The analogous FLASH_SwapBanks example on NUCLEO-H5E5ZJ has all three toolchains agreeing on half the flash per bank, confirming the intended pattern. Fixed GCC to 1024K.

3. Projects/NUCLEO-H503RB/Examples/RAMCFG/RAMCFG_WriteProtection
GCC's .ld declared only 10K RAM. IAR's .icf and Keil's enabled OCR_RVCT9 both declare the full 32K RAM of this device. The identical example on sibling boards (NUCLEO-H533RE, NUCLEO-H563ZI) also declares the board's full RAM in its own GCC script, confirming 10K was a stray value with no basis in the source code (the test writes to a second SRAM bank via direct pointer access, not through the linker-managed heap/BSS, so there was never a design reason to shrink the linker's RAM region). Fixed GCC to 32K.

4. Projects/NUCLEO-H563ZI/Examples/RAMCFG/RAMCFG_ECC_Error_Generation
Same pattern as #3: GCC's .ld declared only 100K RAM vs the full 640K RAM that IAR/Keil declare and that this device actually has. The same example on NUCLEO-H533RE/NUCLEO-H5E5ZJ also declares full RAM in GCC. Fixed GCC to 640K.

5. Projects/STM32H573I-DK/Applications/FileX/FX_IAP/IAP_binary_template
This IAP application image links to start mid-flash at 0x08100000 (after a reserved bootloader region). GCC's .ld and Keil's enabled OCR_RVCT4 both declared a 2048K (2MB) length from that offset — extending to 0x08300000, which is 1MB past the device's actual flash end at 0x08200000 (this device has 2MB total flash starting at 0x08000000). Only IAR's .icf correctly bounds the region at 0x081FFFFF (1024K remaining). This is the classic "start address correctly offset, length not shrunk to match" IAP linker bug — and notably the majority (GCC + Keil) had it wrong while IAR (the minority) was correct, so this was caught by address arithmetic rather than majority vote. Fixed GCC and Keil to 1024K.

6. Projects/NUCLEO-H553ZG: 5 Keil applicationsApplications/FileX/Fx_File_Edit_Standalone, Applications/NetXDuo/Nx_UDP_Echo_Client, Applications/NetXDuo/Nx_UDP_Echo_Server, Applications/ThreadX/Tx_LowPower, Applications/ThreadX/Tx_Thread_Creation
All five .uvprojx files declare IROM/OCR_RVCT4 = 4096K Flash and IRAM/OCR_RVCT9 = 1536K RAM — this is the exact memory profile of a different, larger H5 device (matches STM32H5E5ZJ/STM32H5F5J precisely). The real NUCLEO-H553ZG device has 1024K Flash / 304K RAM, confirmed by this board's own Templates/TrustZoneDisabled, Templates_Board, Templates_LL projects and every one of its USBX examples (all correctly declare IROM=0x100000, IRAM=0x4c000), and matching what these same 5 applications' own GCC .ld files already declare. A Keil build of any of these 5 apps would link successfully against Flash/RAM addresses that simply do not exist on real NUCLEO-H553ZG hardware, which is exactly the "declared size exceeds physical capacity → link output is unsafe on real hardware" failure mode this audit targets. Corrected the Cpu device-map string, IRAM/IROM defaults, and OCR_RVCT4/OCR_RVCT9 in all 5 files to the verified 1024K/304K values.

7. Projects/STM32H573I-DK/Applications/NetXDuo: Nx_Iperf_wifi and Nx_Network_Basics_wifi
GCC's .ld and IAR's .icf both expose the device's full 640K RAM as one contiguous region. In these two Keil projects, the OCR_RVCT6/OCR_RVCT7 entries already describe the correct continuation banks (0x20040000+64K, 0x20050000+320K — together with OCR_RVCT9's 256K, these three exactly complete the device's 640K RAM) but Ra1Chk/Ra2Chk were left disabled, so Keil only ever linked against the first 256K bank. Every other NetXDuo example on this same board (Nx_MQTT_Client, Nx_TCP_Echo_Client, Nx_TCP_Echo_Server, Nx_WebServer) instead exposes the full 640K as a single IRAM entry and is unaffected — confirming these two are the outliers, not the norm. Enabled Ra1Chk/Ra2Chk in both files to restore the full 640K RAM, matching GCC/IAR and the sibling examples.

Not fixed (documented, left as-is)

Projects/STM32H573I-DK/Applications/OpenBootloader: GCC's .ld declares the full 2048K Flash / 640K RAM — apparently an un-customized copy of the generic Templates/TrustZoneDisabled linker script. IAR (64K Flash / 64K RAM) and Keil (128K Flash / 64K RAM) agree on RAM but disagree with each other on the Flash size. I could not find a reliable way to determine whether 64K or 128K is the intended bootloader Flash footprint without ST engineering input, and this project also carries a TrustZone-adjacent secure-alias OCR_RVCT5 entry, so I deliberately left this one untouched rather than guess. Flagging it here in case it's useful to a maintainer with more context.

Excluded as false positives

While auditing, several apparent mismatches from an automated first pass turned out, on manual inspection, to be intentional and correct:

  • OEMiROT application .icf files (NUCLEO-H503RB/Applications/ROT/OEMiROT_Appli, Templates_ROT/OEMiROT_Appli) define their ROM region via symbolic macro expressions (CODE_OFFSET, IMAGE_HEADER_SIZE, CODE_SIZE), not literal hex — not a bug, just not naively regex-parseable.
  • NUCLEO-H503RB/Applications/ThreadX/Tx_CMSIS_Wrapper ships both a normal Flash-execution .icf and an alternate SRAM-execution .icf (stm32h503xx_sram.icf) for a code-runs-from-RAM build variant — both are intentional.
  • NUCLEO-H5E5ZJ/Examples/FLASH/FLASH_SwapBanks and Templates/TrustZoneEnabled — bank-swap demos and secure/non-secure TrustZone splits where all three toolchains already agree with each other and correctly sum to the device's full flash; not a bug.

Test plan

No local ARM toolchain (GCC/IAR/Keil) was available to build/link these projects. All findings and fixes were verified by manual address arithmetic and cross-referencing against: (a) each board's own Templates/TrustZoneDisabled project (ground truth for real device Flash/RAM capacity), (b) sibling/identical examples on other boards of the same family, and (c) other unaffected projects on the same board, to distinguish genuine drift bugs from intentional non-standard memory layouts (bootloaders, bank-swap demos, TrustZone secure/non-secure splits). Every fix in this PR only reduces or corrects a declared memory region to match the other two toolchains and the verified physical device capacity — no region was shrunk below what the other two toolchains already require, and no non-standard/security-related memory layout was altered without independent corroborating evidence (see "Not fixed" above for the one case where I did not have enough confidence to act).

Disclosure

This audit and these fixes were carried out with Claude (Anthropic) as an AI coding assistant, following the same GCC/IAR/Keil cross-referencing methodology previously applied across several other STM32Cube repositories. Every finding above was individually investigated and manually verified (reading each project's actual linker/project files and cross-referencing against sibling projects and each device's real memory capacity) before being classified as a genuine bug and fixed; several other automated-tool mismatches were investigated and excluded as false positives (see above) rather than "fixed" for the sake of it. I (the submitter) have reviewed all the changes in this PR and take responsibility for their correctness.

Cross-checked GCC (STM32CubeIDE .ld), IAR (EWARM .icf) and Keil
(MDK-ARM .uvprojx OCR_RVCTx) memory declarations against each other
and against the true per-device Flash/RAM capacity (verified from each
board's own Templates/TrustZoneDisabled project). Fixed 8 confirmed
drift bugs across 6 boards:

1. STM32H5F5J-DK/Applications/OpenBootloader
   GCC .ld reserved 90K Flash for the bootloader while IAR .icf and the
   enabled Keil OCR_RVCT4 both reserve 70K (0x11800), matching the
   established OpenBootloader footprint convention used elsewhere in
   ST's Cube ecosystem (e.g. STM32CubeU5/B-U585I-IOT02A also uses 70K).
   Reduced GCC's declared length to 70K.

2. NUCLEO-H563ZI/Examples/FLASH/FLASH_SwapBanks
   GCC .ld declared the full 2048K (2MB) Flash region for a single bank
   image, while IAR .icf and the enabled Keil OCR_RVCT4 both correctly
   reserve only 1024K (one bank of the 2MB dual-bank device), matching
   the analogous FLASH_SwapBanks example on NUCLEO-H5E5ZJ where all
   three toolchains agree on half the flash per bank. Reduced GCC's
   ROM length to 1024K.

3. NUCLEO-H503RB/Examples/RAMCFG/RAMCFG_WriteProtection
   GCC .ld declared only 10K RAM while IAR .icf and Keil (enabled
   OCR_RVCT9) both declare the full 32K RAM available on this device.
   The equivalent example on sibling boards (NUCLEO-H533RE,
   NUCLEO-H563ZI) also declares the full per-device RAM in its GCC
   linker script, confirming 10K is a stray/incorrect value. Raised
   GCC's RAM length to 32K.

4. NUCLEO-H563ZI/Examples/RAMCFG/RAMCFG_ECC_Error_Generation
   Same pattern as STMicroelectronics#3: GCC .ld declared only 100K RAM while IAR/Keil
   declare the full 640K RAM of the device, and the same example on
   NUCLEO-H533RE/NUCLEO-H5E5ZJ declares full RAM in GCC too. Raised
   GCC's RAM length to 640K.

5. STM32H573I-DK/Applications/FileX/FX_IAP/IAP_binary_template
   This IAP application image is linked to start mid-flash at
   0x08100000 (after a bootloader region). GCC .ld and the enabled
   Keil OCR_RVCT4 both declared a 2048K (2MB) length from that offset,
   which extends to 0x08300000 -- 1MB past the device's actual flash
   end at 0x08200000 (2MB total flash from 0x08000000). Only IAR .icf
   correctly bounds the region at 0x081FFFFF (1024K remaining). This is
   the classic "offset correctly shifted, length not shrunk to match"
   IAP bug pattern. Reduced both GCC and Keil to 1024K.

6. NUCLEO-H553ZG: 5 Keil applications (Fx_File_Edit_Standalone,
   Nx_UDP_Echo_Client, Nx_UDP_Echo_Server, Tx_LowPower,
   Tx_Thread_Creation)
   These .uvprojx files declare IROM/OCR_RVCT4 = 4096K Flash and
   IRAM/OCR_RVCT9 = 1536K RAM -- the memory profile of a different,
   larger H5 device (matches STM32H5E5ZJ/H5F5J exactly). The actual
   NUCLEO-H553ZG device has 1024K Flash / 304K RAM, confirmed by this
   board's own Templates/TrustZoneDisabled, Templates_Board,
   Templates_LL and every USBX example on the same board (all
   correctly use IROM=0x100000, IRAM=0x4c000), and matching GCC's own
   .ld for these same 5 applications. A Keil build of any of these 5
   apps would link successfully against phantom Flash/RAM addresses
   that do not exist on the real NUCLEO-H553ZG hardware. Corrected the
   Cpu device-map string, IRAM/IROM defaults and OCR_RVCT4/OCR_RVCT9
   to the verified 1024K/304K values.

7. STM32H573I-DK: Nx_Iperf_wifi and Nx_Network_Basics_wifi
   GCC .ld and IAR .icf both expose the device's full 640K RAM as one
   contiguous region. In these two Keil projects the equivalent
   OCR_RVCT6/OCR_RVCT7 entries already describe the correct
   continuation banks (0x20040000+64K, 0x20050000+320K, together with
   OCR_RVCT9's 256K completing exactly 640K) but Ra1Chk/Ra2Chk were
   left disabled, so Keil only linked against the first 256K bank.
   Every other NetXDuo example on this same board (Nx_MQTT_Client,
   Nx_TCP_Echo_Client/Server, Nx_WebServer) instead exposes the full
   640K as a single IRAM entry and is unaffected. Enabled Ra1Chk/Ra2Chk
   to restore access to the full 640K RAM, matching GCC/IAR and the
   sibling examples.

Not fixed (documented, left as-is):
STM32H573I-DK/Applications/OpenBootloader: GCC .ld uses the full
2048K/640K (clearly an un-customized copy of the generic Template),
while IAR (64K Flash/64K RAM) and Keil (128K Flash/64K RAM) agree on
RAM but disagree with each other on the exact reserved Flash size.
Left untouched: no reliable way to determine whether 64K or 128K is
the intended bootloader Flash footprint without ST engineering input,
and this project touches the TrustZone-adjacent secure-alias OCR
entries, so an incorrect guess here carries more risk than benefit.

Signed-off-by: 94xhn <94xhn1@gmail.com>
Signed-off-by: 94xhn <87560781+94xhn@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant