Parece bastante claro que se supone que debe configurar las cosas.
- ¿Cuándo se ejecuta exactamente?
- ¿Por qué hay dos paréntesis?
- Es
__attribute__
una función? Una macro? ¿Sintaxis? - ¿Funciona esto en C? C ++?
- ¿La función con la que funciona debe ser estática?
- ¿Cuándo
__attribute__((destructor))
corre?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
fuente
#define __attribute__(x)
). Si tiene varios atributos, por ejemplo,__attribute__((noreturn, weak))
sería difícil "eliminar" si solo hubiera un conjunto de paréntesis..init/.fini
. (Puede tener válidamente múltiples constructores y destructores en una sola unidad de traducción, no importa múltiples en una sola biblioteca, ¿cómo funcionaría?) En cambio, en plataformas que usan formato binario ELF (Linux, etc.), se hace referencia a los constructores y destructores en las secciones.ctors
y.dtors
del encabezado. Es cierto que en los viejos tiempos, las funciones nombradasinit
yfini
se ejecutarían en la carga y descarga de la biblioteca dinámica si existieran, pero eso está en desuso ahora, reemplazado por este mejor mecanismo.__attribute__
es si no está usando gcc, ya que también es una extensión de gcc..init
/.fini
no está en desuso. Todavía es parte del estándar ELF y me atrevería a decir que será para siempre. El código en.init
/.fini
es ejecutado por el cargador / runtime-linker cuando el código se carga / descarga. Es decir, en cada código ELF de carga (por ejemplo, una biblioteca compartida).init
se ejecutará. Todavía es posible usar ese mecanismo para lograr casi lo mismo que con__attribute__((constructor))/((destructor))
. Es de la vieja escuela pero tiene algunos beneficios..ctors
/.dtors
mecanismo, por ejemplo, requiere soporte de system-rtl / loader / linker-script. Esto está lejos de ser seguro para estar disponible en todos los sistemas, por ejemplo, sistemas profundamente integrados donde el código se ejecuta en metal desnudo. Es decir, incluso si__attribute__((constructor))/((destructor))
es compatible con GCC, no es seguro que se ejecutará, ya que depende del vinculador organizarlo y del cargador (o, en algunos casos, del código de arranque) ejecutarlo. Para usar.init
/ en su.fini
lugar, la forma más fácil es usar banderas de enlace: -init & -fini (es decir, desde la línea de comandos de GCC, la sintaxis sería-Wl -init my_init -fini my_fini
).En el sistema que admite ambos métodos, un beneficio posible es que el código
.init
se ejecuta antes.ctors
y el código.fini
después.dtors
. Si el orden es relevante, esa es al menos una forma cruda pero fácil de distinguir entre las funciones init / exit.Una desventaja importante es que no puedes tener más de uno
_init
y uno_fini
función por cada módulo cargable y probablemente tendría que fragmentar el código más.so
que motivado. Otra es que cuando se usa el método de enlace descrito anteriormente, uno reemplaza las_fini
funciones _init y predeterminadas originales (proporcionadas porcrti.o
). Aquí es donde generalmente se produce todo tipo de inicialización (en Linux aquí es donde se inicializa la asignación de variable global). Aquí se describe una forma de evitarloObserve en el enlace anterior que
_init()
no se necesita una conexión en cascada con el original, ya que todavía está en su lugar. Elcall
de la línea de montaje es sin embargo x86-mnemotécnica y llamar a una función del conjunto se vería completamente diferente para muchas otras arquitecturas (como ARM, por ejemplo). Es decir, el código no es transparente..init
Los mecanismos /.fini
y.ctors
/.detors
son similares, pero no del todo. El código en.init
/ se.fini
ejecuta "tal cual". Es decir, puede tener varias funciones en.init
/.fini
, pero AFAIK es sintácticamente difícil ponerlas allí de forma totalmente transparente en C puro sin romper el código en muchos.so
archivos pequeños ..ctors
/.dtors
están organizados de manera diferente que.init
/.fini
..ctors
Las.dtors
secciones / son solo tablas con punteros a funciones, y el "llamador" es un bucle proporcionado por el sistema que llama a cada función indirectamente. Es decir, la llamada de bucle puede ser específica de la arquitectura, pero como es parte del sistema (si es que existe), no importa.El siguiente fragmento agrega nuevos punteros de función a la
.ctors
matriz de funciones, principalmente de la misma manera que lo__attribute__((constructor))
hace (el método puede coexistir con__attribute__((constructor)))
.También se pueden agregar los punteros de función a una sección autoinventada completamente diferente. Un script enlazador modificado y una función adicional que imita el cargador
.ctors
/.dtors
tal caso, se necesita bucle. Pero con él se puede lograr un mejor control sobre el orden de ejecución, agregar argumentos y manejo de código de retorno eta (en un proyecto de C ++, por ejemplo, sería útil si necesita algo que se ejecute antes o después de los constructores globales).Preferiría
__attribute__((constructor))/((destructor))
siempre que sea posible, es una solución simple y elegante, incluso se siente como hacer trampa. Para codificadores de metal desnudo como yo, esto no siempre es una opción.Alguna buena referencia en el libro Linkers & loaders .
fuente
__attribute__((constructor))/((destructor))
el destructor no se ejecuta. Intenté algunas cosas, como agregar una entrada a .dtor como se muestra arriba. Pero no hay éxito. El problema es fácil de duplicar ejecutando el código con numactl. Por ejemplo, suponga que test_code contiene el destructor (agregue un printf a las funciones de constructor y desctructor para depurar el problema). Entonces correLD_PRELOAD=./test_code numactl -N 0 sleep 1
. Verá que el constructor se llama dos veces pero el destructor solo una vez.Esta página proporciona una gran comprensión sobre
constructor
ydestructor
implementación de atributos y las secciones dentro de dentro de ELF que les permitan trabajar. Después de digerir la información proporcionada aquí, compilé un poco de información adicional y (tomando prestado el ejemplo de la sección de Michael Ambrus anterior) creé un ejemplo para ilustrar los conceptos y ayudar a mi aprendizaje. Esos resultados se proporcionan a continuación junto con la fuente de ejemplo.Como se explica en este hilo, los atributos
constructor
ydestructor
crean entradas en la sección.ctors
y.dtors
del archivo de objeto. Puede colocar referencias a funciones en cualquier sección de una de tres maneras. (1) usando elsection
atributo; (2)constructor
ydestructor
atributos o (3) con una llamada de ensamblaje en línea (como se hace referencia al enlace en la respuesta de Ambrus).El uso de
constructor
y losdestructor
atributos le permiten asignar adicionalmente una prioridad al constructor / destructor para controlar su orden de ejecución antes de quemain()
se llame o después de que regrese. Cuanto menor sea el valor de prioridad dado, mayor será la prioridad de ejecución (las prioridades más bajas se ejecutan antes de las prioridades más altas antes de main () - y después de las prioridades más altas después de main ()). Los valores de prioridad que proporcione deben ser mayores que100
cuando el compilador reserva valores de prioridad entre 0-100 para la implementación. Aconstructor
odestructor
especificado con prioridad se ejecuta antes de aconstructor
odestructor
especificado sin prioridad.Con el atributo 'section' o con el ensamblaje en línea, también puede colocar referencias de funciones en la sección de código
.init
y.fini
ELF que se ejecutará antes de cualquier constructor y después de cualquier destructor, respectivamente. Cualquier función llamada por la referencia de función colocada en la.init
sección, se ejecutará antes de la referencia de función en sí (como de costumbre).He tratado de ilustrar cada uno de esos en el siguiente ejemplo:
salida:
El ejemplo ayudó a cimentar el comportamiento del constructor / destructor, con suerte también será útil para otros.
fuente
MAX_RESERVED_INIT_PRIORITY
), y que eran los mismos que C ++ (init_priority
) 7.7 C ++: atributos específicos de variables, funciones y tipos . Luego probé con99
:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
.Aquí hay un ejemplo "concreto" (y posiblemente útil ) de cómo, por qué y cuándo usar estas construcciones prácticas pero antiestéticas ...
Xcode utiliza un "valor predeterminado de usuario" "global" para decidir qué
XCTestObserver
clase arroja su corazón a la consola asediada .En este ejemplo ... cuando implícitamente cargo esta psuedo-biblioteca, llamémosla ...
libdemure.a
, a través de una bandera en mi objetivo de prueba á la ..Quiero..
En la carga (es decir, cuando
XCTest
carga mi paquete de prueba), anule laXCTest
clase "predeterminada" "observador" ... (a través de laconstructor
función) PD: Por lo que puedo decir ... cualquier cosa que se haga aquí podría hacerse con un efecto equivalente dentro de mi+ (void) load { ... }
método de claseejecutar mis pruebas ... en este caso, con menos verbosidad en los registros (implementación a pedido)
Regrese la
XCTestObserver
clase "global" a su estado original ... para no estropear otrasXCTest
carreras que no se han subido al carro (aliaslibdemure.a
. Supongo que esto se hizo históricamente endealloc
... pero no voy a comenzar a jugar con esa vieja bruja.Entonces...
Sin la bandera de enlace ... (La policía de la moda enjambre Cupertino exigiendo represalias , sin embargo, el defecto de Apple prevalece, como se desea, aquí )
CON la
-ldemure.a
bandera del vinculador ... (Resultados comprensibles, jadeo ... "graciasconstructor
/destructor
" ... Multitud aplaude )fuente
Aquí hay otro ejemplo concreto: es para una biblioteca compartida. La función principal de la biblioteca compartida es comunicarse con un lector de tarjetas inteligentes. Pero también puede recibir 'información de configuración' en tiempo de ejecución a través de udp. El udp es manejado por un hilo que DEBE iniciarse en el momento de inicio.
La biblioteca fue escrita en c.
fuente