Oracle® Solaris 11.2 链接程序和库指南

退出打印视图

更新时间: 2014 年 7 月
 
 

初始化节和终止节

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

.pre_initarray.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() 的地址放置在 .init_array 元素中,并将 bar() 的地址放置在 .fini_array 元素中。

$ cat main.c
#include    <stdio.h>

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

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

void main()
{
        (void) printf("main()\n");
}
$ 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) 转储其内存的动态目标文件涉及到初始化代码,那么数据初始化应该是一个独立的过程。