3 ms·
> When the program starts running, you might think it starts at main. It doesn’t! It actually goes to _start. This does a bunch of Very Important Things that I
by sva_ 3y ago
> When the program starts running, you might think it starts at main. It doesn’t! It actually goes to _start. This does a bunch of Very Important Things that I don’t understand very well, including calling main. So I won’t explain them.
The way I understand it, the symbol main is a C-specific thing. The symbol _start is a language-agnostic entry point for the binary that will in this case call main.
A convention of i.e. calling the entry point _start with main's argc/argv would make the format a lot less flexible.
- a-priori 3y agoTechnically the name _start is not special either. The binary lists its entry point address in a header and that’s where the OS starts execution from. That symbol is just called _start by convention by C and other languages, which is what the linker uses to set the entry point when writing the ELF headers, but if you’re writing your own linker scripts you could call the entry point whatever you want.
- Keyframe 3y agoto extend on it, _start is where .text begins and address of that is set by linker
- a-priori 3y agoThe entry point can be anywhere in the .text section, and often won't be at the beginning of the section.
- Retr0id 3y agoTechnically it doesn't even need to be in the .text section, it could be anywhere in the address space. You'll get a segfault if it's not somewhere executable though (assuming you're on a system with an appropriately configured MMU)
- Keyframe 3y agoyes and then you'll have a bad time, but at the same time per convention _start is where .text begins. You can see where it starts with readelf --file-header <executable> and look at Entry point address field. You can change it, yes.
- logdahl 3y agoA common hack to reduce ELF size is actually to start the first section (possibly the .text) right on the elf header, as this circumvents the alignment requirements.
- Keyframe 3y agoprobably not even mandatory.. lots of /usr/bin stuff on my ubuntu machine have __libc_start_main only
- Retr0id 3y agoNo, it's not even a convention, _start is most commonly not where .text begins. Compiling a static hello world binary on my system (aarch64 fedora 39, gcc -static hello.c -o hello), .text starts at 0x410080, e_entry is at 0x4103c0, and the _start symbol is also at 0x4103c0. This is not unusual at all.
- deleted 3y ago[deleted]
- vlovich123 3y ago> Things that I don’t understand very well, including calling main. So I won’t explain them. It depends on the language runtime, but a common task will be initializing global non-0 statics. For languages like Rust/C/C++ you can also inject variables to be initialized via linker flags. Before start if the program is dynamically linked then I believe the linker runtime is run to resolve the links and then transfer control to _start. Basically hacks on hacks on hacks added organically to offer extensibility and the hacks have enough social adoption and are good enough that we stick with them.
- seanw444 3y ago> Basically hacks on hacks on hacks added organically to offer extensibility and the hacks have enough social adoption and are good enough that we stick with them. The more I learn about the deep depths of modern computing, the more I realize that they're actually full of inelegant legacy cruft.
- kccqzy 3y agoThe style guide at both my previous and current employers explicitly forbids having global non-0 statics for this exact reason: code that runs before main() is very unusual. Many assumptions do not hold. A far better way is to use function-local statics. A static variable inside a function is initialized when execution reaches that point when the function is being called. Furthermore, such initialization is thread safe so that one initialization happens despite multiple concurrent calls of the function. The only exception to that style guide rule is the new constinit in C++20. It is sometimes called linker-initialized to make it even clearer that the program didn't do anything to initialize it, the linker did.
- cesarb 3y ago> Furthermore, such initialization is thread safe so that one initialization happens despite multiple concurrent calls of the function. IIRC, there are some popular compilers in which initialization of static variables inside functions is not thread safe (even though AFAIK the C++ standard said they should be).
- deleted 3y ago[deleted]
- matheusmoreira 3y ago> the symbol main is a C-specific thing Absolutely. And only available on hosted C. Freestanding C lets you have any entry point you want. > The symbol _start is a language-agnostic entry point for the binary that will in this case call main. That's just the linker's default. You can set it to a nicer symbol with -Wl,--entry="${symbol}" and GCC even supports setting it directly with no need for the unsightly -Wl. Also, the entry point is actually a pointer, not a symbol. The linker just takes the address of the symbol you specify and sets the ELF entry point to that. > calling the entry point _start with main's argc/argv In addition to argument count and argument vector, the stack also contains the environment vector and the auxiliary vector. The process startup code can be as simple as popping all that stuff off the stack and into the appropriate registers and then calling a C function of your choosing. Note that the entry point is not itself a function: there's nothing to return to. The entry point code finishes with an exit system call to ensure clean process termination when main returns a status code. This is how things work on Linux at least.