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

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

    ( __CTOR_LIST__ )

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

  • 当我正确理解每个编译单元(从 .cpp 创建的每个 .o)都会在
    __CTOR_LIST__ 
  • 当我通过程序通过 GDB 时,问题(SIGSEGV)不存在

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

    “_do_global_ctors_aux”

有什么提示吗?

谢谢,

乌韦

4

3 回答 3

7

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

要使自己的函数在其他构造函数之前被调用,您需要在此处constructor (priority)描述一个属性。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 之前调用你的函数,你必须释放_init当你的程序被 ELF 加载器加载时通常执行的原始函数(查看-initld 的选项)。我已经有一段时间没有搞砸它了,但我记得它必须做一些初始化的私密细节,所以我不会尝试替换它。尝试使用我链接到的构造函数属性。但是,要非常小心。您的代码可能会在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我们的属性 priority 是如何隐式创建的部分0。现在,如果您给予它优先级,gcc 会发出警告,这是完全正确的:p

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

我通过中断doitand对其进行了测试dothat,它以我们期望的顺序调用它们。玩得开心!

于 2009-01-07T14:07:31.400 回答
1

有可能你被所谓的“静态初始化命令惨败”给咬了。

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

于 2009-01-07T14:01:43.063 回答
0

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

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

因此,您可以将当前代码文件复制到一个临时目录,对它们进行 hack 和 slash 工作,运行 [手动] 二进制搜索,并相当快地隔离问题。

不优雅,但非常有效。


除了臭名昭著的“静态初始化顺序”问题之外,还有更多深奥的案例,例如Charles Bailey最近在SO上向我指出的案例(见评论)。

   E.g Mixing:  int p [] = { 1,2,3 };
          And:  extern int * p;

会产生类似的 coredump 问题。

于 2009-01-07T17:15:14.857 回答