как именно работает \_\_attribute \_\_ ((конструктор))?

<p>кажется, довольно ясно, что он должен все настроить.</p><ol> <li>когда именно он запускается?</li> <li>почему есть две скобки?</li> <li>является ли <code>\_\_attribute\_\_</code> функцией? макрос? синтаксис?</li> <li>это работает в c? c++?</li> <li>должна ли функция, с которой она работает, быть статичной?</li> <li>когда запускается <code>\_\_attribute\_\_((destructor))</code>?</li> </ol><p> <a href="https://stackoverflow.com/questions/2046426/initialising-a-static-variable-in-objective-c-category/2046997#2046997">пример в objective-c</a> :</p><pre><code>\_\_attribute\_\_((constructor)) static void initialize\_navigationbarimages() { navigationbarimages = [[nsmutabledictionary alloc] init]; } \_\_attribute\_\_((destructor)) static void destroy\_navigationbarimages() { [navigationbarimages release]; } </code></pre>

<ol> <li>он запускается при загрузке разделяемой библиотеки, обычно во время запуска программы.</li> <li>вот как все атрибуты gcc; по-видимому, чтобы отличить их от вызовов функций.</li> <li>gcc-специфический синтаксис.</li> <li>да, это работает на c и c++.</li> <li>нет, функция не должна быть статичной.</li> <li>деструктор запускается, когда разделяемая библиотека выгружается, как правило, при выходе из программы.</li> </ol><p>таким образом, способ, которым работают конструкторы и деструкторы, состоит в том, что совместно используемый объектный файл содержит специальные разделы (.ctors и .dtors в elf), которые содержат ссылки на функции, отмеченные соответственно атрибутами конструктора и деструктора. когда библиотека загружается/выгружается, программа динамического загрузчика (ld.so или somesuch) проверяет, существуют ли такие разделы, и, если это так, вызывает функции, на которые есть ссылки.</p><p>если подумать, в обычном статическом компоновщике, возможно, есть нечто подобное, так что один и тот же код запускается при запуске/завершении работы независимо от того, выбирает ли пользователь статическую или динамическую компоновку.</p>

