答案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文件。
我感兴趣的是解决某个特定问题,或证明无法解决(至少为什么上述解决方案不起作用)。
答案 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之后构建,但在任何常规代码需要它之前。