-
-
Save jstaursky/ae82f546680881b556c4b7b45b16a793 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tl;dr: C program initialization, written in C! | |
* | |
* This applies to an executable dynamically linked with glibc. | |
* It is current as of glibc 2.26. | |
* | |
* A LOT of information has been omitted for simplicity; hell, | |
* some of it might be flat-out wrong (I wrote this after about | |
* 3 hours of experimenting with GDB). If you want to know EXACTLY | |
* what goes on under the covers, I advise you to read the | |
* actual glibc source. | |
*/ | |
/* | |
* glibc/sysdeps/<arch>/start.S | |
* | |
* Entry point of the program. Effectively just calls | |
* __libc_start_main(). | |
*/ | |
void _start(void) | |
{ | |
__libc_start_main( | |
&main, | |
argc, | |
argv, | |
init = __libc_csu_init, | |
fini = __libc_csu_fini, | |
rtld_fini = _dl_fini, | |
stack_end = /* current stack pointer */ | |
); | |
} | |
/* | |
* glibc/csu/libc-start.c | |
* | |
* The "main" function of libc. Does setup and teardown. | |
*/ | |
void __libc_start_main( | |
int (*main)(int, char **, char **), | |
int argc, | |
char **argv, | |
int (*init)(int, char **, char **), | |
void (*fini)(void), | |
void (*rtld_fini)(void), | |
void *stack_end) | |
{ | |
/* | |
* Register the dynamic linker's finalizer (_dl_fini) | |
* to run when exit() is called. | |
*/ | |
if (rtld_fini != NULL) | |
atexit(rtld_fini); | |
/* | |
* Call the initialization function (__libc_csu_init). | |
*/ | |
if (init != NULL) | |
init(argc, argv, envp); | |
/* | |
* Call main() (note that the envp array starts right | |
* after the argv array). | |
*/ | |
char **envp = &argv[argc + 1]; | |
int result = main(argc, argv, envp); | |
/* | |
* And call exit() with the return value of main(). | |
*/ | |
exit(result); | |
} | |
/* | |
* glibc/csu/elf-init.c | |
* | |
* Performs initialization. I don't really know how to describe | |
* it any better. | |
*/ | |
int __libc_csu_init(int argc, char **argv, char **envp) | |
{ | |
/* | |
* Call all the __attribute__((constructor)) functions. | |
* These symbols are generated by the linker. | |
*/ | |
size_t num_init = __init_array_end - __init_array_start; | |
for (size_t i = 0; i < num_init; i++) { | |
__init_array_start[i](argc, argv, envp); | |
} | |
} | |
/* | |
* glibc/stdlib/exit.c | |
* | |
* Calls all functions registered with atexit() in reverse | |
* registration order (like a stack), then halts the program. | |
*/ | |
void exit(int status) | |
{ | |
/* | |
* __exit_funcs is a linked list of arrays of functions that | |
* were registered with atexit(). The order is important; | |
* the structure looks like this: | |
* | |
* __exit_funcs -> [G] -> [D] -> [A] -> NULL | |
* [H] [E] [B] | |
* [ ] [F] [C] | |
* | |
*/ | |
struct exit_function_list *head = __exit_funcs; | |
for (struct exit_function_list *curr = head; curr != NULL; curr = curr->next) { | |
for (int i = curr->idx - 1; i >= 0; i--) { | |
curr->fns[i](); | |
} | |
} | |
/* | |
* Do the actual Linux exit syscall. | |
*/ | |
_exit(status); | |
} | |
/* | |
* glibc/elf/dl-fini.c | |
* | |
* The linker's finalizer. Calls the destructor of all loaded | |
* libraries in the correct order (i.e. if X depends on Y, | |
* the destructors of X are run before the destructors of Y). | |
*/ | |
void _dl_fini(void) | |
{ | |
/* | |
* Get a list of all the loaded shared objects and sort | |
* them in the appropriate order. This is essentially | |
* a DAG topological sort. | |
*/ | |
int nmaps = /* ... */; | |
struct link_map *maps[nmaps] = /* ... */; | |
_dl_sort_fini(maps, nmaps, /* ... */); | |
/* | |
* Then run all the destructors for each shared object. | |
* This calls functions marked with __attribute__((destructor)). | |
*/ | |
for (int i = 0; i < nmaps; i++) { | |
struct link_map *map = maps[i]; | |
for (int j = FINI_ARRAY_SZ(map) - 1; j >= 0; j--) { | |
FINI_ARRAY(map)[j](); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment