由于现代ELF使用运行时重定位机制。即:运行钱各种libc库函数的地址,编译时不知道libc会被mmap到哪
因此如果kernel发现需要运行时重定位, 那么就会转而jmp到这个elf指定的动态链接器(也就是常用的ld.so.2), 由ld去重定位elf中相关地址后再jmp到elf的入口点
但是ld并不是直接执行main()函数, 因为有析构函数就必定有构造函数, 在进入main之前还需要进行参数设置, 申请流缓冲区等操作
实际上ld会跳转到elf中的_start标号处, 这才是elf中第一个被执行的指令地址
_start标号处的程序由汇编编写, 对应libc中start.S文件,
ENTRY (_start) /* 编译时告诉链接器, 这里才是整个函数的入口点 */ /* Clearing frame pointer is insufficient, use CFI. */ cfi_undefined (rip) /* 初始化栈底: %ebp=0 */ xorl %ebp, %ebp /* 设置__libc_start_main的参数 调用__libc_start_main的参数会通过如下寄存器传递, 因为linux才用cdecl函数调用约定: main: %rdi argc: %rsi argv: %rdx init: %rcx fini: %r8 rtld_fini: %r9 stack_end: stack. */ mov %RDX_LP, %R9_LP /* 设置参数rtld_fini */ #ifdef __ILP32__ mov (%rsp), %esi /* Simulate popping 4-byte argument count. */ add $4, %esp #else popq %rsi /* Pop argc */ #endif /* 设置参数argv */ mov %RSP_LP, %RDX_LP /* rsp对齐 */ and $~15, %RSP_LP /* Push garbage because we push 8 more bytes. */ pushq %rax /* Provide the highest stack address to the user code (for stackswhich grow downwards). */ pushq %rsp #ifdef SHARED /* 设置参数init和fini */ mov __libc_csu_fini@GOTPCREL(%rip), %R8_LP mov __libc_csu_init@GOTPCREL(%rip), %RCX_LP /* 设置参数main函数地址 */ mov main@GOTPCREL(%rip), %RDI_LP /* 调用__libc_start_main() __libc_start_main()进行一些构造工作, 然后调用main() main() return到__libc_start_main之后 __libc_start_main会进行析构工作 */ call __libc_start_main@PLT #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 /* Call the user's main function, and exit with its value. But let the libc call main. */ call __libc_start_main #endif hlt /* Crash if somehow `exit' does return. */ END (_start)
这个函数定义在libc源码的libc-start.c文件中, 由于比较复杂, 因此只分析关键部分
static int __libc_start_main( int (*main)(int, char **, char **MAIN_AUXVEC_DECL), //参数: main函数指针 int argc, char **argv, //参数: argc argv ElfW(auxv_t) * auxvec, __typeof(main) init, //参数: init ELF的构造函数 void (*fini)(void), //参数: fini ELF的析构函数 void (*rtld_fini)(void), //参数: rtld_fini ld的析构函数 void *stack_end //参数: 栈顶 ) { ...函数体; }
_dl_fini的任务:
void internal_function _dl_fini(void) { #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] { __rtld_lock_lock_recursive(GL(dl_load_lock)); //对rtld_global上锁 unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded; /* 如果这个NameSapce没加载模块, 或者不需要释放, 就不需要做任何事, 就直接调用rtld中的函数指针释放锁 */ if (nloaded == 0 || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit) __rtld_lock_unlock_recursive(GL(dl_load_lock)); else //否则遍历模块 { /* 把这个命名空间中的所有模块指针, 都复制到maps数组中 */ struct link_map *maps[nloaded]; unsigned int i; struct link_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) //遍历链表 if (l == l->l_real) /* Do not handle ld.so in secondary namespaces. */ { 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; } ...; unsigned int nmaps = i; //多少个模块 /* 对maps进行排序, 确定析构顺序 */ _dl_sort_fini(maps, nmaps, NULL, ns); //释放锁 __rtld_lock_unlock_recursive(GL(dl_load_lock)); /* 从前往后, 析构maps中的每一个模块 */ for (i = 0; i < nmaps; ++i) { struct link_map *l = maps[i]; if (l->l_init_called) { /* Make sure nothing happens if we are called twice. */ l->l_init_called = 0; /* 是否包含fini_array节, 或者fini节 */ if (l->l_info[DT_FINI_ARRAY] != NULL || l->l_info[DT_FINI] != NULL) { /* debug时打印下相关信息 */ 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); /* 如果有fini_array节的话 */ 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中有多少个析构函数 */ unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof(ElfW(Addr))); while (i-- > 0) //从后往前, 调用fini_array中的每一个析构函数 ((fini_t)array[i])(); } /* 调用fini段中的函数 */ if (l->l_info[DT_FINI] != NULL) 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; } } } ...; }