__do_global_ctors_aux未显示在 objdump 中

__do_global_ctors_aux not shown in objdump

本文关键字:objdump 显示 aux do global ctors      更新时间:2023-10-16

请考虑以下代码:

#include <stdio.h> 
void __attribute__ ((constructor)) a_constructor() 
{ 
    printf("%sn", __func__); 
}
void __attribute__ ((constructor)) b_constructor() 
{ 
    printf("%sn", __func__); 
} 
int main() 
{ 
    printf("%sn",__func__); 
}

我将上面的代码编译为:gcc -ggdb prog2.c -o prog2。代码按预期运行。

a_constructor
b_constructor
main

但是当我看到它的转储使用 objdump -d prog2 > f .既没有呼吁__do_global_ctors_aux _init或其他任何地方的任何地方,也没有__do_global_ctors_aux的定义。那么,构造函数是如何被调用的呢?__do_global_ctors_aux的定义在哪里?这是一些优化吗?

我也尝试在没有像这样的优化的情况下编译它:gcc -ggdb -O0 prog2.c -o prog2.请澄清。编译是在 32 位 Linux 机器上完成的。

编辑

我从 gdb bt 输出的是:

Breakpoint 1, a_constructor () at prog2.c:5
5       printf("%sn", __func__); 
(gdb) bt
#0  a_constructor () at prog2.c:5
#1  0x080484b2 in __libc_csu_init ()
#2  0xb7e31a1a in __libc_start_main (main=0x8048445 <main>, argc=1, argv=0xbffff014, init=0x8048460 <__libc_csu_init>, 
fini=0x80484d0 <__libc_csu_fini>, rtld_fini=0xb7fed180 <_dl_fini>, stack_end=0xbffff00c) at libc-start.c:246
#3  0x08048341 in _start ()

那么,构造函数是如何被调用的呢?

如果你看一下用gcc -g -O0 -S -fverbose-asm prog2.c -o prog2.s产生的拆卸,有以下几点:

    .text
.Ltext0:
    .globl  a_constructor
    .type   a_constructor, @function
a_constructor:
.LFB0:
    .file 1 "test.c"
    .loc 1 4 0
    .cfi_startproc
    pushq   %rbp    #
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp  #,
    .cfi_def_cfa_register 6
    .loc 1 5 0
    movl    $__func__.2199, %edi    #,
    call    puts    #
    .loc 1 6 0
    popq    %rbp    #
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   a_constructor, .-a_constructor
    .section    .init_array,"aw"
    .align 8
    .quad   a_constructor

在上面,函数a_constructor放在.text部分。指向该函数的指针也附加到.init_array部分。在调用main之前,glibc 会遍历此数组并调用在那里找到的所有构造函数。

详细信息是特定于实现的,您不会提及您的实现。

某些实现使用的一个完全有效的策略是创建一个运行时库,其中包含程序的实际入口点。该真正的入口点首先调用所有构造函数,然后调用main。如果您的程序是动态链接的,并且该真实入口点背后的代码驻留在共享库中(例如,libc),那么清楚地反汇编程序不可能显示构造函数的调用位置。

确定调用精确

来源的一种简单方法是在调试器中加载程序,在其中一个构造函数上设置断点,并在命中断点时请求调用堆栈。例如,在Cygwin上:

$ gdb ./testGNU gdb (GDB) 7.8版权所有 (C) 2014 自由软件基金会公司。许可证 GPLv3+:GNU GPL 版本 3 或更高版本这是自由软件:您可以自由更改和重新分发它。在法律允许的范围内,没有保证。 键入"显示复制"和"显示保修"了解详细信息。此 GDB 配置为"i686-pc-cygwin"。键入"显示配置"以获取配置详细信息。有关错误报告说明,请参阅:.在线查找 GDB 手册和其他文档资源:.如需帮助,请键入"帮助"。键入"apropos word"以搜索与"word"相关的命令...正在从 ./test 中读取符号...做。(GDB) b a_constructor0x4011c6处的断点 1:文件 test.cc,第 5 行。(GDB) 运行起始程序:/home/Harald van Dijk/test[新线程 4440.0x1734][新线程 4440.0xa8c]b_constructor断点 1,a_constructor () 在 test.cc:5 处5 printf("%s", __func__);(GDB) 英国电信#0 a_constructor () 在 test.cc:5#1 0x61006986 in __main () 来自/usr/bin/cygwin1.dll#2 0x004011f6在主 () 中 test.cc:14(分行)
这表明在 Cygwin

上,使用了我提到的策略的变体:真正的入口点是 main 函数,但编译器在开始时插入对特定于 Cygwin 的 __main 函数的调用,并且该函数__main搜索所有构造函数并直接调用它们。

(顺便说一下,如果递归调用main显然会中断:构造函数将第二次运行。这就是为什么C++不允许递归调用main的原因。C 确实允许它,但是,标准 C 没有构造函数。

你可以得到一个提示,说明__main函数如何搜索它们,方法是不反汇编可执行程序,而是要求编译器提供生成的程序集:

$ gcc -S test.c -o -

我不会在此处复制整个程序集列表,但它表明在此特定实现中,构造函数在.ctors段中发出,因此__main函数很容易简单地调用该段中的所有函数,而无需编译器逐个枚举每个此类函数。