【问题标题】:understanding the __libc_init_array了解 __libc_init_array
【发布时间】:2013-02-22 07:16:52
【问题描述】:

我从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));

在链接描述文件中定义。
部分链接描述文件可能如下所示:

  .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
  ...

然后我在 ELF-v1.1、gcc 4.7.2、ld 和 codesourcery(我正在使用 codesourcery g++ lite)的文档中使用键“init_array”进行搜索,但一无所获。

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

【问题讨论】:

    标签: gcc startup elf linker-scripts newlib


    【解决方案1】:

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

    The System V Application Binary Interface 似乎适用于 gcc 生成的可执行文件(我猜想其他一些编译器 - 想到了 clang)。

    The special sections chapter 状态(仅相关部分,由我重新排序):

    .preinit_array:

    本节包含一个函数指针数组,这些指针构成了包含该节的可执行文件或共享对象的单个预初始化数组。

    .init_array

    本节包含一个函数指针数组,这些指针组成一个初始化数组,用于包含该节的可执行文件或共享对象。

    .fini_array

    本节包含一个函数指针数组,这些指针构成包含该节的可执行文件或共享对象的单个终止数组。

    The file init.c from newlib 包括:

    /* 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] ();
    }
    

    这对应于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
    

    链接描述文件部分非常清楚:它定义了 Newlib 执行 the System V Application Binary Interfacepreinitinit 指定的数组函数所需的符号。这似乎是 C++ 中静态构造函数的标准解决方案。而fini 将对应于静态析构函数。

    当然,这个故事中最讽刺的部分是使用没有Construct On First Use Idiom 的静态C++ 对象是获得static initialization order problem 的最佳方式! IE。 C++ 对象实际上不应该通过上面的preinit/init 数组来构造!

    【讨论】:

      【解决方案2】:

      这些对象的规范是elf头文件格式的规范。至少他们为什么在那里。

      它们不能以任何方式使用形状手段或形式,除非您计划重写 glic 库及其与之对话的所有内容。简而言之,elf 标头需要一个 _start 函数。没有它就不会启动二进制文件。

      libc 库的很大一部分是用汇编而不是 C 编写的,C 没有考虑到这一点。 pre 数组函数是添加此标头的一种方式。

      查看 glibcteeny-efl.git 中的 gnu-csu 文件夹以获取示例。它还将数组设置为斜线格式的字符串。将两个元素设置为静态,argv 中的数组和 init_array。稍后它将检查以确保它们匹配。它还需要比你应该添加到这种函数中的更多的代码来破坏这个过程或做任何事情,而不是它意味着什么是不理会的。去玩你的冰箱。

      【讨论】:

        【解决方案3】:

        这些符号与在main() 之前/之后调用的 C/C++ 构造函数和析构函数启动和拆除代码有关。名为.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()。请注意,此代码在计算拆解时调用的函数计数时似乎存在剪切和粘贴错误。大概他们正在处理实时微控制器操作系统并且从未遇到过这个部分。

        【讨论】:

        • 另外,i 最好在 __libc_fini_array 中签名,以便循环终止(可能不是)。
        【解决方案4】:

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

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

        【讨论】:

        • 感谢您的回复。我只是用谷歌编辑它。 .init_array 部分是 System V ABI 指定的特殊部分,我猜它是由 gcc 自动生成的。
        猜你喜欢
        • 1970-01-01
        • 2014-12-22
        • 2019-10-04
        • 2017-05-16
        • 1970-01-01
        • 2010-11-12
        • 2020-09-18
        • 2011-01-07
        • 2016-07-15
        相关资源
        最近更新 更多