.init/ .fininon è deprecato. Fa ancora parte dello standard ELF e oserei dire che lo sarà per sempre. Il codice in .init/ .finiviene eseguito dal caricatore / runtime-linker quando il codice viene caricato / scaricato. Vale a dire su ogni carico ELF (ad esempio una libreria condivisa) .initverrà eseguito il codice in . È ancora possibile utilizzare quel meccanismo per ottenere quasi la stessa cosa di __attribute__((constructor))/((destructor)). È vecchia scuola ma ha alcuni vantaggi.
.ctorsIl .dtorsmeccanismo / , ad esempio, richiede il supporto di system-rtl / loader / linker-script. Questo è tutt'altro che certo disponibile su tutti i sistemi, ad esempio i sistemi profondamente integrati in cui il codice viene eseguito su bare metal. Vale a dire anche se __attribute__((constructor))/((destructor))è supportato da GCC, non è certo che verrà eseguito poiché spetta al linker organizzarlo e al caricatore (o in alcuni casi, codice di avvio) per eseguirlo. Per usare .init/ .finiinvece, il modo più semplice è usare i flag di linker: -init & -fini (cioè dalla riga di comando di GCC, la sintassi sarebbe -Wl -init my_init -fini my_fini).
Sul sistema che supporta entrambi i metodi, un possibile vantaggio è che il codice in .initviene eseguito prima .ctorse il codice in .finidopo .dtors. Se l'ordine è rilevante, questo è almeno un modo semplice ma semplice per distinguere tra le funzioni init / exit.
Un grosso svantaggio è che non puoi avere facilmente più di una _inite una _finifunzione per ciascun modulo caricabile e probabilmente dovresti frammentare il codice in più .soche motivato. Un altro è che quando si utilizza il metodo linker sopra descritto, si sostituiscono le funzioni originali _init e _finipredefinite (fornite da crti.o). Questo è dove di solito si verificano tutti i tipi di inizializzazione (su Linux è qui che viene inizializzata l'assegnazione delle variabili globali). Un modo per aggirare questo è descritto qui
Si noti nel collegamento sopra che _init()non è necessario un collegamento in cascata all'originale in quanto è ancora in atto. L' callassemblaggio in linea è tuttavia x86-mnemonico e la chiamata di una funzione dall'assemblaggio sembrerebbe completamente diversa per molte altre architetture (come ad esempio ARM). Cioè il codice non è trasparente.
.initI meccanismi / .finie .ctors/ .detorssono simili, ma non del tutto. Il codice in .init/ .finiviene eseguito "così com'è". Vale a dire che puoi avere diverse funzioni in .init/ .fini, ma sintatticamente AFAIK è difficile metterle lì in modo completamente trasparente in C puro senza rompere il codice in molti piccoli .sofile.
.ctors/ .dtorssono organizzati diversamente da .init/ .fini. .ctorsLe .dtorssezioni / sono entrambe tabelle con puntatori a funzioni e il "chiamante" è un ciclo fornito dal sistema che chiama indirettamente ciascuna funzione. Vale a dire il loop-caller può essere specifico dell'architettura, ma poiché fa parte del sistema (se esiste affatto, cioè) non importa.
Il frammento seguente aggiunge nuovi puntatori di .ctorsfunzione all'array di funzioni, principalmente allo stesso modo __attribute__((constructor))(il metodo può coesistere con __attribute__((constructor))).
#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;
Si possono anche aggiungere i puntatori di funzione a una sezione auto-inventata completamente diversa. In questo caso sono necessari uno script linker modificato e una funzione aggiuntiva che imita il caricatore .ctors/ .dtorsloop. Ma con esso si può ottenere un migliore controllo sull'ordine di esecuzione, aggiungere l'argomento e la gestione del codice di ritorno eta (In un progetto C ++, ad esempio, sarebbe utile se fosse necessario eseguire qualcosa prima o dopo i costruttori globali).
Preferirei __attribute__((constructor))/((destructor))dove possibile, è una soluzione semplice ed elegante anche se ti sembra di barare. Per i programmatori bare metal come me, questa non è sempre un'opzione.
Qualche buona referenza nel libro Linker e caricatori .
#define __attribute__(x)). Se hai più attributi, ad esempio,__attribute__((noreturn, weak))sarebbe difficile "eliminare" se esistesse solo un set di parentesi.