系统相关
首页 > 系统相关> > Linux中程序是怎样启动的

Linux中程序是怎样启动的

作者:互联网

Linux中程序是怎样启动的

前言

ELF的入口点_start()

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_start_main

这个函数定义在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()

_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;
            }
        }
    }

    ...;
}

标签:__,ns,启动,libc,程序,fini,start,Linux,main
来源: https://www.cnblogs.com/7resp4ss/p/16677932.html