Расширение компоновщика gcc __attribute __ ((конструктор)) вызывает сбой в main () - PullRequest
0 голосов
/ 16 февраля 2019

Сначала у меня есть файл singleton.cpp для создания одноэлементного объекта и объявления экземпляра с использованием атрибута ((конструктор))

#include<iostream>
using namespace std;
class singleton{
public:
    singleton(){cout<<"singleton ctor\n";}
};
__attribute__((constructor)) static void beforeFunction()
{
    printf("beforeFunction\n");
    singleton obj;
}

и простого main.cpp

#include<iostream>
using namespace std;
int main(){
    return 0;
}

Я создаю main.cpp singleton.cpp вместе:

g++ singleton.cpp main.cpp -o main

./main
beforeFunction
Segmentation fault

Так почему моя программа падает, что случилось?Как это исправить?Я использую GCC на Ubuntu.Большое спасибо.

Ответы [ 3 ]

0 голосов
/ 16 февраля 2019

Я воспроизвел это с g++ (Debian 7.3.0-5), а также g++ (GCC) 9.0.0 20180902 (experimental).

Интересно, что это не получается:

$ g++ singleton.cpp main.cpp && ./a.out
beforeFunction
Segmentation fault

, но это работает, как и ожидалось:

$ g++ main.cpp singleton.cpp && ./a.out
beforeFunction
singleton ctor

Как правильно сказал Акорн, механизм iostream / std::cout не был должным образом инициализирован к тому времени, когда вы вызвали одноэлементный конструктор.Это происходит потому, что в main.oonly main.o) есть специальный код, который вызывает std::ios_base::Init::Init().И только потому, что main.cpp имеет постороннее #include <iostream>.

Как это исправить?

Лучшее решение - вообще не использовать __attribute__((constructor)) .В вашем случае нет причин делать то, что вы делаете.Сделайте это вместо:

// singleton2.cpp
#include<iostream>
using namespace std;

class singleton{
  public:
    singleton(){cout<<"singleton ctor\n";}
};

static singleton obj;

С приведенным выше кодом работает любой порядок связывания:

$ g++ main.cpp singleton2.cpp && ./a.out
singleton ctor

$ g++ singleton2.cpp main.cpp && ./a.out
singleton ctor

Если вы настаиваете на использовании __attribute__((constructor)), то убедитесь, что на вашем компьютере стоит main.oлиния связи перед любым другим объектом, который может использовать iostream s.

0 голосов
/ 16 февраля 2019

std::cout инициализируется с помощью статического объекта C ++, см. <iostream>:

  // For construction of filebuffers for cout, cin, cerr, clog et. al.
  static ios_base::Init __ioinit;

Поскольку ваш код опирается как на этот статический конструктор, так и на конструктор ELF, он запускается в этот GCC ограничение :

Однако в настоящее время порядок вызова конструкторов для объектов C ++ со статической продолжительностью хранения и функций, снабженных атрибутом constructor, не определен.

Если вместо этого вы используете объект C ++, порядок будет четко определен.В руководстве GCC также предлагается использовать атрибут init_priority , но поскольку вы не можете применить его к определению __ioinit (кроме как с помощью взлома препроцессором), я не думаю, что это полезно в этом контексте.

0 голосов
/ 16 февраля 2019

Так почему же происходит сбой моей программы, что случилось?

Скорее всего, машинный механизм iostream еще не инициализирован во время выполнения функций __attribute__((constructor)).

Как это исправить?

Либо используйте CI / O, например printf, что, похоже, работает в вашем случае;или, что лучше, избегайте использования __attribute__((constructor)) в целом (это не стандарт C или C ++, т. е. делает вашу программу непереносимой).

Обратите внимание, что __attribute__((constructor)) не требуется для создания синглетонов или глобальных объектов.

...