Where did my segfault go?

RMPR2 pts0 comments

Where did my segfault go? – Paul Mairo – The faster you fail, the faster you learn

Where did my segfault go?

The other day I was iterating on a small C program with entr:

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

hello happened to segfault, but entr showed me… nothing. No Segmentation<br>fault, no non-zero exit visible, just complete silence after the compile<br>output. Hmm, very weird. Let’s prefix with bash -c:

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

Still nothing. What about moving the command into a script:

#!/bin/bash<br>gcc -o hello hello.c && ./hello

And point entr to it:

ls hello.c | entr -s ./run.sh

./run.sh: line 2: 104465 Segmentation fault (core dumped) ./hello

There’s my segmentation fault!

The reason(s)

Thanks to superuser (remember that site we<br>used to use pre LLMs?) for pointing me in the right direction. The question is<br>not exactly the same, but it still helped me. At first, I thought the problem<br>was with entr because I could reproduce both with bash and fish.

Who actually prints “Segmentation fault”? Obviously not the crashing<br>program. It can’t since it’s dead. The message is printed by the shell when it<br>reaps a child that died from SIGSEGV. No parent shell, no message, simple<br>enough.

But wait, why does even the explicit bash -c 'gcc && ./hello' version stay silent?

Turns out bash likes to exec the command if it can. When you run bash -c<br>"some_command" and some_command is effectively the only thing to do, bash<br>doesn’t bother forking a new process for<br>it.<br>It just execves into it, replacing itself. It’s a nice optimization, invisible<br>99% of the time.

In the script version, entr runs ./run.sh as a child, and the script’s<br>shebang starts a fresh bash to interpret the file. That bash runs gcc,<br>forks for ./hello, waits, sees it died from SIGSEGV, and dutifully prints<br>Segmentation fault before exiting.

The follow-up

But then it occurred to me, maybe you don’t need a wrapper script after all, what<br>happens if I have the crashing command running in a subshell?

$ ls hello.c | entr -s "gcc -o hello hello.c && (./hello)"<br>hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello )

Yep, I don’t think I have ever been that satisfied to see a program segfaulting.<br>Here the parens force ./hello into a forked subshell, so bash can’t exec its<br>way out of being the parent. When the subshell dies from SIGSEGV, bash reaps<br>it and prints the message like normal.

I could have stopped there but I wanted a way to test my second hypothesis. What<br>if you just make sure the shell has something to do after the crashing command?

$ ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'"<br>bash: line 1: 109516 Segmentation fault (core dumped) ./hello

Victory!

Now I should probably get back to writing some C…

Written on July 11, 2026

hello bash entr segmentation fault segfault

Related Articles