Linux x86 program start up – How the heck do we get to main()? (2011)
dbp-consulting.com
dbp-consulting.com
Linux x86 Program Start Up - https://news.ycombinator.com/item?id=8739661 - Dec 2014 (30 comments)
There are more than that lol. Gets reposted once a year almost.
The important Linux facts are:
1. _start is not a function
It can't be returned from, the exit system call must be issued before execution terminates.
2. Arguments and environment are on the stack
Argument count and vector can be simply popped off the stack into appropriate registers, in that order.
The environment vector is located after the NULL terminator of the argument vector, or argument count + 1.
The auxiliary vector is located after the NULL terminator of the environment vector. No count is provided for that, so code must loop through the environment looking for the sentinel in order to find it.
The auxiliary vector is really interesting. I don't usually see software making direct use of it. It contains interesting information such as CPU identifier and capabilities, page size, the location of the Linux vDSO, some random bytes, program file name, user and group IDs, among other things.
https://github.com/torvalds/linux/blob/master/include/uapi/l...
https://github.com/torvalds/linux/blob/master/arch/x86/inclu...
https://github.com/torvalds/linux/blob/master/Documentation/...
This is the data the Linux kernel passes to programs. After organizing these parameters, the program is free to do whatever it wants. The libc entry point will naturally start setting up libc. In particular, it seems to spend a lot of time setting up the init and fini insanity that's probably better off forgotten.
https://blogs.oracle.com/solaris/post/init-and-fini-processi...
It's not necessary. After this, you can just run your program directly. I used to develop a liblinux that illustrates all this with much simpler code:
https://github.com/matheusmoreira/liblinux/blob/master/start...
https://github.com/matheusmoreira/liblinux/blob/master/start...
The entry point code passes the stack pointer to a C function which gathers all kernel parameters and starts the program with no further setup. I made several example programs, including one which outputs all these variables.
https://github.com/matheusmoreira/liblinux/blob/master/examp...
I stopped developing this because I discovered the Linux itself has a better solution that they use for their own tools:
https://github.com/torvalds/linux/blob/master/tools/include/...
The entry point code for all supported architectures is present as inline assembly code!
I'll just add that yes, things are actually way simple from ELF point of view. If you generate an ELF file by hand (or from a compiler you write), you can simply point it to the first instruction and the argv, argc, and environment pointers arrive as described above.
Virgil startup code is ~15 assembly instructions (even less for test binaries), and then it calls into the Virgil runtime source to get the heap setup and start allocating the first objects (array of strings for arguments).
I love low level posts like these. It's important we don't forget that C is just one alternative.
> I love low level posts like these. It's important we don't forget that C is just one alternative.
Yes!! It's good to know where Linux ends and all the other stuff begins. Existing documentation makes things really confusing, it assumes people want the C stuff. Sometimes it even tells readers they aren't supposed to touch these "internals". It takes a lot of work to unravel this mess and get to the essential stuff.
For those who read that and don't know, it is the second occurrence of "environment" that should be "arguments".
So what happens if exit isn't called?
2. If you add nothing, the CPU will continue to execute the bytes that follow.
In both cases it is quite certain you end with a segmentation fault.(Or in case 2., an illegal instruction)
I've seen code with a hlt instruction after main and the exit system call. Not sure what their intentions are, it should be unreachable.
glibc nulls out its internal pointer (_dl_random) after use so you can't easily get the pointer later, but of course it'd be a bad idea to try and use it.
> dbp-consulting.com uses an invalid security certificate.
> The certificate is not trusted because it is self-signed.
Just three paragraphs in.
> Be kind. Don't be snarky.
> Comments should get more thoughtful and substantive, not less, as a topic gets more divisive.
> Eschew flamebait. Avoid unrelated controversies and generic tangents.
[1] https://news.ycombinator.com/newsguidelines.html#comments
Unfortunately, the articles HN links to don't have to follow those guidelines.