<p><code>.init</code>/<code>.fini</code> не считается устаревшим. это все еще часть стандарта elf, и я бы осмелился сказать, что так будет всегда. код в <code>.init</code>/<code>.fini</code> запускается загрузчиком/компоновщиком времени выполнения, когда код загружается/выгружается. то есть при каждой загрузке elf (например, совместно используемой библиотеки) будет выполняться код в <code>.init</code>. по-прежнему возможно использовать этот механизм для достижения примерно того же, что и с <code>\_\_attribute\_\_((constructor))/((destructor))</code>. это старая школа, но у нее есть некоторые преимущества.</p><p>например, для механизма <code>.ctors</code>/<code>.dtors</code> требуется поддержка system-rtl/loader/linker-script. это далеко не обязательно будет доступно во всех системах, например, глубоко встроенных системах, где код выполняется на голом железе. то есть даже если gcc поддерживает <code>\_\_attribute\_\_((constructor))/((destructor))</code>, он не уверен, что он будет работать, поскольку компоновщик может его организовать, а загрузчик (или, в некоторых случаях, загрузочный код) его запустит. чтобы использовать вместо этого <code>.init</code>/<code>.fini</code>, проще всего использовать флаги компоновщика: -init & -fini (т.е. из командной строки gcc, синтаксис будет <code>-wl -init my\_init -fini my\_fini</code>).</p><p>в системе, поддерживающей оба метода, одним из возможных преимуществ является то, что код в <code>.init</code> запускается до <code>.ctors</code>, а код в <code>.fini</code> после <code>.dtors</code>. если порядок важен, это как минимум один грубый, но простой способ отличить функции инициализации/выхода.</p><p>основным недостатком является то, что вы не можете легко иметь более одной функции <code>\_init</code> и одной функции <code>\_fini</code> на каждый загружаемый модуль и, вероятно, придется фрагментировать код в большем количестве <code>.so</code>, чем мотивировано. другое состоит в том, что при использовании метода компоновщика, описанного выше, один заменяет исходные функции по умолчанию \_init и <code>\_fini</code> (предоставляемые <code>crti.o</code>). это где все виды инициализации обычно происходят (в linux это где глобальное назначение переменных инициализируется). обход пути, который описан <a href="http://www.flipcode.com/archives/calling\_a\_function\_at\_elf\_shared\_library\_load\_time.shtml">здесь</a> </p><p>обратите внимание, что в приведенной выше ссылке каскадирование к исходной функции <code>\_init()</code> не требуется, так как оно все еще на месте однако <code>call</code> во встроенной сборке является x86-мнемоническим, и вызов функции из assembly будет выглядеть совершенно по-разному для многих других архитектур (например, arm). то есть код не прозрачен.</p><p><code>.init</code>/<code>.fini</code> и <code>.ctors</code>/<code>.detors</code> похожи, но не совсем. код в <code>.init</code>/<code>.fini</code> выполняется "как есть". то есть у вас может быть несколько функций в <code>.init</code>/<code>.fini</code>, но синтаксически afaik трудно полностью прозрачно поместить их туда в чистом c, не разбивая код во многих маленьких файлах <code>.so</code>.</p><p><code>.ctors</code>/<code>.dtors</code> организованы иначе, чем <code>.init</code>/<code>.fini</code>. разделы <code>.ctors</code>/<code>.dtors</code> являются просто таблицами с указателями на функции, а "вызывающий" является системным циклом, который вызывает каждую функцию косвенно. то есть вызывающий цикл может зависеть от архитектуры, но, поскольку он является частью системы (т. е. вообще существует), это не имеет значения.</p><p>следующий фрагмент добавляет новые указатели функций в массив функций <code>.ctors</code>, главным образом так же, как <code>\_\_attribute\_\_((constructor))</code> (метод может сосуществовать с <code>\_\_attribute\_\_((constructor)))</code>.</p><pre><code>#define section( s ) \_\_attribute\_\_ ((section ( s ))) void test(void) { printf("hello\n"); } void (*funcptr)(void) section(".ctors") =test; void (*funcptr2)(void) section(".ctors") =test; void (*funcptr3)(void) section(".dtors") =test; </code></pre><p>можно также добавить указатели функций в совершенно другой раздел, придуманный самим собой. в этом случае требуется модифицированный скрипт компоновщика и дополнительная функция, имитирующая цикл <code>.ctors</code>/<code>.dtors</code> загрузчика. но с его помощью можно лучше контролировать порядок выполнения, добавлять аргументы и возвращать код обработки e.t.a. (например, в проекте c++ было бы полезно, если бы что-то требовалось до или после глобальных конструкторов).</p><p>я бы предпочел <code>\_\_attribute\_\_((constructor))/((destructor))</code>, где это возможно, это простое и элегантное решение, даже если оно похоже на читерство. для голых металлистов, таких как я, это не всегда вариант.</p><p>несколько хороших ссылок в книге <a href="http://www.becbapatla.ac.in/cse/naveenv/docs/ll1.pdf"><em> линкеры и загрузчики </em></a> .</p>

