.init/ .finiは非推奨ではありません。それはまだELF標準の一部であり、それは永遠に続くと思います。.init/ 内の.finiコードは、コードがロード/アンロードされるときにloader / runtime-linkerによって実行されます。つまり、ELFロード(共有ライブラリなど)ごとにコード.initが実行されます。そのメカニズムを使用して、とほぼ同じことを実現することはまだ可能 __attribute__((constructor))/((destructor))です。それは古い学校ですが、いくつかの利点があります。
.ctors/ .dtorsメカニズムは、たとえばsystem-rtl / loader / linker-scriptによるサポートを必要とします。これは、コードがベアメタルで実行される深く埋め込まれたシステムなど、すべてのシステムで利用できるとはほど遠いものです。つまり__attribute__((constructor))/((destructor))、GCCでサポートされている場合でも、それを構成するのはリンカー次第であり、それを実行するのはローダー(場合によってはブートコード)なので、実行されるかどうかは不明です。代わりに.init/ を使用するため.finiの最も簡単な方法は、リンカーフラグを使用することです-Wl -init my_init -fini my_fini。
両方の方法をサポートするシステムでは、1つの可能な利点は、の.init前.ctorsにコードが実行され、.fini後にコードが実行されることです.dtors。順序が関連している場合、それは少なくとも1つの粗雑ですが、init関数とexit関数を区別する簡単な方法です。
主な欠点は、ロード可能なモジュールごとに_init1つ以上の_fini関数を簡単に持つことができず、おそらく.soモチベーションを上げるよりもコードをフラグメント化する必要があることです。もう1つは、上記のリンカーメソッドを使用すると、元の_initおよび_finiデフォルト関数(によって提供されるcrti.o)が置き換えられることです。これは通常、あらゆる種類の初期化が行われる場所です(Linuxでは、これがグローバル変数割り当てが初期化される場所です)。その回避策はここで説明されています
上記のリンクでは、元の_init()場所へのカスケードはまだ残っているため必要ないことに注意してください。callアセンブリがインラインでは、x86-ニーモニック及びアセンブリ(例えば、ARMなど)、他の多くのアーキテクチャで完全に異なって見えることになるから関数を呼び出しています。つまり、コードは透過的ではありません。
.init/ .finiと.ctors/の.detorsメカニズムは似ていますが、完全ではありません。.init/のコードは.fini「そのまま」実行されます。つまり、.init/ .finiにいくつかの関数を含めることが.soできますが、多くの小さなファイルのコードを分割せずに純粋なCで完全に透過的にそれらを配置することは、AFAIKの構文上困難です。
.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))可能な場合は私が好みますが、それはごまかしのように感じても、シンプルでエレガントなソリューションです。私のようなベアメタルプログラマーにとって、これは必ずしも選択肢ではありません。
『リンカーとローダー』という本の良い参考文献。
#define __attribute__(x))が簡単になります。たとえばのように複数の属性__attribute__((noreturn, weak))がある場合、ブラケットのセットが1つしかないと「マクロアウト」するのが難しくなります。