それは物事を設定することになっていることはかなり明らかなようです。
__attribute__
は関数ですか?マクロ?構文?__attribute__((destructor))
はいつ実行されますか?__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
そのため、コンストラクタとデストラクタの動作方法は、共有オブジェクトファイルに、コンストラクタとデストラクタの属性でマークされた関数への参照をそれぞれ含む特別なセクション(ELFの.ctorと.dtor)が含まれることです。ライブラリがロード/アンロードされると、ダイナミックローダープログラム(ld.soなど)は、そのようなセクションが存在するかどうかを確認し、存在する場合は、参照されている関数を呼び出します。
考えてみると、通常の静的リンカーにはおそらく似たような魔法があるので、ユーザーが静的リンクと動的リンクのどちらを選択しても、同じコードが起動/シャットダウンで実行されます。
.init
/.fini
は非推奨ではありません。それはまだELF標準の一部であり、私はそれが永遠に続くことを敢えて言うだろう。 .init
/.fini
のコードは、コードのロード/アンロード時にローダー/ランタイムリンカーによって実行されます。つまり各ELFロード(共有ライブラリなど)で.init
のコードが実行されます。 __attribute__((constructor))/((destructor))
とほぼ同じことを達成するために、そのメカニズムを使用することはまだ可能です。それは古い学校ですが、いくつかの利点があります。
たとえば、.ctors
/.dtors
メカニズムには、system-rtl/loader/linker-scriptによるサポートが必要です。これは、コードがベアメタル上で実行される深く埋め込まれたシステムなど、すべてのシステムで利用できるかどうかはまだはっきりしていません。つまり__attribute__((constructor))/((destructor))
がGCCでサポートされている場合でも、それを整理するのはリンカ次第であり、実行するローダー(または場合によってはブートコード)次第であるため、実行されるかどうかは不明です。代わりに.init
/.fini
を使用する最も簡単な方法は、リンカーフラグ-init&-finiを使用することです(つまり、GCCコマンドラインから、構文は-Wl -init my_init -fini my_fini
になります)。
両方の方法をサポートするシステムでは、1つの可能な利点は、.init
のコードが.ctors
の前に実行され、.fini
のコードが.dtors
の後に実行されることです。順序が関連する場合、それは少なくとも1つの粗雑ですが、init/exit関数を区別する簡単な方法です。
主な欠点は、ロード可能なモジュールごとに複数の_init
関数と1つの_fini
関数を簡単に作成できないことであり、おそらく動機付けよりも多くの.so
でコードをフラグメント化する必要があることです。もう1つは、上記のリンカーメソッドを使用する場合、元の_initおよび_fini
デフォルト関数(crti.o
で提供)を置き換えることです。これは通常、あらゆる種類の初期化が行われる場所です(Linuxでは、グローバル変数の割り当てが初期化される場所です)。それを回避する方法は here
上記のリンクで、元の_init()
へのカスケードはまだ存在しているため必要ありません。ただし、インラインアセンブリのcall
はx86ニーモニックであり、アセンブリから関数を呼び出すと、他の多くのアーキテクチャ(たとえばARMなど)でまったく異なるように見えます。つまりコードは透過的ではありません。
.init
/.fini
と.ctors
/.detors
のメカニズムは似ていますが、完全ではありません。 .init
/.fini
のコードは「現状のまま」実行されます。つまり.init
/.fini
に複数の関数を含めることができますが、多くの小さな.so
ファイルのコードを分割せずに純粋なCで完全に透過的に配置することは構文的に困難です。
.ctors
/.dtors
は、.init
/.fini
とは異なる構成です。 .ctors
/.dtors
セクションは両方とも関数へのポインタを持つ単なるテーブルであり、「呼び出し元」は各関数を間接的に呼び出すシステム提供のループです。つまりループ呼び出し元はアーキテクチャ固有である場合がありますが、システムの一部であるため(存在する場合)、それは重要ではありません。
次のスニペットは、.ctors
関数配列に新しい関数ポインターを追加します。これは主に__attribute__((constructor))
と同じ方法です(メソッドは__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;
完全に異なる自己発明セクションに関数ポインターを追加することもできます。このような場合、変更されたリンカースクリプトと、ローダー.ctors
/.dtors
ループを模倣する追加関数が必要です。しかし、それを使用すると、実行順序をより適切に制御し、引数を追加して、コード処理などを返すことができます。 (たとえば、C++プロジェクトでは、グローバルコンストラクターの前または後に何かを実行する必要がある場合に役立ちます)。
可能な場合は__attribute__((constructor))/((destructor))
を好みます。それは不正行為のように感じても、シンプルでエレガントなソリューションです。私のようなベアメタルコーダーにとって、これは常にオプションとは限りません。
本のいくつかの良いリファレンス リンカーとローダー .
このページでは、constructor
およびdestructor
属性の実装と、それらを機能させるELF内のセクションについて十分に理解できます。ここで提供された情報を消化した後、少しの追加情報をコンパイルし(上記のMichael Ambrusのセクションの例を借りて)、概念を説明し、学習に役立つ例を作成しました。これらの結果は、サンプルのソースとともに以下に提供されます。
このスレッドで説明したように、constructor
およびdestructor
属性は、オブジェクトファイルの.ctors
および.dtors
セクションにエントリを作成します。 3つの方法のいずれかで、いずれかのセクションに関数への参照を配置できます。 (1)section
属性を使用します。 (2)constructor
およびdestructor
属性、または(3)インラインアセンブリ呼び出し(Ambrusの回答のリンクを参照)。
constructor
およびdestructor
属性を使用すると、main()
が呼び出される前または戻った後に、コンストラクター/デストラクターに優先順位を追加して実行の順序を制御できます。指定された優先度の値が低いほど、実行優先度が高くなります(低い優先度は、main()の前に高い優先度の前に実行され、main()の後に高い優先度の後に実行されます)。コンパイラが実装のために0〜100の優先度値を予約しているため、指定する優先度値は100
より大きくなければなりませんです。優先度なしで指定されたconstructor
またはdestructor
の前に、優先度付きで指定されたAconstructor
またはdestructor
が実行されます。
'section'属性またはインラインアセンブリを使用すると、コンストラクタの前およびデストラクタの後にそれぞれ実行される.init
および.fini
ELFコードセクションに関数参照を配置することもできます。 .init
セクションに配置された関数参照によって呼び出される関数は、関数参照自体の前に(通常どおり)実行されます。
以下の例でそれらのそれぞれを説明しようとしました:
#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;
}
出力:
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
この例は、コンストラクタ/デストラクタの動作を強化するのに役立ちました。他の人にも役立つことを願っています。
以下に、これらの便利な、しかし見苦しい構造を使用するhow、why、whenの「具体的な」(およびおそらく便利な)の例を示します...
Xcodeは「グローバル」「ユーザーデフォルト」を使用して、どのXCTestObserver
クラスがハートアウトをbereagueredconsoleに吐き出すかを決定します。
この例では...この擬似ライブラリを暗黙的にロードするときに、テスト対象のフラグを介して... libdemure.a
を呼び出しましょう。
OTHER_LDFLAGS = -ldemure
したい..
ロード時(つまり、XCTest
がテストバンドルをロードするとき)、「デフォルト」XCTest
「observer」クラスをオーバーライドします...(constructor
関数を介して)クラスの+ (void) load { ... }
メソッド内の効果。
私のテストを実行します...
「グローバルな」XCTestObserver
クラスを元の状態に戻します。時流に乗っていない他のXCTest
実行を汚さないように(別名:libdemure.a
にリンク)。これは歴史的にdealloc
で行われたと思いますが、私はその古いハグをいじり始めるつもりはありません。
そう...
#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
リンカフラグなし...(ファッションポリスの群れクパチーノ報復を要求する、それでもAppleのデフォルトが優先、望まれるように、ここで)
WITH -ldemure.a
リンカーフラグ...(わかりやすい結果、gasp... "thanks constructor
/destructor
" ... Crowd cheers)
次に、別の具体的な例を示します。これは共有ライブラリ用です。共有ライブラリの主な機能は、スマートカードリーダーと通信することです。ただし、udpを介して実行時に「構成情報」を受け取ることもできます。 udpは、MUST init時に開始されるスレッドによって処理されます。
__attribute__((constructor)) static void startUdpReceiveThread (void) {
pthread_create( &tid_udpthread, NULL, __feigh_udp_receive_loop, NULL );
return;
}
ライブラリはcで作成されました。