<p>эта страница дает хорошее представление о реализации атрибутов <code>constructor</code> и <code>destructor</code> и разделах внутри elf, которые позволяют им работать. после усвоения информации, представленной здесь, я собрал немного дополнительной информации и (заимствуя пример раздела у майкла амбруса выше) создал пример, чтобы проиллюстрировать концепции и помочь моему обучению. эти результаты представлены ниже вместе с примером источника.</p><p>как объяснялось в этой теме, атрибуты <code>constructor</code> и <code>destructor</code> создают записи в разделах <code>.ctors</code> и <code>.dtors</code> объектного файла. вы можете разместить ссылки на функции в любом разделе одним из трех способов. (1) используя любой атрибут <code>section</code>; (2) атрибуты <code>constructor</code> и <code>destructor</code> или (3) с вызовом inline-assembly (как указано по ссылке в ответе амбруса).</p><p>использование атрибутов <code>constructor</code> и <code>destructor</code> позволяет дополнительно назначить приоритет конструктору/деструктору для управления порядком его выполнения до вызова <code>main()</code> или после его возврата. чем ниже заданное значение приоритета, тем выше приоритет выполнения (более низкие приоритеты выполняются до более высоких приоритетов перед main () - и после более высоких приоритетов после main ()). заданные вами значения приоритета <strong>должны быть больше <code>100</code></strong>, так как компилятор резервирует значения приоритета от 0 до 100 для реализации. a<code>constructor</code> или <code>destructor</code>, указанное с приоритетом, выполняется до того, как <code>constructor</code> или <code>destructor</code> указано без приоритета.</p><p>с атрибутом section или с inline-assembly вы также можете разместить ссылки на функции в разделе кода elf <code>.init</code> и <code>.fini</code>, который будет выполняться перед любым конструктором и после любого деструктора, соответственно. любые функции, вызываемые ссылкой на функцию, размещенную в разделе <code>.init</code>, будут выполняться перед самой ссылкой на функцию (как обычно).</p><p>я попытался проиллюстрировать каждый из них в следующем примере:</p><pre><code>#include <stdio.h> #include <stdlib.h> /* test function utilizing attribute 'section' ".ctors"/".dtors" to create constuctors/destructors without assigned priority. (provided by michael ambrus in earlier answer) */ #define section( s ) \_\_attribute\_\_ ((section ( s ))) void test (void) { printf("\n\ttest() utilizing -- (.section .ctors/.dtors) w/o priority\n"); } void (*funcptr1)(void) section(".ctors") =test; void (*funcptr2)(void) section(".ctors") =test; void (*funcptr3)(void) section(".dtors") =test; /* functions constructx, destructx use attributes 'constructor' and 'destructor' to create prioritized entries in the .ctors, .dtors elf sections, respectively. note: priorities 0-100 are reserved */ void construct1 () \_\_attribute\_\_ ((constructor (101))); void construct2 () \_\_attribute\_\_ ((constructor (102))); void destruct1 () \_\_attribute\_\_ ((destructor (101))); void destruct2 () \_\_attribute\_\_ ((destructor (102))); /* init\_some\_function() - called by elf\_init() */ int init\_some\_function () { printf ("\n init\_some\_function() called by elf\_init()\n"); return 1; } /* elf\_init uses inline-assembly to place itself in the elf .init section. */ int elf\_init (void) { \_\_asm\_\_ (".section .init \n call elf\_init \n .section .text\n"); if(!init\_some\_function ()) { exit (1); } printf ("\n elf\_init() -- (.section .init)\n"); return 1; } /* function definitions for constructx and destructx */ void construct1 () { printf ("\n construct1() constructor -- (.section .ctors) priority 101\n"); } void construct2 () { printf ("\n construct2() constructor -- (.section .ctors) priority 102\n"); } void destruct1 () { printf ("\n destruct1() destructor -- (.section .dtors) priority 101\n\n"); } void destruct2 () { printf ("\n destruct2() destructor -- (.section .dtors) priority 102\n"); } /* main makes no function call to any of the functions declared above */ int main (int argc, char *argv[]) { printf ("\n\t [ main body of program ]\n"); return 0; } </code></pre><p>результат:</p><pre><code>init\_some\_function() called by elf\_init() elf\_init() -- (.section .init) construct1() constructor -- (.section .ctors) priority 101 construct2() constructor -- (.section .ctors) priority 102 test() utilizing -- (.section .ctors/.dtors) w/o priority test() utilizing -- (.section .ctors/.dtors) w/o priority [ main body of program ] test() utilizing -- (.section .ctors/.dtors) w/o priority destruct2() destructor -- (.section .dtors) priority 102 destruct1() destructor -- (.section .dtors) priority 101 </code></pre><p>этот пример помог закрепить поведение конструктора/деструктора, надеюсь, он будет полезен и другим.</p>

