.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関数を区別する簡単な方法です。
主な欠点は、ロード可能なモジュールごとに_init
1つ以上の_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つしかないと「マクロアウト」するのが難しくなります。