C ++ ctor问题(linux)

时间:2009-01-07 13:47:32

标签: c++ linux constructor elf sigsegv

  • 环境:linux,用户空间应用程序通过g ++从几个C ++文件创建 (结果是ELF)

  • 遍历构造函数列表时出现问题(SIGSEGV)

    ( __CTOR_LIST__ )

(注意:通过此列表调用的代码是每个类的一种系统初始化,   我写的构造函数代码)

  • 当我正确理解每个编译单元(每个.o创建一个.cpp) 在
  • 中创建一个条目
    __CTOR_LIST__ 
  • 当我通过程序通过GDB执行时,问题(SIGSEGV)不存在

  • 为了调试这个我正在寻找一种方法来添加自己的代码之前 致电

    "_do_global_ctors_aux"

有任何提示吗?

感谢,

乌韦

3 个答案:

答案 0 :(得分:7)

这可能有很多原因。你访问尚未创建的对象的范围(因为不同的翻译单元创建对象的顺序是未定义的),我认为在这种情况下很可能,并且在构建环境中存在错误。

要在其他构造函数之前调用自己的函数,您有一个constructor (priority)属性描述为here。 GCC为每个文件的构造函数输入节保留优先级。它按照这些优先顺序将它们联系起来。在我的linux系统的链接器脚本中,该代码如下所示(使用ld -verbose输出):

  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }

您可能希望给它一个低优先级,使其在具有更高优先级编号的其他已注册ctor函数之前执行。然而从它的外观来看,似乎首先执行没有数字的构造函数。不完全确定。最好你试一试。如果你想在_do_global_ctors_aux之前调用你的函数,你必须释放在ELF加载程序加载程序时通常执行的原始_init函数(查看ld的-init选项) 。自从我搞砸了它已经有一段时间了,但我记得它必须做一些初始化的细节,所以我不会尝试替换它。尝试使用我链接到的构造函数属性。但是,要非常小心。您的代码可能会在构建其他重要对象(如cout之前)执行。

更新:我做了一个测试,它实际上反过来执行ctor功能。因此,首先链接的ctor函数将在稍后执行。此代码恰好位于gcc源代码的crtstuff.c中:

  func_ptr *p;
  for (p = __CTOR_END__ - 1; *p != (func_ptr) -1; p--)
    (*p) ();

我做了一点测试:

void dothat() { }
struct f {
    f() { dothat(); }
} f_;
void doit() __attribute__((constructor (0)));
void doit() { }
int main() { }

链接--print-map会产生以下输出:

.ctors          0x080494f4       0x10
 *crtbegin.o(.ctors)                 
 .ctors         0x080494f4        0x4 /usr/lib/gcc/i686-pc-linux-gnu/4.3.2/crtbegin.o
 *crtbegin?.o(.ctors)                                                                
 *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)                                        
 .ctors         0x080494f8        0x4 /tmp/ccyzWBjs.o                                
 *(SORT(.ctors.*))                                                                   
 .ctors.65535   0x080494fc        0x4 /tmp/ccyzWBjs.o                                
 *(.ctors)                                                                           
 .ctors         0x08049500        0x4 /usr/lib/gcc/i686-pc-linux-gnu/4.3.2/crtend.o  

请注意.ctors.65535是如何通过属性优先级0隐式创建的部分。现在,如果你给予它优先权,gcc警告并且这是完全正确的:p

  

test.cpp:7:警告:从0到100的构造函数优先级保留用于实现

我通过打破doitdothat来测试它,并按我们预期的顺序调用它们。玩得开心!

答案 1 :(得分:1)

你可能被所谓的“静态初始化订单Fiasco”所困扰。

基本上,当有多个翻译单元(即C ++源文件),并且每个文件定义一个全局对象时,C ++编译器/链接器无法确定首先构造哪个。如果x依赖于首先构造的y,但偶然编译/链接会导致xy之前构建,程序通常会崩溃。有关详细信息,请参阅C++ FAQ Lite的第[10.12]项。 Item [10.13]包含一个解决方案 - “首次使用时构造”的习语。

答案 2 :(得分:0)

不是你问的问题,但是......

在C ++ / g ++中,您可以拥有一个类,其中声明的[header]方法永远不会在源[.cc]文件中实际定义,只要这些方法永远不会被调用。

因此,您可以将当前的代码文件复制到临时目录,对它们执行黑客攻击并删除作业,运行[手动]二进制搜索,并快速隔离问题。

不优雅,但非常有效。


除了臭名昭着的“静态初始化顺序”问题之外,还有更多深奥的案例,例如我最近在SO by Charles Bailey (see the comments).

上指出的那个案例。
   E.g Mixing:  int p [] = { 1,2,3 };
          And:  extern int * p;

会产生类似的coredump问题。