__do_global_ctors_aux не отображается в objdump

0

Рассмотрим следующий код:

#include <stdio.h> 

void __attribute__ ((constructor)) a_constructor() 
{ 
    printf("%s\n", __func__); 
}

void __attribute__ ((constructor)) b_constructor() 
{ 
    printf("%s\n", __func__); 
} 

int main() 
{ 
    printf("%s\n",__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("%s\n", __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
objdump

2 ответа

1
Лучший ответ

Итак, как вызываются конструкторы?

Если вы посмотрите на разборку, произведенную с помощью 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 функция a_constructor помещается в .text раздел. А указатель на функцию также добавляется в раздел .init_array. Перед вызовом main glibc выполняет итерацию по этому массиву и вызывает все найденные там функции-конструкторы.

0

Детали специфичны для реализации, и вы не упоминаете о своей реализации.

Совершенно правильная стратегия, используемая некоторыми реализациями, заключается в создании библиотеки времени выполнения, которая содержит реальную точку входа для вашей программы. Эта реальная точка входа сначала вызывает все конструкторы, а затем вызывает main. Если ваша программа динамически связана и код, стоящий за этой реальной точкой входа, находится в общей библиотеке (например, libc), тогда четкая разборка вашей программы не может показать вам, где вызван конструктор.

Простой подход для определения того, откуда именно идет вызов, - это загрузить вашу программу в отладчик, установить точку останова на одном из конструкторов и запросить стек вызовов при достижении точки останова. Например, на Cygwin:

$ gdb ./test
GNU gdb (GDB) 7.8
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-cygwin".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...done.
(gdb) b a_constructor
Breakpoint 1 at 0x4011c6: file test.cc, line 5.
(gdb) run
Starting program: /home/Harald van Dijk/test
[New Thread 4440.0x1734]
[New Thread 4440.0xa8c]
b_constructor

Breakpoint 1, a_constructor () at test.cc:5
5           printf("%s\n", __func__);
(gdb) bt
#0  a_constructor () at test.cc:5
#1  0x61006986 in __main () from /usr/bin/cygwin1.dll
#2  0x004011f6 in main () at test.cc:14
(gdb)

Это показывает, что на Cygwin используется вариант стратегии, о которой я упоминал: реальная точка входа является main функцией, но компилятор вставляет вызов функции __main специфичной для Cygwin, в начале, и это функция __main которая выполняет поиск для всех конструкторов и вызывает их напрямую.

(Кстати, очевидно, что это прерывается, если main называется рекурсивно: конструкторы будут выполняться во второй раз, поэтому C++ не позволяет main быть вызванным рекурсивно. C разрешает его, но тогда стандарт C не имеет конструктора функции.)

И вы можете понять, как __main функция __main ищет их, не __main исполняемую программу, а спрашивая компилятор для сгенерированной сборки:

$ gcc -S test.c -o -

Я не буду копировать весь список сборок здесь, но он показывает, что в этой конкретной реализации функции конструктора испускаются в сегменте .ctors, поэтому было бы легко, чтобы функция __main просто __main все функции в этом сегменте без компилятор должен перечислить каждую такую функцию один за другим.

  • 0
    Мой вывод для bt отличается от вашего. Я обновил в вопросе.
  • 0
    @TapanAnand Итак, ваша реализация делает то, что я описал во втором абзаце. Это тоже работает.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню