If you link a program with a compiler driver (clang/gcc) in a standard way (not -nostdlib), the following components are usually on the linker command line.
- crt1.o (glibc/musl):
-no-pie/-pie/-static-pie- crt1.o:
-no-pie - Scrt1.o:
-pie,-shared - rcrt1.o:
-static-pie - gcrt1.o:
- crt1.o:
- crti.o (glibc/musl)
- crtbegin.o
- crtbegin.o:
-no-pie - crtbeginS.o:
-pie,-shared - crtbeginT.o:
-static-pie
- crtbegin.o:
- user input
- -lstdc++
- Some combination of -lc -lgcc_s -lgcc -lgcc_eh
- crtn.o (glibc/musl)
- crtend.o
- crtend.o:
-no-pie - crtendS.o:
-pie,-shared - crtendT.o:
-static-pie
- crtend.o:
This file is only used by executables.
In glibc, the file is -r linked from csu/start.c csu/abi-note.c csu/init.c csu/static-reloc.c.
It used to call __libc_start_main with arguments main, __libc_csu_init, __libc_csu_fini (defined by libc_nonshared.a(elf-init.oS)).
From BZ #23323 onwards, on most architectures, start.S:_start calls __libc_main_start with two zero arguments instead, and __libc_csu_init and __libc_csu_fini are moved into csu/libc-start.c.
In musl, this file calls __libc_start_main with main, _init, and _fini.
crti.o defines _init in the .init section and _fini in the .fini section.
The defined _init is a fragment which is expected to be concatenated with other files and finally crtn.o to get the full definition.
In glibc x86-64,
# crti.o
Disassembly of section .init:
0000000000000000 <_init>:
0: 48 83 ec 08 subq $8, %rsp
4: 48 8b 05 00 00 00 00 movq (%rip), %rax # b <_init+0xb>
0000000000000007: R_X86_64_REX_GOTPCRELX __gmon_start__-0x4
b: 48 85 c0 testq %rax, %rax
e: 74 02 je 0x12 <_init+0x12>
10: ff d0 callq *%rax
Disassembly of section .fini:
0000000000000000 <_fini>:
0: 48 83 ec 08 subq $8, %rsp
# crtn.o
Disassembly of section .init:
0000000000000000 <.init>:
0: addq $8, %rsp
4: retq
Disassembly of section .fini:
0000000000000000 <.fini>:
0: addq $8, %rsp
4: retq
The linker defines DT_INIT if _init (default value for -init) is defined, and DT_FINI if _fini is defined.
The section fragment idea is fragile. On RISC-V, DT_INIT is not used.
glibc defines the files in sysdeps/aarch64/crt[in].S.
crti.o calls __gmon_start__ (gmon profiling system) if defined. This is used by gcc -pg.
libgcc/crtstuff.c
If __LIBGCC_INIT_ARRAY_SECTION_ASM_OP__ is not defined and __LIBGCC_INIT_SECTION_ASM_OP__ is defined,
- crtend.o defines a
.initsection which calls__do_global_ctors_aux.__do_global_ctors_auxcalls the static constructors in the.ctorssection. - crtbegin.o defines a
.finisection which calls__do_global_dtors_aux.__do_global_dtors_auxcalls the static constructors in the.dtorssection. - crtbegin.o defines .ctors and .dtors with a single -1 value.
- crtend.o defines .ctors and .dtors with a single 0 value.
On modern distributions, __LIBGCC_INIT_ARRAY_SECTION_ASM_OP__ is 0 and crtend.o contains no .text/.ctors/.dtors.
Below the control flows are flattened.
In rtld:
sysdeps/x86_64/dl-machine.h:_userelf/rtld.c:_dl_startsysdeps/x86_64/dl-machine.h:_dl_start_userelf/dl-init.c:_dl_init- Jump to the main executable
e_entry
In the main executable:
sysdeps/x86_64/start.S:_startcsu/libc-start.c:__libc_start_main, theSHAREDbranch- (if
ELF_INITFINIis defined) RunDT_INIT - Run
DT_INITARRAY - Run
main - Run
exit
In the main executable:
sysdeps/x86_64/start.S:_startcsu/libc-start.c:__libc_start_main, the!SHAREDbranch_dl_relocate_static_pieARCH_SETUP_IRELARCH_SETUP_TLScsu/libc-start.c:call_init- Run
[__preinit_array_start, __preinit_array_end) - (if
ELF_INITFINIis defined) Run_init - Run
[__init_array_start, __init_array_end)
- Run
- Run
main - Run
exit
For a dynamically linked executable, the rtld process:
arch/x86_64/crt_arch.h:_dlstartldso/dlstart.c:_dlstart_cldso/dynlink.c:__dls2relocate rtldldso/dynlink.c:__dls2bsetup early thread pointerldso/dynlink.c:__dls3- Jump to the main executable
e_entry
In the main executable:
arch/x86_64/crt_arch.h:_startcrt/crt1.c:_start_csrc/env/__libc_start_main.c:__libc_start_main__init_libcinitialize auxv/TLS/stack protector/etclibc_start_main_stage2__libc_start_initexit(main(argc, argv, envp));
__libc_start_init has different behaviors for dynamically and statically linked executables.
For a dynamically linked executable: it runs DT_INIT (unless NO_LEGACY_INITFINI) then DT_INIT_ARRAY.
Note: libc.so has a dummy _init.
helpful, i'm confused in musl and libgcc