Parece bastante claro que é suposto configurar as coisas.
- Quando exatamente ele funciona?
- Por que existem dois parênteses?
- É
__attribute__
uma função? Uma macro? Sintaxe? - Isso funciona em C? C ++?
- A função com a qual trabalha precisa ser estática?
- Quando
__attribute__((destructor))
funciona?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
c++
objective-c
c
gcc
Casebash
fonte
fonte
#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..init/.fini
. (Você pode validamente ter vários construtores e destruidores em uma única unidade de tradução, não se preocupe com vários em uma única biblioteca - como isso funcionaria?) Em vez disso, em plataformas que usam o formato binário ELF (Linux, etc.), os construtores e destruidores são referenciados nas seções.ctors
e.dtors
do cabeçalho. É verdade que, antigamente, as funções nomeadasinit
efini
seriam executadas com carregamento e descarregamento dinâmico de biblioteca, se existissem, mas isso foi preterido agora, substituído por esse mecanismo melhor.__attribute__
é se você não estiver usando o gcc, pois também é uma extensão do gcc..init
/.fini
nã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).init
será 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
/.dtors
mecanismo, 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.fini
vez 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.ctors
e o código.fini
depois.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
_init
e uma_fini
função por cada módulo carregável e provavelmente teria que fragmentar o código em mais do.so
que motivado. Outra é que, ao usar o método vinculador descrito acima, um substitui as_fini
funções _init e padrão originais (fornecidas porcrti.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 aquiObserve 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
/.fini
e.ctors
/.detors
mecanismos são semelhantes, mas não exatamente. Código em.init
/.fini
executado "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.so
arquivos pequenos ..ctors
/.dtors
sã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
.ctors
função à matriz de funções, principalmente da mesma maneira que__attribute__((constructor))
faz (o método pode coexistir__attribute__((constructor)))
.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
/.dtors
loop. 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 .
fonte
__attribute__((constructor))/((destructor))
o destruidor não é executado. Tentei algumas coisas, como adicionar uma entrada ao .dtor, como mostrado acima. Mas sem sucesso. É fácil duplicar o problema executando o código com numactl. Por exemplo, suponha que test_code contenha o destruidor (adicione um printf às funções construtor e desctrutor para depurar o problema). Então corraLD_PRELOAD=./test_code numactl -N 0 sleep 1
. Você verá que o construtor é chamado duas vezes, mas destruidor apenas uma vez.Esta página fornece grande compreensão sobre o
constructor
edestructor
implementação atributo e as seções dentro dentro ELF que lhes permitem trabalhar. Depois de digerir as informações fornecidas aqui, eu compilei um pouco de informações adicionais e (emprestando o exemplo da seção de Michael Ambrus acima) criei um exemplo para ilustrar os conceitos e ajudar meu aprendizado. Esses resultados são fornecidos abaixo, juntamente com a fonte de exemplo.Conforme explicado neste encadeamento, os atributos
constructor
edestructor
criam entradas na seção.ctors
e.dtors
do arquivo de objeto. Você pode colocar referências a funções em qualquer seção de uma das três maneiras. (1) usando osection
atributo; (2)constructor
edestructor
atributos ou (3) com uma chamada de montagem em linha (conforme referenciado no link na resposta da Ambrus).O uso de
constructor
edestructor
atributos permitem atribuir adicionalmente uma prioridade ao construtor / destruidor para controlar sua ordem de execução antes demain()
ser chamado ou depois que ele retornar. Quanto mais baixo for o valor da prioridade, mais alta será a prioridade de execução (as prioridades mais baixas serão executadas antes das prioridades mais altas antes de main () - e subseqüentes às prioridades mais altas depois de main ()). Os valores de prioridade que você fornecer devem ser maiores do100
que o compilador reserva valores de prioridade entre 0 e 100 para implementação. Umconstructor
oudestructor
especificado com prioridade é executado antes de umconstructor
oudestructor
especificado sem prioridade.Com o atributo 'section' ou com assembly em linha, você também pode colocar referências de função na seção de código
.init
e.fini
ELF que será executada antes de qualquer construtor e após qualquer destruidor, respectivamente. Quaisquer funções chamadas pela referência de função colocada na.init
seção serão executadas antes da própria referência de função (como de costume).Eu tentei ilustrar cada uma delas no exemplo abaixo:
resultado:
O exemplo ajudou a cimentar o comportamento construtor / destruidor, espero que seja útil para outros também.
fonte
MAX_RESERVED_INIT_PRIORITY
), e que eram os mesmos que C ++ (init_priority
) 7.7 C ++ - Atributos específicos de variável, função e tipo . Então eu tentei com99
:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
.Aqui está um exemplo "concreto" (e possivelmente útil ) de como, por que e quando usar essas construções úteis, porém desagradáveis ...
O Xcode usa um "global" "padrão do usuário" para decidir qual
XCTestObserver
classe vomita seu coração no console sitiado .Neste exemplo ... quando eu carrego implicitamente essa biblioteca psuedo, vamos chamá-la ...
libdemure.a
, através de uma bandeira no meu destino de teste à la ..Eu quero..
No carregamento (ou seja, quando
XCTest
carrega meu pacote de teste), substitua aXCTest
classe "padrão" "observador" ... (através daconstructor
função) PS: Tanto quanto eu sei ... tudo o que é feito aqui pode ser feito com efeito equivalente dentro do meu+ (void) load { ... }
método de classe ' .executar meus testes .... neste caso, com menos verbosidade inana nos logs (implementação mediante solicitação)
Retorne a
XCTestObserver
classe "global" ao seu estado primitivo .. para não estragar outrasXCTest
corridas que ainda não entraram no movimento (também conhecido como .linkedlibdemure.a
). Acho que isso foi feito historicamente emdealloc
... mas não vou começar a mexer com aquela velha bruxa.Assim...
Sem a bandeira do vinculador ... (A polícia da moda enxame Cupertino exigindo retribuição , mas o padrão da Apple prevalece, conforme desejado, aqui )
COM a
-ldemure.a
bandeira do vinculador ... (Resultados compreensíveis, suspiro ... "obrigadoconstructor
/destructor
" ... Aplausos da multidão )fonte
Aqui está outro exemplo concreto: é para uma biblioteca compartilhada. A principal função da biblioteca compartilhada é se comunicar com um leitor de cartão inteligente. Mas também pode receber 'informações de configuração' em tempo de execução pelo udp. O udp é tratado por um thread que DEVE ser iniciado no momento do init.
A biblioteca foi escrita em c.
fonte