enum { ef_free, /* `ef_free' MUST be zero! */ ef_us, ef_on, ef_at, ef_cxa };
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; };
void attribute_hidden __run_exit_handlers(int status, struct exit_function_list **listp, bool run_list_atexit, bool run_dtors) { /* First, call the TLS destructors. */ #ifndef SHARED if (&__call_tls_dtors != NULL) #endif if (run_dtors) __call_tls_dtors(); //首先释放tls
/* 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;
__libc_lock_lock(__exit_funcs_lock); //给互斥锁上锁
restart: cur = *listp; //最开始的就是initial变量
if (cur == NULL) { /* Exit processing complete. We will not allow any more atexit/on_exit registrations. */ __exit_funcs_done = true; __libc_lock_unlock(__exit_funcs_lock); //给互斥锁解锁 break; }
/* Unlock the list while we call a foreign function. */ __libc_lock_unlock(__exit_funcs_lock); switch (f->flavor) { void (*atfct)(void); void (*onfct)(int status, void *arg); void (*cxafct)(void *arg, int status);
case ef_free: case ef_us: break; case ef_on: onfct = f->func.on.fn; //绑定on类型的函数指针 #ifdef PTR_DEMANGLE PTR_DEMANGLE(onfct); //用宏来解密函数指针 #endif onfct(status, f->func.on.arg); //执行相应析构函数 break; case ef_at: atfct = f->func.at; #ifdef PTR_DEMANGLE PTR_DEMANGLE(atfct); #endif atfct(); 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; //为了防止条件竞争,执行过的cxa类型析构函数之后会被置free cxafct = f->func.cxa.fn; #ifdef PTR_DEMANGLE PTR_DEMANGLE(cxafct); #endif cxafct(f->func.cxa.arg, status); break; } /* Re-lock again before looking at global state. */ __libc_lock_lock(__exit_funcs_lock);
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. */ goto restart; }
*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, ());
_exit(status); //系统调用,内核exit }
TLS:Thread Local Storage,是一种数据的储存方式,作用是保持数据在线程内全局可访问,而不能被其他线程访问到。
/* Call the destructors. This is called either when a thread returns from the initial function or when the process exits via the exit function. */ void __call_tls_dtors (void) { while (tls_dtor_list) { structdtor_list *cur = tls_dtor_list; dtor_func func = cur->func; #ifdef PTR_DEMANGLE PTR_DEMANGLE (func); #endif
/* Ensure that the MAP dereference happens before l_tls_dtor_count decrement. That way, we protect this access from a potential DSO unload in _dl_close_worker, which happens when l_tls_dtor_count is 0. See CONCURRENCY NOTES for more detail. */ atomic_fetch_add_release (&cur->map->l_tls_dtor_count, -1); free (cur); } }
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 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)));
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. */ /*这里写的是__libc_start_main函数的传参寄存器*/
mov %RDX_LP, %R9_LP /* Address of the shared library termination function. *//*设置参数rtld_fini*/ #ifdef __ILP32__ mov (%rsp), %esi /* Simulate popping 4-byte argument count. */ add $4, %esp #else popq %rsi /* Pop the argument count. */ //设置参数argc #endif /* argv starts just at the current stack top. */ mov %RSP_LP, %RDX_LP //设置参数argv /* Align the stack to a 16 byte boundary to follow the ABI. */ and $~15, %RSP_LP //rsp对齐
/* 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
#ifdef PIC /* Pass address of our own entry points to .fini and .init. */ //设置参数init和fini mov __libc_csu_fini@GOTPCREL(%rip), %R8_LP mov __libc_csu_init@GOTPCREL(%rip), %RCX_LP
mov main@GOTPCREL(%rip), %RDI_LP //设置参数main函数地址 #else /* Pass address of our own entry points to .fini and .init. */ mov $__libc_csu_fini, %R8_LP mov $__libc_csu_init, %RCX_LP
mov $main, %RDI_LP #endif
/* 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)
/* 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); //生成canary,然后保存 # ifdef THREAD_SET_STACK_GUARD THREAD_SET_STACK_GUARD (stack_chk_guard); # else __stack_chk_guard = stack_chk_guard; # endif
# ifdef DL_SYSDEP_OSCHECK if (!__libc_multiple_libcs) { /* 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 (); //初始化TLS
/* Set up the pointer guard value. */ uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random, stack_chk_guard); //生成pointer_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); //注册ld的析构函数
#ifndef SHARED /* 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); //初始化libc
/* Register the destructor of the program, if any. */ if (fini) __cxa_atexit ((void (*) (void *)) fini, NULL, NULL); //注册fini函数
/* 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
/* 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]); #endif if (init) (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM); //调用构造函数init
... //省略了一些代码,大概是对线程的初始化和一些维护工作 /* Nothing fancy, just call the function. */ result = main (argc, argv, __environ MAIN_AUXVEC_PARAM); //调用main函数 #endif
void _dl_fini (void) { /* Lots of fun ahead. We have to call the destructors for all still loaded objects, in all namespaces. The problem is that the ELF specification now demands that dependencies between the modules are taken into account. I.e., the destructor for a module is called before the ones for any of its dependencies. To make things more complicated, we cannot simply use the reverse order of the constructors. Since the user might have loaded objects using `dlopen' there are possibly several other modules with its dependencies to be taken into account. Therefore we have to start determining the order of the modules once again from the beginning. */
/* We run the destructors of the main namespaces last. As for the other namespaces, we pick run the destructors in them in reverse order of the namespace ID. */ #ifdef SHARED int do_audit = 0; again: #endif for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns) //遍历_rtld_global中的所有非共享模块: _dl_ns[DL_NNS] { /* Protect against concurrent loads and unloads. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); //对rtld_global上锁
unsignedint nloaded = GL(dl_ns)[ns]._ns_nloaded; /* No need to do anything for empty namespaces or those used for auditing DSOs. */ if (nloaded == 0 #ifdef SHARED || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit #endif ) __rtld_lock_unlock_recursive (GL(dl_load_lock)); //如果这个命名空间中没有模块,则直接解锁 else//否则遍历模块 { /* Now we can allocate an array to hold all the pointers and copy the pointers in. */ struct link_map *maps[nloaded]; //把这个命名空间中的所有模块指针, 都复制到maps数组中
unsignedint i; structlink_map *l; assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL); for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next) /* Do not handle ld.so in secondary namespaces. */ if (l == l->l_real) { assert (i < nloaded);
maps[i] = l; l->l_idx = i; ++i;
/* Bump l_direct_opencount of all objects so that they are not dlclose()ed from underneath us. */ ++l->l_direct_opencount; } assert (ns != LM_ID_BASE || i == nloaded); assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1); unsignedint nmaps = i; //模块数量
/* Now we have to do the sorting. We can skip looking for the binary itself which is at the front of the search list for the main namespace. */ _dl_sort_maps (maps + (ns == LM_ID_BASE), nmaps - (ns == LM_ID_BASE), NULL, true); //排序确定析构顺序
/* We do not rely on the linked list of loaded object anymore from this point on. We have our own list here (maps). The various members of this list cannot vanish since the open count is too high and will be decremented in this loop. So we release the lock so that some code which might be called from a destructor can directly or indirectly access the lock. */ __rtld_lock_unlock_recursive (GL(dl_load_lock)); //解锁
/* 'maps' now contains the objects in the right order. Now call the destructors. We have to process this array from the front. */ for (i = 0; i < nmaps; ++i) //按顺序析构每一个模块 { structlink_map *l = maps[i];
if (l->l_init_called) { /* Make sure nothing happens if we are called twice. */ l->l_init_called = 0;
/* Is there a destructor function? */ /* 是否包含fini_array节, 或者fini节 */ if (l->l_info[DT_FINI_ARRAY] != NULL || l->l_info[DT_FINI] != NULL) { /* When debugging print a message first. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", DSO_FILENAME (l->l_name), ns);
/* First see whether an array is given. */ if (l->l_info[DT_FINI_ARRAY] != NULL) { /* l->l_addr: 模块l的加载基地址 l->l_info[DT_FINI_ARRAY]: 模块l中fini_array节的描述符 l->l_info[DT_FINI_ARRAY]->d_un.d_ptr: 模块l中fini_arrary节的偏移 array: 为模块l的fini_array节的内存地址 */ ElfW(Addr) *array = (ElfW(Addr) *) (l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); /* ELF中 fini_arraysz节用来记录fini_array节的大小 l->l_info[DT_FINI_ARRAYSZ]: 模块l中fini_arraysz节描述符 l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val: 就是fini_array节的大小, 以B为单位 i: fini_array节的大小/一个指针大小, 即fini_array中有多少个析构函数 */ unsignedint i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr))); while (i-- > 0) //从后往前调用析构函数 ((fini_t) array[i]) (); }
/* Next try the old-style destructor. */ if (l->l_info[DT_FINI] != NULL) //调用fini段中的函数 DL_CALL_DT_FINI (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr); }
...
/* Correct the previous increment. */ --l->l_direct_opencount; } } }
structrtld_global { #endif /* Don't change the order of the following elements. 'dl_loaded' must remain the first element. Forever. */
/* Non-shared code has no support for multiple namespaces. */ #ifdef SHARED # define DL_NNS 16 #else # define DL_NNS 1 #endif EXTERN structlink_namespaces { /* A pointer to the map for the main map. */ structlink_map *_ns_loaded;//每个模块用_ns_loaded描述, 这个命名空间中所映射的模块组成一个双向链表, _ns_loaded就是这个链表的指针 /* Number of object in the _dl_loaded list. */ unsignedint _ns_nloaded; //模块数量 /* Direct pointer to the searchlist of the main object. */ structr_scope_elem *_ns_main_searchlist;//映射模块的搜索表 /* This is zero at program start to signal that the global scope map is allocated by rtld. Later it keeps the size of the map. It might be reset if in _dl_close if the last global object is removed. */ unsignedint _ns_global_scope_alloc;
/* During dlopen, this is the number of objects that still need to be added to the global scope map. It has to be taken into account when resizing the map, for future map additions after recursive dlopen calls from ELF constructors. */ unsignedint _ns_global_scope_pending_adds;
/* Search table for unique objects. */ structunique_sym_table //这个命名空间中的符号表, 单个命名空间中的符号不允许重复 { __rtld_lock_define_recursive (, lock) structunique_sym { uint32_t hashval; //符号的哈希值 constchar *name; //符号名称 constElfW(Sym) *sym; //符号 conststructlink_map *map;//所属模块 } *entries; //索引指针 size_t size; //元素数量 size_t n_elements; void (*free) (void *); //析构函数 } _ns_unique_sym_table; /* Keep track of changes to each namespace' list. */ structr_debug _ns_debug; } _dl_ns[DL_NNS]; //一个命名空间一个link_namespace结构体 /* One higher than index of last used namespace. */ EXTERN size_t _dl_nns; //使用了多少个命名空间
/* During the program run we must not modify the global data of loaded shared object simultanously in two threads. Therefore we protect `_dl_open' and `_dl_close' in dl-close.c. This must be a recursive lock since the initializer function of the loaded object might as well require a call to this function. At this time it is not anymore a problem to modify the tables. */ __rtld_lock_define_recursive (EXTERN, _dl_load_lock) /* This lock is used to keep __dl_iterate_phdr from inspecting the list of loaded objects while an object is added to or removed from that list. */ __rtld_lock_define_recursive (EXTERN, _dl_load_write_lock)
structlink_map { /* These first few members are part of the protocol with the debugger. This is the same format used in SVR4. */ //模块的基地址 ElfW(Addr) l_addr; /* Difference between the address in the ELF file and the addresses in memory. *///模块的基地址 char *l_name; /* Absolute file name object was found in. *///模块的文件名 ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. *///指向elf的dyn节 structlink_map *l_next, *l_prev;/* Chain of loaded objects. */
/* All following members are internal to the dynamic linker. They may change without notice. */
/* This is an element which is only ever different from a pointer to the very same copy of this type for ld.so when it is used in more than one namespace. */ structlink_map *l_real;
/* Number of the namespace this link map belongs to. */ Lmid_t l_ns; //模块所属命名空间的idx
structlibname_list *l_libname; /* Indexed pointers to dynamic section. [0,DT_NUM) are indexed by the processor-independent tags. [DT_NUM,DT_NUM+DT_THISPROCNUM) are indexed by the tag minus DT_LOPROC. [DT_NUM+DT_THISPROCNUM,DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM) are indexed by DT_VERSIONTAGIDX(tagvalue). [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM, DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) are indexed by DT_EXTRATAGIDX(tagvalue). [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM, DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM) are indexed by DT_VALTAGIDX(tagvalue) and [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM, DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM+DT_ADDRNUM) are indexed by DT_ADDRTAGIDX(tagvalue), see <elf.h>. */ /* l_info是ELF节描述符组成的的数组 ELF中一个节, 使用一个ElfW(Dyn)描述 各个类型的节在l_info中的下标固定, 因此可以通过下标来区分节的类型 */ ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; constElfW(Phdr) *l_phdr; /* Pointer to program header table in core. *///elf的头表 ElfW(Addr) l_entry; /* Entry point location. *///elf的入口 ElfW(Half) l_phnum; /* Number of program header entries. *///头表的节数 ElfW(Half) l_ldnum; /* Number of dynamic segment entries. *///dyn中的描述符数量