主程序(可执行文件)的初始化机制

在非动态库(如主程序)中,.init.init_array 的初始化函数是通过 C 运行时库(CRT) 的入口代码(如 _start)直接调用的,而非依赖 .dynamic 段中的 DT_INITDT_INIT_ARRAY 标签。以下是详细分析:

1. 主程序的初始化流程

主程序的初始化由 CRT(C Runtime) 直接处理,具体步骤如下:

  1. 入口点 _start 可执行文件的入口地址(e_entry)指向 CRT 提供的 _start 函数(位于 crt1.oScrt1.o 中)。 _start 负责初始化全局环境(如栈、寄存器),并调用 __libc_start_main

  2. __libc_start_main 的核心逻辑 __libc_start_main 在调用 main 函数前,会依次执行以下操作:

    • 调用 .init 节的代码(若有定义)。
    • 遍历 .init_array 数组,依次执行其中的构造函数(如 C++ 全局对象的构造函数或 __attribute__((constructor)) 函数)。
    • 初始化线程局部存储(TLS)、设置环境变量等。
  3. .init.init_array 的调用代码 CRT 通过 静态链接阶段获取的地址 直接访问 .init.init_array,无需通过 .dynamic 段。例如,在 glibc 的实现中,以下代码片段负责调用 .init_array

    // 伪代码(glibc/csu/libc-start.c)
    void __libc_start_main(...) {
        // 调用 .init 节(若存在)
        if (__ehdr.ehdr->e_type == ET_EXEC && __init != NULL) __init();
     
        // 调用 .init_array 中的函数
        size_t init_array_size = __init_array_end - __init_array_start;
        for (size_t i = 0; i < init_array_size; i++) {
            __init_array_start[i]();
        }
     
        // 进入 main 函数
        result = main(argc, argv, __environ);
    }

动态库

因为没有入口,所以要用 .dynamic 找到

typedef struct {
        Elf32_Sword d_tag;
        union {
                Elf32_Word      d_val;
                Elf32_Addr      d_ptr;
                Elf32_Off       d_off;
        } d_un;
} Elf32_Dyn;//32位程序
 
typedef struct {
        Elf64_Xword d_tag;
        union {
                Elf64_Xword     d_val;
                Elf64_Addr      d_ptr;
        } d_un;
} Elf64_Dyn;
 

其中 d_tag 决定这个是什么类别信息,以及该如何解析 d_un 内部变量。具体的定义在 https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-42444.html

NameValued_unExecutableShared Object
DT_INIT12d_ptrOptionalOptional
DT_FINI13d_ptrOptionalOptional
DT_FINI_ARRAY26d_ptrOptionalOptional
DT_INIT_ARRAYSZ27d_valOptionalOptional
DT_FINI_ARRAYSZ28d_valOptionalOptional

所以是有这些东西定位的,在 linker 的时候就加载了

区别:主程序 vs 动态库

特性主程序(ET_EXEC)动态库(ET_DYN)
初始化触发者CRT 的 _start__libc_start_main动态链接器(如 ld.so
依赖的元数据无(直接通过静态链接地址访问).dynamic 段中的 DT_INIT_ARRAY 标签
初始化代码位置.init.init_array同左
是否需要 .dynamic不需要(除非动态链接)必须包含 .dynamic