Hard Fault When __udivsi3 Calls Undefined Memory on MSPM0C1104

Summary

A hard fault occurs when the compiler‑generated call to the helper routine __udivsi3 jumps into an undefined memory region on the MSPM0C1104. The fault is triggered not by an illegal division (e.g., divide‑by‑zero) but by the linker placing the division helper in a memory area that is not marked as executable, causing the CPU to fetch an invalid instruction word (.word 0xfxxxxxxx) and raise a hard fault.

Root Cause

  • Missing or misplaced compiler runtime library: The GNU ARM compiler relies on libgcc for __udivsi3; if the library is not linked or is placed in a non‑executable section, the resolver points to random flash/ram data.
  • Incorrect linker script memory attributes: The FLASH region is defined as RX but the DEVICE_CONFIG or other custom regions may overlap the address where __udivsi3 ends up, stripping the execute permission.
  • Optimization‑level dependent code generation: Even with -O0 the compiler emits a call to __udivsi3; with -O2 it may inline or reuse the same helper, but if the helper is missing the fault appears identically.
  • Section placement due to -ffunction-sections / --gc-sections: The linker may discard the __udivsi3 object file if it believes the symbol is unused, leaving a dangling reference that resolves to zero‑filled or undefined memory.

Why This Happens in Real Systems

  • Real‑world firmware often mixes vendor SDK libraries, custom linker scripts, and aggressive garbage collection of sections.
  • The division helper is not referenced directly in source code, so static analysis tools may not flag it as a required symbol.
  • Cortex‑M0+ (armv6-m) lacks hardware division; the software routine must reside in executable memory, a requirement easily overlooked when defining custom memory regions for configuration or calibration data.
  • Toolchain upgrades or changes in spec files (e.g., switching from nosys.specs to nano.specs) can alter which libc/libgcc symbols are pulled in.

Real-World Impact

  • Intermittent hard faults that appear only after certain compiler/linker flag changes, making debugging difficult.
  • System watchdog resets or fault handlers mask the root cause, leading to prolonged field failures.
  • Wasted development time chasing “divide‑by‑zero” bugs when the actual problem is a missing executable symbol.
  • Potential safety‑critical consequences if the fault occurs in control loops or fault‑tolerant sections of the application.

Example or Code (if necessary and relevant)

volatile uint32_t test;
test = 24000000;
test /= 24000000;

How Senior Engineers Fix It

  • Verify that -lgcc (or the appropriate compiler runtime) appears on the linker command line; add it explicitly if using -nostdlib/-nostartfiles.
  • Inspect the linker script: ensure any custom RAM/ROM regions do not overlap the address range where __udivsi3 is placed, and that all code sections have the X (execute) attribute.
  • Use -Wl,--print-map or read the .map file to confirm __udivsi3 resides inside the FLASH (RX) region and not in a RW!x or data area.
  • Disable garbage collection for the runtime library (-Wl,--undefined=__udivsi3) or wrap the helper in a forced‑include object file.
  • Rebuild with -specs=nosys.specs (or ensure nano.specs still pulls in libgcc) and confirm the division helper is present in the final binary.
  • Add a defensive check in code (e.g., if (denom) …) only after confirming the helper is correctly linked, to avoid masking the real issue.

Why Juniors Miss It

  • They focus on the division operation itself and assume a hardware divide unit exists, overlooking the software helper requirement for Cortex‑M0+.
  • They trust that the default toolchain flags will always pull in needed runtime symbols, missing the effect of -nostdlib/-nostartfiles or custom spec files.
  • They overlook the linker map file, assuming the build succeeded if no compile‑time errors appear.
  • They attribute the hard fault to stack corruption or interrupt misconfiguration without verifying the program counter at fault time.

Leave a Comment