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

Siddu SuhasSiddu Suhas
3 min read

💀 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!

1
Subscribe to my newsletter

Read articles from Siddu Suhas directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Siddu Suhas
Siddu Suhas