__attribute__((构造函数)) 调用顺序混淆

__attribute__((constructor)) call order confusion

本文关键字:顺序 调用 attribute 构造函数      更新时间:2023-10-16

这里的答案表明__attribute__((构造函数))不是在静态初始化调用的,而是在声明顺序中调用的。

那么,如果不能保证在所有数据初始化时调用它,它的目的是什么?我们也可以在Foo构造函数中使用我们的((构造函数))代码。

我正在寻找一种在共享库中拥有一个代码的方法,该代码将在初始化所有静态数据并调用静态构造函数后执行。我看到人们推荐__attribute__((构造函数))作为DllMain的替代品;正如我们所看到的,这是错误的,因为某些静态数据可能仍未初始化。

当然,在单个文件(编译单元)中,我们可以安排静态文件。但是在典型的程序中,有很多文件。有没有办法保证在初始化共享库中的所有其他静态数据后,肯定会调用一个文件中的((构造函数))?

如果我将一个具有静态初始化的文件(构造函数、对象等)放在 gcc 命令行的末尾:

g++ -shared -fPIC source1.o source2.o MyLastInitChance.o

是否保证最后调用此文件的静态构造函数?我进行了实验,当我更改源文件的顺序时,printfs 的顺序会更改;但是它是否在某处指定并保证在编译系统/计算机中相同?

例如,引用:

在链接时,gcc 驱动程序将 crtbegin.o 放在所有之前 可重定位文件和 crtend.o 紧跟在所有之后 可重定位的文件。©

据我了解,上面的引用意味着传递给链接器的 .o 文件的顺序定义了静态初始化的顺序。我说的对吗?

另一个有趣的可能解决方案可能是编写一个调整静态初始化的 GCC 插件(例如,将代码添加到 .ctors 部分等)。但这只是一个想法,也许有人可以扩展。

这里提出了另一种可能的解决方案。简而言之,外部生成后工具可用于对可执行文件(库)中的 .ctors 条目重新排序。但我不是 ELF 格式的专家;我想知道以这种方式调整 .so 文件是否可行且足够容易。

我感兴趣的是解决一个特定的问题,或者证明它不可能解决(至少为什么上面的解决方案不起作用)。

您可以

尝试使用链接器脚本进行ld。你可以在这里阅读更多关于它的信息,但我想你正在寻找的是

.ctors : { *(SORT(.ctors)) MyLastInitChance.o(SORT(.ctors)) }
.dtors : { *(SORT(.dtors)) MyLastInitChance.o(SORT(.dtors)) }

SECTIONS{...}块中。这应该重新排列.ctors部分,以便提供的文件将调用它的构造函数作为最后一个。显然,如果您发现自己需要更高级的解决方案,也可以使用;

提示:编写自己的链接脚本很乏味。使用 ld--verbose 选项打印出使用的链接脚本并对其进行修改。然后使用-T开关添加链接脚本。

attribute((constructor))__的最大优点是与每个块相关的优先级,特别有助于您的情况。

您有两个具有数据危害的代码块(应首先执行一组代码)。这不能通过单个静态块来实现。使用一个静态块和一个属性((构造函数))__ 不会解决您的问题,因为顺序混淆。

处理这个问题的最好方法是有两个具有两个不同优先级的属性((构造函数))__。将现有静态初始化块移动到较高优先级 (0),将另一个代码块移动到较低优先级。

看到这个: http://gcc.gnu.org/ml/gcc-help/2011-05/msg00220.html 答案 http://gcc.gnu.org/ml/gcc-help/2011-05/msg00221.html

特别是引用答案:

所有具有 init_priority 属性的对象都是在之前构造的 没有init_priority属性的任何对象。

请注意,它实际上是关于__attribute__((init_priority))而不是关于__attribute__((constructor))但我相信它们实际上都在 gcc 中使用相同的代码,分别在 gnu 链接器中。首先只是对应于C++对象,即调用它们的构造函数/析构函数,后者是关于将特定函数标记为构造函数或析构函数。

恕我直言,__attribute__((constructor))的存在主要是因为 C,而不是C++。

你能为这个全局实现"首次使用时构造"模式吗?

例如

Magic& gMagic()
{
    static Magic magic;
    return magic;
}

它将在所有常规静态 ctor 之后构建,但在任何常规代码需要它之前。