There seem to be a lot of posts assuming that the set fault is to do with some C++ feature. The magic here is the flag telling the compiler not to add files like crtbegin.o to the link step. These files contain the real program entry point in most programs. They do things like stashing ELF auxiliary arguments for libc, performing any dynamic relocations that are needed for a statically linked binary, and then they call main. On return from main, they call exit with the return value. If you do not provide them, you end up with a binary that will start executing at the first function that it provides. When this function returns, it will jump to the return address, which will be 0 on most RISC architectures. On x86, it will pop some chunk of kernel-provided argument data (argc, if I remember the calling convention correctly) off the stack and jump there, so in this invocation it will jump to address 1. In both cases, this will jump to an unmapped page and crash. If I remember correctly, doing this on DOS would jump to the reset vector and reboot.
I’ve heard of at least one app that did some multi hour CPU heavy calculation in a global object’s constructor. All before WinMain(). This was on Windows, where doing that has some consequences. The message pump never gets run so if a user accidentally interacts with it in any way (I think it popped a blank window up or something), Windows decides it isn’t responding to messages (true) and then pesters the human every 30 seconds to ask if they would like to stop the clearly crashed / infinite looping program now.
(Side-rant: This is why having modules or globals with implicit initialization of some kind is almost always a mistake. You think it starts simple, but running any arbitrary code at module init time sooner or later causes bonkers things like this. Rust’s lazy_static and such things are kind of painful, but semi-dodge the spooky-action-at-a-distance, because all the action at least starts from a specific point.)
There seem to be a lot of posts assuming that the set fault is to do with some C++ feature. The magic here is the flag telling the compiler not to add files like crtbegin.o to the link step. These files contain the real program entry point in most programs. They do things like stashing ELF auxiliary arguments for libc, performing any dynamic relocations that are needed for a statically linked binary, and then they call main. On return from main, they call exit with the return value. If you do not provide them, you end up with a binary that will start executing at the first function that it provides. When this function returns, it will jump to the return address, which will be 0 on most RISC architectures. On x86, it will pop some chunk of kernel-provided argument data (argc, if I remember the calling convention correctly) off the stack and jump there, so in this invocation it will jump to address 1. In both cases, this will jump to an unmapped page and crash. If I remember correctly, doing this on DOS would jump to the reset vector and reboot.
How about this C? It does have a main(), but with empty body!
That’s not allowed per C89 (no variable length arrays). It may be legal under C99 (at least, GCC and clang accept it as C99) but … ew.
You don’t even need to put effort to make C++ code Segfault. As mentioned, this is glorious!
I’ve heard of at least one app that did some multi hour CPU heavy calculation in a global object’s constructor. All before WinMain(). This was on Windows, where doing that has some consequences. The message pump never gets run so if a user accidentally interacts with it in any way (I think it popped a blank window up or something), Windows decides it isn’t responding to messages (true) and then pesters the human every 30 seconds to ask if they would like to stop the clearly crashed / infinite looping program now.
Glorious.
(Side-rant: This is why having modules or globals with implicit initialization of some kind is almost always a mistake. You think it starts simple, but running any arbitrary code at module init time sooner or later causes bonkers things like this. Rust’s
lazy_static
and such things are kind of painful, but semi-dodge the spooky-action-at-a-distance, because all the action at least starts from a specific point.)It’s all fine since standard in section
basic.start.main
requires existence of globalmain
function. This makes that snipet an illegal program! 🙃And if you call
_exit(0)
after thewrite()
, it seems to exit without a segfault, even! 🤯