C++ ctor 问题 (linux)

发布于 2024-07-11 13:02:20 字数 510 浏览 8 评论 0 原文

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

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

    ( __CTOR_LIST__ )

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

  • 当我正确理解每个编译单元(从 .cpp 创建的每个 .o)时 创建一个条目
    __CTOR_LIST__ 
  • 当我通过程序单步执行 GDB 时,

  • 用于调试此 I正在寻找一种在之前添加自己的代码的方法 调用

    "_do_global_ctors_aux"

对此有何提示

谢谢,

乌韦

  • environment: linux, userspace-application created via g++ from a couple of C++ files
    (result is an ELF)

  • there is a problem (SIGSEGV) when traversing the constructor list

    ( __CTOR_LIST__ )

(note: code called via this list is a kind of system initialisation for every class,
not the constructor-code I wrote)

  • when I understand correctly every compilation unit (every .o created from a .cpp)
    creates one entry in
    __CTOR_LIST__ 
  • the problem (SIGSEGV) does not exist when I step via GDB through the program

  • for debugging this I'm looking for an way to add own code code before the
    call of

    "_do_global_ctors_aux"

any hints for this ?

thanks,

Uwe

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

独﹏钓一江月 2024-07-18 13:02:20

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

要使自己的函数在其他构造函数之前被调用,您需要定义一个构造函数(优先级)属性此处。 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))
  }

您需要给它一个低优先级,以使其在其他具有更高的优先级编号。 但从表面上看,没有编号的构造函数似乎会先执行。 完全不确定。 最好你尝试一下。 如果您希望在 _do_global_ctors_aux 之前调用您的函数,则必须释放通常在 ELF 加载程序加载程序时执行的原始 _init 函数(查看 -init< /code> ld 的选项)。 自从我搞乱它以来已经有一段时间了,但我记得它必须做一些初始化的亲密细节,所以我不会尝试替换它。 尝试使用我链接到的构造函数属性。 但是,请务必小心。 您的代码可能会在构造其他重要对象(例如 cout)之前执行。

更新:我做了一个测试,它实际上反向执行了ctor函数。 首先链接的 Soctor 函数稍后执行。 这段代码恰好位于 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 来测试它,并按顺序调用它们我们期待。 玩得开心!

There are many possible reasons of this. Ranges from that you access objects not yet created (because order of creation of objects across different translation units is undefined) which i think is quite probable in this case, and ranges to an error on your build-environment.

To make a own function be called before other constructor function, you have a constructor (priority) attribute described here. GCC keeps a priority for each files' constructor input section. And it links them in order of those priorities. In the linker script of my linux system, that code looks like this (output it using 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))
  }

You would want to give it a low priority to make it execute before other registered ctor functions having a higher priority number. However from the looks of it, it seems like constructors having no number will be executed first. Not sure entirely. Best you give it a try. If you want to have your function called even before _do_global_ctors_aux, you have to release the original _init function that is normally executed when your program is loaded by the ELF loader (look into the -init option of ld). It's been some time since i messed with it, but i remember it has to do some intimate details of initialization, so i wouldn't try to replace it. Try using the constructor attribute i linked to. However, be very careful. Your code will possibly be executed before other important objects like cout are constructed.

Update: I did a test, and it actually executes ctor functions in reverse. So ctor functions that are linked first are executed later. This code happens to be in crtstuff.c of the gcc source code:

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

I made a little test:

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

Linking with --print-map yields, among others, this output:

.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  

Notice how .ctors.65535 is the section we implicitly created by our attribute priority 0. Now, if you give it that priority, gcc warns and it's totally right :p

test.cpp:7: warning: constructor priorities from 0 to 100 are reserved for the implementation

I tested it by breaking on doit and dothat, and it called them in the order we expect. Have fun!

幸福还没到 2024-07-18 13:02:20

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

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

因此,您可以将当前的代码文件复制到临时目录,对它们进行修改,运行[手动]二分搜索,并相当快速地隔离问题。

不优雅,但非常有效。


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

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

将产生类似的核心转储问题。

Not the question you asked, but...

In C++/g++, you can have a class where the declared [header] methods are never actually defined in the source [.cc] files as long as those methods are never invoked.

As a consequence, you can copy your current code files to a temporary directory, do a hack and slash job on them, run a [manual] binary search, and isolate the problem fairly quickly.

Not elegant, but highly effective.


Aside from the infamous "Static Initialization Order" issue, there are also more esoteric cases such as the one recently pointed out to me here on SO by Charles Bailey (see the comments).

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

Will produce a similar coredump problem.

又爬满兰若 2024-07-18 13:02:20

您可能被所谓的“静态初始化顺序惨败”所困扰。

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

It's possible that you're being bitten by the so-called "Static Initialization Order Fiasco".

Basically, when there is more than one translation unit (that is, C++ source file), and each file defines a global object, it is not possible for the C++ compiler/linker to establish which to construct first. If x depends on y being constructed first, but by chance compilation/linking causes x to be constructed before y, the program will typically crash. See item [10.12] of the C++ FAQ Lite for more details. Item [10.13] contains a solution -- the "construct on first use" idiom.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文