.init/ .fininão está obsoleto. Ainda faz parte do padrão ELF e ouso dizer que será para sempre. O código em .init/ .finié executado pelo carregador / runtime-linker quando o código é carregado / descarregado. Ou seja, em cada código de carregamento ELF (por exemplo, uma biblioteca compartilhada) .initserá executado. Ainda é possível usar esse mecanismo para obter a mesma coisa que com __attribute__((constructor))/((destructor)) . É à moda antiga, mas tem alguns benefícios.
.ctors/ .dtorsmecanismo, por exemplo, requer suporte do system-rtl / loader / linker-script. Isso está longe de estar disponível em todos os sistemas, por exemplo, sistemas profundamente embutidos nos quais o código é executado no bare metal. Ou seja, mesmo que o __attribute__((constructor))/((destructor))GCC seja suportado, não é certo que ele funcione, pois cabe ao vinculador organizá-lo e ao carregador (ou, em alguns casos, código de inicialização) executá-lo. Para usar .init/ em .finivez disso, a maneira mais fácil é usar sinalizadores de vinculador: -init & -fini (ou seja, na linha de comando do GCC, a sintaxe seria -Wl -init my_init -fini my_fini).
No sistema que suporta os dois métodos, um benefício possível é que o código in .inité executado antes .ctorse o código .finidepois .dtors. Se a ordem for relevante, é pelo menos uma maneira simples, porém fácil de distinguir entre as funções de inicialização / saída.
Uma grande desvantagem é que você não pode facilmente ter mais de uma _inite uma _finifunção por cada módulo carregável e provavelmente teria que fragmentar o código em mais do .soque motivado. Outra é que, ao usar o método vinculador descrito acima, um substitui as _finifunções _init e padrão originais (fornecidas por crti.o). É aqui que geralmente ocorrem todos os tipos de inicialização (no Linux é onde a atribuição global de variáveis é inicializada). Uma maneira de contornar isso é descrita aqui
Observe no link acima que uma cascata no original _init()não é necessária, pois ainda está em vigor. ocall entanto, na montagem embutida é x86-mnemônico e chamar uma função de montagem pareceria completamente diferente para muitas outras arquiteturas (como o ARM, por exemplo). Ou seja, o código não é transparente.
.init/ .finie .ctors/ .detorsmecanismos são semelhantes, mas não exatamente. Código em .init/ .finiexecutado "como está". Ou seja, você pode ter várias funções em .init/ .fini, mas é AFAIK sintaticamente difícil colocá-las totalmente transparentes em C puro, sem quebrar o código em muitos .soarquivos pequenos .
.ctors/ .dtorssão organizados de forma diferente de .init/ .fini. .ctors/.dtors seções são apenas tabelas com ponteiros para funções, e o "chamador" é um loop fornecido pelo sistema que chama cada função indiretamente. Ou seja, o chamador de loop pode ser específico da arquitetura, mas como faz parte do sistema (se é que existe), isso não importa.
O fragmento a seguir adiciona novos ponteiros de .ctorsfunção à matriz de funções, principalmente da mesma maneira que __attribute__((constructor))faz (o método pode coexistir __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;
Pode-se também adicionar os ponteiros de função a uma seção auto-inventada completamente diferente. Nesse caso, é necessário um script vinculador modificado e uma função adicional que imite o carregador .ctors/ .dtorsloop. Mas, com ele, é possível obter um melhor controle sobre a ordem de execução, adicionar argumentos e retornar código eta (em um projeto C ++, por exemplo, seria útil se você precisasse de algo em execução antes ou depois dos construtores globais).
Eu preferiria sempre __attribute__((constructor))/((destructor))que possível, é uma solução simples e elegante, mesmo que pareça trapaça. Para codificadores bare-metal como eu, isso nem sempre é uma opção.
Alguma boa referência no livro Linkers & loaders .
#define __attribute__(x)). Se você tiver vários atributos, por exemplo,__attribute__((noreturn, weak))seria difícil "macro out" se houvesse apenas um conjunto de colchetes.