__attribute __((构造函数))调用命令混淆

时间:2012-06-19 18:14:19

标签: c++ linux gcc shared-libraries static-initialization

答案here表明__attribute __((构造函数))在静态初始化之后不被称为,它在声明顺序中被调用。

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

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

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

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

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

这个文件的静态构造函数是否保证最后被调用?我试验过,当我改变源文件的顺序时,printfs的顺序改变了;但它是否在某处指定并保证在编译系统/计算机上相同?

例如,引用:

  

在链接时,gcc驱动程序会立即放置crtbegin.o   所有的后面的可重定位文件和crtend.o   可重定位文件。 ©

据我所知,上面的引用暗示传递给链接器的.o文件的顺序定义了静态初始化的顺序。我是对的吗?

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

另一个可能的解决方案是presented。简而言之,可以使用外部构建后工具来重新排序可执行文件(库)中的.ctors条目。但我不是ELF格式的专家;我想知道这是否可行并且很容易以这种方式调整.so文件。

我感兴趣的是解决某个特定问题,或证明无法解决(至少为什么上述解决方案不起作用)。

4 个答案:

答案 0 :(得分:12)

您可以尝试使用ld的链接描述文件。您可以阅读更多相关信息here,但我想您要找的是

.ctors : { *(SORT(.ctors)) MyLastInitChance.o(SORT(.ctors)) }
.dtors : { *(SORT(.dtors)) MyLastInitChance.o(SORT(.dtors)) }
SECTIONS{...}块中的

。这应该重新排列.ctors部分,因此提供的文件将把它的构造函数称为最后一个。显然,如果您发现自己需要一个更先进的解决方案;)

提示:编写自己的链接脚本非常繁琐。使用ld的{​​{1}}选项打印出已使用的链接脚本并对其进行修改。然后使用--verbose开关添加您的链接脚本。

答案 1 :(得分:2)

属性((构造函数))__的最大优势是与每个块关联的优先级,特别有助于您的情况。

您有两个具有数据危险的代码块(应首先执行一个代码集)。单个静态块无法实现这一点。使用一个静态块和一个属性((构造函数))__将无法解决您的问题,因为有关订单的混淆。

解决问题的最佳方法是使用两个不同的优先级来拥有两个属性((构造函数))__。将现有静态初始化块移至更高优先级(0),将另一个代码块移至更低优先级。

答案 2 :(得分:2)

请参阅: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))但是我相信它们实际上分别在gnu链接器中使用gcc中的相同代码。首先只对应C ++对象,即调用它们的构造函数/析构函数,后者是将特定函数标记为构造函数或析构函数。

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

答案 3 :(得分:2)

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

e.g。

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

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