理解__libc_init_array

Pon*_*279 11 gcc startup elf linker-scripts newlib

我从http://newlib.sourcearchive.com/documentation/1.18.0/init_8c-source.html查看了__libc_init_array的源代码.
但我不太明白这个功能是做什么的.

我知道这些符号

/* These magic symbols are provided by the linker.  */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));
extern void (*__fini_array_start []) (void) __attribute__((weak));
extern void (*__fini_array_end []) (void) __attribute__((weak));
Run Code Online (Sandbox Code Playgroud)

在链接描述文件中定义.
链接器脚本的一部分可能如下所示:

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
  ...
Run Code Online (Sandbox Code Playgroud)

然后我用ELF-v1.1,gcc 4.7.2,ld和codesourcery(我使用的是代码源g ++ lite)的文档中的密钥"init_array"进行搜索,但却什么都没得到.

我在哪里可以找到这些符号的规格?

Rob*_*ugs 13

这些符号与C/C++构造函数和析构函数启动和拆除之前/之后调用的代码有关main().段命名.init,.ctors,.preinit_array,并.init_array与C/C++对象,以及部分的初始化做.fini,.fini_array.dtors是推倒.开始和结束符号定义与此类操作相关的代码段的开头和结尾,并且可以从运行时支持代码的其他部分引用.

.preinit_array.init_array部分包含指针数组到将在初始化时被调用的函数.这.fini_array是一个将在销毁时调用的函数数组.据推测,开始和结束标签用于走这些列表.

使用这些符号的代码的一个很好的例子可以在这里找到libc source for initfini.c.您可以看到在启动时__libc_init_array()调用它,它首先.preinit_array通过引用开始和结束标签来调用部分中的所有函数指针.然后它调用_init().init部分中的函数.最后,它调用了节中的所有函数指针.init_array.完成后,在最终调用之前main(),拆除调用将__libc_fini_array()导致调用所有函数.请注意,当计算要在拆卸时调用的函数数时,此代码中似乎存在剪切和粘贴错误.据推测,他们正在处理实时微控制器操作系统,从未遇到过这一部分..fini_array_fini()


nil*_*ilo 8

@Robotbugs 的回答很有趣,但我发现了一些额外的信息,可能会满足其他人的好奇心。

System V 应用程序二进制接口似乎适用于 gcc 生成的可执行文件(我猜还有其他一些编译器 - 我想到了 clang)。

特殊部分章节指出(仅相关部分,由我重新排序):

.preinit_array:

此部分包含一个函数指针数组,该数组有助于包含该部分的可执行文件或共享对象的单个预初始化数组。

.init_array

此部分包含一个函数指针数组,这些函数指针有助于包含该部分的可执行文件或共享对象的单个初始化数组。

.fini_array

此部分包含一个函数指针数组,这些函数指针有助于包含该部分的可执行文件或共享对象的单个终止数组。

newlib 中的文件 init.c包括:

/* Iterate over all the init routines.  */
void
__libc_init_array (void)
{
    size_t count;
    size_t i;

    count = __preinit_array_end - __preinit_array_start;
    for (i = 0; i < count; i++)
        __preinit_array_start[i] ();

#ifdef HAVE_INIT_FINI
    _init ();
#endif

    count = __init_array_end - __init_array_start;
    for (i = 0; i < count; i++)
    __init_array_start[i] ();
}
Run Code Online (Sandbox Code Playgroud)

这对应于 STM32 处理器的规范链接器脚本解决方案(作为示例):

.preinit_array     :
{
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
Run Code Online (Sandbox Code Playgroud)

该链接描述文件部分非常清楚:它定义了 Newlib 执行System V 应用程序二进制接口preinit和指定的数组函数所需的符号init。这似乎是 C++ 中静态构造函数的标准解决方案。并且fini将对应于静态析构函数。

当然,这个故事最讽刺的部分是,使用没有Construct On First Use Idiom 的静态 C++ 对象是解决静态初始化顺序问题的最佳方法!即 C++ 对象实际上不应该通过上面的preinit/init数组构造!


nne*_*neo 4

这些特殊符号最终将被PT_DYNAMIC生成的库的部分引用。PT_DYNAMIC定义了动态链接成功所需的各种资源(库依赖项、导出的符号、符号哈希表、init/fini 数组等)。

因此,这些列表中的任何函数最终都会链接到该PT_DYNAMIC部分,并在动态链接过程中的适当时间被调用。您可能需要查阅来源以ldd获取更多信息。