Postmortem: A SIGSEGV Caused by Incomplete Rebuilds in a C Firmware Project

💀 Postmortem: A SIGSEGV Caused by Incomplete Rebuilds in a C Firmware Project
Recently encountered a persistent SIGSEGV
in one of our firmware builds. The bug only occurred on specific hardware after modifying some structure definitions.
Same codebase, same binary — yet inconsistent runtime behaviour.
🔍 Root Cause: Broken Dependency Tracking in the Makefile
The issue wasn’t in the logic — it was in the build system.
Like many C projects, our Makefile was manually written. But it didn’t track header dependencies. Specifically:
- There was no -MMD -MP
passed to the compiler.
- The Makefile didn’t include the generated .d
dependency files.
- The clean:
target only removed .o
files, not .d
or the final binary.
So when a .h
file changed, make
had no way of knowing which .c
files depended on it — and skipped recompiling them.
This left stale .o
files using outdated struct layouts, causing misaligned memory access and a SIGSEGV
at runtime.
💡 Diagnostic Clue
I modified a header (`.h`), ran make
, and observed that nothing was rebuilt — confirming the lack of header-to-source dependency tracking.
A full manual clean + rebuild fixed the issue, confirming that the crash was due to stale compiled objects, not logic bugs.
✅ The Fix: Enabling Proper Dependency Management
Here’s what I changed in the Makefile:
CFLAGS += -O0 -g -MMD -MP # Generate .d files for dependencies
-include $(OBJ:.o=.d) # Include those .d files so make can act on them
clean:
rm -f $(OBJ) $(TARGET) $(VERSION_FILE) *.d
Explanation:
-MMD tells the compiler to generate .d dependency files for each .c file.
-MP adds phony targets for each header to prevent build errors if a header is later removed.
The -include line makes make read those .d files and recompile .c files if their headers changed.
The updated clean: target removes stale .d files and the final binary too — ensuring future rebuilds are clean.
🧠 Takeaway
In C projects, modifying a header without proper dependency tracking is like playing roulette with your build.
Many Makefiles fail silently when .h changes don’t trigger recompilation. This is often invisible — until it causes a subtle memory bug, or worse, a crash in production.
If you’re not using -MMD -MP (or a build system like Automake/CMake that abstracts it), you’re at risk.
📚 References
💬 Got a Similar Story?
Have you ever been bitten by stale object files or Makefile traps? Share your debugging war stories in the comments!
Subscribe to my newsletter
Read articles from Siddu Suhas directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by