C++ ctor вопрос (linux)

  • среда: linux, пользовательское приложение, созданное с помощью g ++ из пары файлов C++ (результат - ELF)

  • есть проблема (SIGSEGV) при обходе списка конструкторов

    ( __CTOR_LIST__ )

(примечание: код, вызываемый через этот список, является своего рода инициализацией системы для каждого класса, нет конструктор-код, который я написал)

  • когда я правильно понимаю каждую единицу компиляции (каждый .o, созданный из .cpp) создает одну запись в
    __CTOR_LIST__ 
  • проблема (SIGSEGV) не существует, когда я перехожу через GDB через программу

  • для отладки я ищу способ добавить собственный код code перед зов

    "_do_global_ctors_aux"

какие-нибудь намеки на это?

Благодарность,

Уве

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
3 930
3

Ответы 3

Возможно, вас укусит так называемое «Фиаско статического порядка инициализации».

По сути, когда существует более одной единицы трансляции (то есть исходный файл C++), и каждый файл определяет глобальный объект, компилятор / компоновщик C++ не может определить, какую из них построить в первую очередь. Если x зависит от того, что y создается первым, но случайно компиляция / компоновка приводит к тому, что x создается раньше y, программа обычно аварийно завершает работу. См. Пункт [10.12] C++ FAQ Lite для получения более подробной информации. Пункт [10.13] содержит решение - идиому «построить при первом использовании».

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

Чтобы собственная функция вызывалась перед другой функцией-конструктором, у вас есть атрибут constructor (priority), описанный здесь. 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))
  }

Вы хотели бы дать ему низкий приоритет, чтобы он выполнялся перед другими зарегистрированными функциями ctor, имеющими более высокий приоритет. Однако на первый взгляд кажется, что конструкторы без номера будут выполнены первыми. Не совсем уверен. Лучше всего попробовать. Если вы хотите, чтобы ваша функция вызывалась даже до _do_global_ctors_aux, вы должны выпустить исходную функцию _init, которая обычно выполняется, когда ваша программа загружается загрузчиком ELF (посмотрите опцию -init для ld). Прошло какое-то время с тех пор, как я испортил его, но я помню, что он должен выполнять некоторые важные детали инициализации, поэтому я не буду пытаться его заменить. Попробуйте использовать атрибут конструктора, с которым я связан. Однако будьте очень осторожны. Ваш код, возможно, будет выполнен до того, как будут созданы другие важные объекты, такие как cout.

Обновлять: Я провел тест, и он фактически выполняет функции ctor в обратном порядке. Таким образом, функции ctor, которые связаны первыми, выполняются позже. Этот код находится в crtstuff.c исходного кода gcc:

  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: warning: constructor priorities from 0 to 100 are reserved for the implementation

Я протестировал его, взломав doit и dothat, и он вызвал их в ожидаемом нами порядке. Повеселись!

Не тот вопрос, который вы задали, но ...

В C++ / g ++ у вас может быть класс, в котором объявленные методы [header] никогда не определяются в исходных файлах [.cc], если эти методы никогда не вызываются.

Как следствие, вы можете скопировать свои текущие файлы кода во временный каталог, выполнить над ними работу по взлому и косой чертой, запустить [ручной] двоичный поиск и довольно быстро локализовать проблему.

Не элегантно, но очень эффективно.


Помимо печально известной проблемы «Статический порядок инициализации», есть также более эзотерические случаи, такие как тот, который недавно указал мне здесь, на SO by Чарльз Бейли (см. Комментарии)..

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

Возникнет аналогичная проблема с загрузкой ядра.

Другие вопросы по теме