链接程序和库指南

初始化和终止节

动态库可以提供用于运行时初始化和终止处理的代码。每次在进程中装入动态库时,都会执行一次动态库的初始化代码。每次从进程中卸载动态库或进程终止时,都会执行一次动态库的终止代码。可以将此代码封装在以下两种节类型的任意一种中:函数指针数组或单个代码块。这两种节类型都是通过串联输入可重定位目标文件中的相似节生成的。

.preinit_array.init_array.fini_array 节分别提供运行时预初始化数组、初始化数组和终止函数数组。创建动态库时,链接编辑器相应地使用 .dynamic 标记对(DT_PREINIT_[ARRAY/ARRAYSZ]DT_INIT_[ARRAY/ARRAYSZ]DT_FINI_[ARRAY/ARRAYSZ])标识这些数组。这些标记标识关联的节,以便运行时链接程序可以调用这些节。预初始化数组仅适用于动态可执行文件。

.init.fini 节分别提供运行时初始化代码块和终止代码块。编译器驱动程序通常提供 .init.fini 节以及添加到输入文件列表开头和末尾的文件。编译器提供这些文件的作用相当于将可重定位目标文件中的 .init.fini 代码封装到各个函数中。这些函数分别用保留符号名称 _init_fini 标识。 创建动态库时,链接编辑器相应地使用 .dynamic 标记(DT_INITDT_FINI)标识这些符号。这些标记标识关联的节,以便运行时链接程序可以调用这些节。

有关运行时执行初始化和终止代码的更多信息,请参见初始化和终止例程

链接编辑器可以使用 -z initarray-z finiarray 选项直接注册初始化函数和终止函数。例如,以下命令将 foo() 的地址放置在 .initarray 元素中,并将 bar() 的地址放置在 .finiarray 元素中。


$ cat main.c

#include    <stdio.h>



void foo()

{

        (void) printf("initializing: foo()\n");

}



void bar()

{

        (void) printf("finalizing: bar()\n");

}



main()

{

        (void) printf("main()\n");

        return (0);

}



$ cc -o main -zinitarray=foo -zfiniarray=bar main.c

$ main

initializing: foo()

main()

finalizing: bar()

可以使用汇编程序直接创建初始化节和终止节。但是,大多数编译器提供特殊元语来简化其声明。例如,可以使用以下 #pragma 定义重新编写上面的代码示例。这些定义导致在 .init 节中调用 foo(),并在 .fini 节中调用 bar()


$ cat main.c

#include    <stdio.h>



#pragma init (foo)

#pragma fini (bar)



.......

$ cc -o main main.c

$ main

initializing: foo()

main()

finalizing: bar()

分布在几个可重定位目标文件中的初始化和终止代码包含在归档库或共享库中时,这些代码可以产生不同的行为。对使用此归档的应用程序进行链接编辑时,可能仅提取此归档中包含的部分目标文件。这些目标文件可能仅提供分布在归档成员中的部分初始化和终止代码。在运行时仅执行此部分代码。在运行时装入依赖性时,针对共享库生成的相同应用程序将执行所有累积的初始化和终止代码。

在运行时确定进程中执行初始化和终止代码的顺序是一个很复杂的问题,需要进行依赖性分析。限制初始化代码和终止代码的内容可简化此分析过程。简化的初始化代码和终止代码提供可预测的运行时行为。 有关更多详细信息,请参见初始化和终止顺序

如果初始化代码中包含可以使用 dldump(3C) 转储其内存的动态库,则数据初始化应该是一个独立的过程。