structexit_function { /* `flavour' should be of type of the `enum' above but since we need this element in an atomic operation we have to use `long int'. */ longint flavor; union { void (*at) (void); struct { void (*fn) (int status, void *arg); void *arg; } on; struct { void (*fn) (void *arg, int status); void *arg; void *dso_handle; } cxa; } func; }; structexit_function_list { structexit_function_list *next; size_t idx; structexit_functionfns[32]; }; externstructexit_function_list *__exit_funcsattribute_hidden;
/* We do it this way to handle recursive calls to exit () made by the functions registered with `atexit' and `on_exit'. We call everyone on the list and use the status value in the last exit (). */ while (true) { structexit_function_list *cur = *listp;
if (cur == NULL) { /* Exit processing complete. We will not allow any more atexit/on_exit registrations. */ __exit_funcs_done = true; break; }
case ef_free: case ef_us: break; case ef_on: onfct = f->func.on.fn; arg = f->func.on.arg; #ifdef PTR_DEMANGLE PTR_DEMANGLE (onfct); #endif /* Unlock the list while we call a foreign function. */ __libc_lock_unlock (__exit_funcs_lock); onfct (status, arg); __libc_lock_lock (__exit_funcs_lock); break; case ef_at: atfct = f->func.at; #ifdef PTR_DEMANGLE PTR_DEMANGLE (atfct); #endif /* Unlock the list while we call a foreign function. */ __libc_lock_unlock (__exit_funcs_lock); atfct (); __libc_lock_lock (__exit_funcs_lock); break; case ef_cxa: /* To avoid dlclose/exit race calling cxafct twice (BZ 22180), we must mark this function as ef_free. */ f->flavor = ef_free; cxafct = f->func.cxa.fn; arg = f->func.cxa.arg; #ifdef PTR_DEMANGLE PTR_DEMANGLE (cxafct); #endif /* Unlock the list while we call a foreign function. */ __libc_lock_unlock (__exit_funcs_lock); cxafct (arg, status); __libc_lock_lock (__exit_funcs_lock); break; }
if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called)) /* The last exit function, or another thread, has registered more exit functions. Start the loop over. */ continue; }
*listp = cur->next; if (*listp != NULL) /* Don't free the last element in the chain, this is the statically allocate element. */ free (cur); }
__libc_lock_unlock (__exit_funcs_lock);
if (run_list_atexit) RUN_HOOK (__libc_atexit, ());
typedefstruct { void *tcb; /* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */ dtv_t *dtv; void *self; /* Pointer to the thread descriptor. */ int multiple_threads; int gscope_flag; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; unsignedlongint unused_vgetcpu_cache[2]; /* Bit 0: X86_FEATURE_1_IBT. Bit 1: X86_FEATURE_1_SHSTK. */ unsignedint feature_1; int __glibc_unused1; /* Reservation of some values for the TM ABI. */ void *__private_tm[4]; /* GCC split stack support. */ void *__private_ss; /* The lowest address of shadow stack, */ unsignedlonglongint ssp_base; /* Must be kept even if it is no longer used by glibc since programs, like AddressSanitizer, depend on the size of tcbhead_t. */ __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));
/* Register FUNC to be executed by `exit'. */ int #ifndef atexit attribute_hidden #endif atexit (void (*func) (void)) { return __cxa_atexit ((void (*) (void *)) func, NULL, __dso_handle); }
发现只是对__cxa_atexit的封装
1 2 3 4 5 6 7 8 9
/* Register a function to be called by exit or when a shared library is unloaded. This function is only called from code generated by the C++ compiler. */ int __cxa_atexit (void (*func) (void *), void *arg, void *d) { return __internal_atexit (func, arg, d, &__exit_funcs); } libc_hidden_def (__cxa_atexit)
/* As a QoI issue we detect NULL early with an assertion instead of a SIGSEGV at program exit when the handler is run (bug 20544). */ assert (func != NULL);
__libc_lock_lock (__exit_funcs_lock); new = __new_exitfn (listp);
if (new == NULL) { __libc_lock_unlock (__exit_funcs_lock); return-1; }
/* Must be called with __exit_funcs_lock held. */ structexit_function * __new_exitfn (structexit_function_list **listp) { structexit_function_list *p =NULL; structexit_function_list *l; structexit_function *r =NULL; size_t i = 0;
if (__exit_funcs_done) /* Exit code is finished processing all registered exit functions, therefore we fail this registration. */ returnNULL;
for (l = *listp; l != NULL; p = l, l = l->next) { for (i = l->idx; i > 0; --i) if (l->fns[i - 1].flavor != ef_free) break;
if (i > 0) break;
/* This block is completely unused. */ l->idx = 0; }
if (l == NULL || i == sizeof (l->fns) / sizeof (l->fns[0])) { /* The last entry in a block is used. Use the first entry in the previous block if it exists. Otherwise create a new one. */ if (p == NULL) { assert (l != NULL); p = (struct exit_function_list *) calloc (1, sizeof (struct exit_function_list)); if (p != NULL) { p->next = *listp; *listp = p; } }
if (p != NULL) { r = &p->fns[0]; p->idx = 1; } } else { /* There is more room in the block. */ r = &l->fns[i]; l->idx = i + 1; }
/* Mark entry as used, but we don't know the flavor now. */ if (r != NULL) { r->flavor = ef_us; ++__new_exitfn_called; }
ENTRY (_start) /* Clearing frame pointer is insufficient, use CFI. */ cfi_undefined (rip) /* Clear the frame pointer. The ABI suggests this be done, to mark the outermost frame obviously. */ xorl %ebp, %ebp
/* Extract the arguments as encoded on the stack and set up the arguments for __libc_start_main (int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end). The arguments are passed via registers and on the stack: main: %rdi argc: %rsi argv: %rdx init: %rcx fini: %r8 rtld_fini: %r9 stack_end: stack. */
mov %RDX_LP, %R9_LP /* Address of the shared library termination function. */ #ifdef __ILP32__ mov (%rsp), %esi /* Simulate popping 4-byte argument count. */ add $4, %esp #else popq %rsi /* Pop the argument count. */ #endif /* argv starts just at the current stack top. */ mov %RSP_LP, %RDX_LP /* Align the stack to a 16 byte boundary to follow the ABI. */ and $~15, %RSP_LP
/* Push garbage because we push 8 more bytes. */ pushq %rax
/* Provide the highest stack address to the user code (for stacks which grow downwards). */ pushq %rsp
/* These used to be the addresses of .fini and .init. */ xorl %r8d, %r8d xorl %ecx, %ecx
/* Call the user's main function, and exit with its value. But let the libc call main. Since __libc_start_main in libc.so is called very early, lazy binding isn't relevant here. Use indirect branch via GOT to avoid extra branch to PLT slot. In case of static executable, ld in binutils 2.26 or above can convert indirect branch into direct branch. */ call *__libc_start_main@GOTPCREL(%rip)
hlt /* Crash if somehow `exit' does return. */ END (_start)
/* Define a symbol for the first piece of initialized data. */ .data .globl __data_start __data_start: .long 0 .weak data_start data_start = __data_start
/* Note: The init and fini parameters are no longer used. fini is completely unused, init is still called if not NULL, but the current startup code always passes NULL. (In the future, it would be possible to use fini to pass a version code if init is NULL, to indicate the link-time glibc without introducing a hard incompatibility for new programs with older glibc versions.) For dynamically linked executables, the dynamic segment is used to locate constructors and destructors. For statically linked executables, the relevant symbols are access directly. */ STATIC int LIBC_START_MAIN(int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char **argv, #ifdef LIBC_START_MAIN_AUXVEC_ARG ElfW(auxv_t) *auxvec, #endif __typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *stack_end) { #ifndef SHARED char **ev = &argv[argc + 1];
__environ = ev;
/* Store the lowest stack address. This is done in ld.so if this is the code for the DSO. */ __libc_stack_end = stack_end;
# ifdef HAVE_AUX_VECTOR /* First process the auxiliary vector since we need to find the program header to locate an eventually present PT_TLS entry. */ # ifndef LIBC_START_MAIN_AUXVEC_ARG ElfW(auxv_t) *auxvec; { char **evp = ev; while (*evp++ != NULL) ; auxvec = (ElfW(auxv_t) *) evp; } # endif _dl_aux_init (auxvec); if (GL(dl_phdr) == NULL) # endif { /* Starting from binutils-2.23, the linker will define the magic symbol __ehdr_start to point to our own ELF header if it is visible in a segment that also includes the phdrs. So we can set up _dl_phdr and _dl_phnum even without any information from auxv. */
/* Initialize very early so that tunables can use it. */ __libc_init_secure ();
__tunables_init (__environ);
ARCH_INIT_CPU_FEATURES ();
/* Do static pie self relocation after tunables and cpu features are setup for ifunc resolvers. Before this point relocations must be avoided. */ _dl_relocate_static_pie ();
/* The stack guard goes into the TCB, so initialize it early. */ ARCH_SETUP_TLS ();
/* In some architectures, IREL{,A} relocations happen after TLS setup in order to let IFUNC resolvers benefit from TCB information, e.g. powerpc's hwcap and platform fields available in the TCB. */ ARCH_APPLY_IREL ();
/* Set up the stack checker's canary. */ uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); # ifdef THREAD_SET_STACK_GUARD THREAD_SET_STACK_GUARD (stack_chk_guard); # else __stack_chk_guard = stack_chk_guard; # endif
# ifdef DL_SYSDEP_OSCHECK { /* This needs to run to initiliaze _dl_osversion before TLS setup might check it. */ DL_SYSDEP_OSCHECK (__libc_fatal); } # endif
/* Initialize libpthread if linked in. */ if (__pthread_initialize_minimal != NULL) __pthread_initialize_minimal ();
/* Set up the pointer guard value. */ uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random, stack_chk_guard); # ifdef THREAD_SET_POINTER_GUARD THREAD_SET_POINTER_GUARD (pointer_chk_guard); # else __pointer_chk_guard_local = pointer_chk_guard; # endif
#endif/* !SHARED */
/* Register the destructor of the dynamic linker if there is any. */ if (__glibc_likely (rtld_fini != NULL)) __cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL);
#ifndef SHARED /* Perform early initialization. In the shared case, this function is called from the dynamic loader as early as possible. */ __libc_early_init (true);
/* Call the initializer of the libc. This is only needed here if we are compiling for the static library in which case we haven't run the constructors in `_dl_start_user'. */ __libc_init_first (argc, argv, __environ);
/* Register the destructor of the statically-linked program. */ __cxa_atexit (call_fini, NULL, NULL);
/* Some security at this point. Prevent starting a SUID binary where the standard file descriptors are not opened. We have to do this only for statically linked applications since otherwise the dynamic loader did the work already. */ if (__builtin_expect (__libc_enable_secure, 0)) __libc_check_standard_fds (); #endif/* !SHARED */
/* Call the initializer of the program, if any. */ #ifdef SHARED if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) GLRO(dl_debug_printf) ("\ninitialize program: %s\n\n", argv[0]);
if (init != NULL) /* This is a legacy program which supplied its own init routine. */ (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM); else /* This is a current program. Use the dynamic segment to find constructors. */ call_init (argc, argv, __environ);
/* Auditing checkpoint: we have a new object. */ _dl_audit_preinit (GL(dl_ns)[LM_ID_BASE]._ns_loaded);
/* Starting with glibc 2.34, the init parameter is always NULL. Older libcs are not prepared to handle that. The macro DEFINE_LIBC_START_MAIN_VERSION creates GLIBC_2.34 alias, so that newly linked binaries reflect that dependency. The macros below expect that the exported function is called __libc_start_main_impl. */
/* Initialization for dynamic executables. Find the main executable link map and run its init functions. */ staticvoid call_init(int argc, char **argv, char **env) { /* Obtain the main map of the executable. */ structlink_map *l = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
/* DT_PREINIT_ARRAY is not processed here. It is already handled in _dl_init in elf/dl-init.c. Also see the call_init function in the same file. */
/* Initialization for static executables. There is no dynamic segment, so we access the symbols directly. */ staticvoid call_init(int argc, char **argv, char **envp) { /* For static executables, preinit happens right before init. */ { constsize_t size = __preinit_array_end - __preinit_array_start; size_t i; for (i = 0; i < size; i++) (*__preinit_array_start [i]) (argc, argv, envp); }
# if ELF_INITFINI _init (); # endif
constsize_t size = __init_array_end - __init_array_start; for (size_t i = 0; i < size; i++) (*__init_array_start [i]) (argc, argv, envp); }
/* Don't do anything if there is no preinit array. */ if (__builtin_expect (preinit_array != NULL, 0) && preinit_array_size != NULL && (i = preinit_array_size->d_un.d_val / sizeof (ElfW(Addr))) > 0) { ElfW(Addr) *addrs; unsignedint cnt;
/* Stupid users forced the ELF specification to be changed. It now says that the dynamic loader is responsible for determining the order in which the constructors have to run. The constructors for all dependencies of an object must run before the constructor for the object itself. Circular dependencies are left unspecified. This is highly questionable since it puts the burden on the dynamic loader which has to find the dependencies at runtime instead of letting the user do it right. Stupidity rules! */
i = main_map->l_searchlist.r_nlist; while (i-- > 0) call_init (main_map->l_initfini[i], argc, argv, env);
/* Initial entry point code for the dynamic linker. The function _dl_start is the real entry point; it's return value is the user program's entry point. */ ENTRY (_start) /* Count arguments in r11 */ l.ori r3, r1, 0 l.movhi r11, 0 1: l.addi r3, r3, 4 l.lwz r12, 0(r3) l.sfnei r12, 0 l.addi r11, r11, 1 l.bf 1b l.nop l.addi r11, r11, -1 /* store argument counter to stack. */ l.sw 0(r1), r11
#define DT_INIT 12 /* Address of init function */ #define DT_FINI 13 /* Address of termination function */ ... #define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ #define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ #define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ #define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
int _IO_cleanup (void) { /* We do *not* want locking. Some threads might use streams but that is their problem, we flush them underneath them. */ int result = _IO_flush_all_lockp (0);
/* We currently don't have a reliable mechanism for making sure that C++ static destructors are executed in the correct order. So it is possible that other static destructors might want to write to cout - and they're supposed to be able to do so. The following will make the standard streambufs be unbuffered, which forces any output from late destructors to be written out. */ _IO_unbuffer_all ();