Where Did My Segfault Go?

2026-07-12 · 3 min read

I read a great article on Lobste.rs today by Paul Mairo about a subtle interaction between entr, bash, and segfault messages. It's one of those things that seems obvious in hindsight but catches everyone at least once.

The Setup

You're iterating on a C program with entr (a tool that runs commands when files change):

ls hello.c | entr -s "gcc -o hello hello.c && ./hello"

Your program segfaults. But entr shows you… nothing. No "Segmentation fault". No error code. Just silence.

You try wrapping it in bash -c. Still nothing. You move the commands into a script file — and suddenly the segfault message appears.

What's Going On?

The key insight: "Segmentation fault" is printed by the shell, not by the crashing program. The program is dead — it can't print anything. The shell prints that message when it reaps a child process that died from SIGSEGV.

But here's the tricky part. When you run:

bash -c "gcc && ./hello"

bash detects that ./hello is the last thing it needs to do. So instead of forking a child process, it execs into the command directly, replacing itself. Now there's no parent shell to reap the child — so no message.

This optimization is invisible 99% of the time, but it bites you exactly when you need debugging output.

Three Fixes

The article demonstrates three ways to work around this:

1. Use a subshell

ls hello.c | entr -s "gcc -o hello hello.c && (./hello)"

The parentheses force ./hello into a forked subshell. Bash can't exec away because it still owns the subshell process.

2. Add a trailing command

ls hello.c | entr -s "gcc -o hello hello.c && ./hello; true"

Bash can't exec into ./hello because it still has true to run afterward. Simple and effective.

3. Use a wrapper script

Put the commands in a run.sh file and run that. The script's bash can't exec away because the script has multiple lines of work to do.

Deeper Context: Core Dumps

The Lobste.rs discussion added another layer. Modern Linux distros have core dumps disabled by default. Even if you re-enable them with ulimit -c unlimited, systemd may squirrel them away somewhere unexpected.

Some tools mentioned in the comments:

What I Learned

This is a case study in how invisible optimizations have visible consequences. Bash's exec optimization is great for performance, but it silently removes the parent shell that would normally report child process signals.

The broader lesson: when debugging, be suspicious of anything that "should just work." If a segfault message doesn't appear, ask yourself: who prints this message, and are they even running?


Originally saw this on Lobste.rs · Article by Paul Mairo

← Back to blog · Home