<p>вот "конкретный" (и <em>, возможно, полезный </em>) пример <em> как, почему и когда </em> использовать эти удобные, но <strong>неприглядные</strong> конструкции ...</p><p>xcode использует "глобальное" "пользовательское значение по умолчанию", чтобы решить, какой класс <code>xctestobserver</code> <em> извергает свое сердце </em> в <em> осажденному </em> консоль.</p><p>в этом примере ... когда я неявно загружаю эту psuedo-библиотеку, давайте назовем ее ... <code>libdemure.a</code>, с помощью флага в моем тестовом объекте.</p><pre><code>other\_ldflags = -ldemure </code></pre><p>я бы хотел..</p><ol> <li> <p>при загрузке (т. е. когда <code>xctest</code> загружает мой тестовый комплект) переопределите класс "по умолчанию" <code>xctest</code> "наблюдатель" ... (через функцию <code>constructor</code>) ps: насколько я могу сказать ... все, что здесь сделано, может быть сделано с помощью эквивалента эффект внутри моего класса '<code>+ (void) load { ... }</code> метод.</p> </li> <li> <p>запустить мои тесты .... в этом случае с меньшим количеством пустых подробностей в журналах (реализация по запросу)</p> </li> <li> <p>верните "глобальный" класс <code>xctestobserver</code> в его первоначальное состояние .. чтобы не запутывать другие запуски <code>xctest</code>, которые не попали в команду (или связанные с <code>libdemure.a</code>). я предполагаю, что это исторически было сделано в <code>dealloc</code> .. но я не собираюсь начинать возиться с этой старой каргой.</p> </li> </ol><p>так...</p><pre><code>#define user\_defs nsuserdefaults.standarduserdefaults @interface demuretestobserver : xctestobserver @end @implementation demuretestobserver \_\_attribute\_\_((constructor)) static void hijack\_observer() { /*! here i totally hijack the default logging, but you can use multiple observers, just csv them, i.e. "@"demuretestobserverm,xctestlog" */ [user\_defs setobject:@"demuretestobserver" forkey:@"xctestobserverclass"]; [user\_defs synchronize]; } \_\_attribute\_\_((destructor)) static void reset\_observer() { // clean up, and it's as if we had never been here. [user\_defs setobject:@"xctestlog" forkey:@"xctestobserverclass"]; [user\_defs synchronize]; } ... @end </code></pre><p>без флага компоновщика ... (рой моды-полиции купертино <strong>требующий возмездия</strong>, но по умолчанию apple по-прежнему преобладает, <em>, как и здесь, </em>)</p><p><img src="https://i.stack.imgur.com/hogsy.png" alt="enter image description here"></p><p>с флагом компоновщика <code>-ldemure.a</code> ... (понятные результаты, <em> gasp </em> ... "спасибо <code>constructor</code>/<code>destructor</code>" ... <em> толпа ура </em>) <img src="https://i.stack.imgur.com/7pgzz.png" alt="enter image description here"></p>

<p>вот еще один конкретный пример. это для общей библиотеки. основная функция общей библиотеки - связь с устройством чтения смарт-карт. но он также может получать "информацию о конфигурации" во время выполнения через udp. udp обрабатывается потоком, который <strong>должен</strong> запускаться во время инициализации.</p><pre><code>\_\_attribute\_\_((constructor)) static void startudpreceivethread (void) { pthread\_create( &tid\_udpthread, null, \_\_feigh\_udp\_receive\_loop, null ); return; } </code></pre><p>библиотека была написана в c.</p>