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

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

#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 ()

0

Решение

Итак, как же вызывать конструкторы?

Если вы посмотрите на разборку, произведенную с 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 перебирает этот массив и вызывает все найденные там функции конструктора.

1

Другие решения

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

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

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

$ gdb ./test
GNU GDB (GDB) 7,8
Copyright (C) 2014 Free Software Foundation, Inc.
Лицензия GPLv3 +: GNU GPL версии 3 или более поздней
Это бесплатное программное обеспечение: вы можете свободно изменять и распространять его.
НЕ ПРЕДОСТАВЛЯЕТСЯ ГАРАНТИИ, если это разрешено законом. Введите «показать копирование» и «показать гарантию» для деталей.
Этот GDB был настроен как «i686-pc-cygwin».
Напечатайте "show configuration" для деталей конфигурации.
Инструкции по сообщению об ошибках смотрите:
,
Найдите руководство GDB и другие источники документации в Интернете по адресу:
,
Для получения справки введите «помощь».
Введите «apropos word» для поиска команд, связанных с «word» ...
Чтение символов из ./test...done.
(gdb) b a_constructor
Точка останова 1 в 0x4011c6: файл test.cc, строка 5.
(GDB) запустить
Стартовая программа: / home / Харальд ван Дейк / test
[Новая тема 4440.0x1734]
[Новая тема 4440.0xa8c]
b_constructor

Точка останова 1, a_constructor () в test.cc:5
5 printf ("% s \ n", __func__);
(GDB) Bt
# 0 a_constructor () в test.cc:5
# 1 0x61006986 в __main () из /usr/bin/cygwin1.dll
# 2 0x004011f6 в main () в test.cc:14
(GDB)

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

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

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

$ gcc -S test.c -o -

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

0

По вопросам рекламы [email protected]