__attribute__((used)) __attribute__((section(x)))
7000 ワード
1、通知コンパイラは、参照されていない場合でも、ターゲットファイルに静的関数を保持します.
2、attribute_とタグ付け()の関数は、リンクが未使用のセクションを削除しないようにターゲットファイルにマークされます.
3、静的変数はusedとしてマークすることもできます.方法は__です.attribute__((used)).
4、ルーチン
1、section関数のプロパティを使用すると、画像の異なる部分にコードを配置できます.
2、この関数属性はARMコンパイラがサポートするGNUコンパイラ拡張です.
3、ルーチン
次の例ではFunction_Attributes_section_0はRO section new_に配置されます.sectionではなくtextで.
GNUでもARMのコンパイラでも、サポートされています.attribute__指定されたコンパイル属性について、ここではKEIL環境での_attribute__に表示されます.
sectionキーワードは、指定する入力セグメントに変数を定義することができる、以下、具体的な例でsectionの使い方を説明する.
このマクロ定義はやや複雑に見えますが、大丈夫です.ここでは一歩一歩分析します.
まずSECTIONというマクロ定義を見てみましょう.このマクロは変数を入力セグメントに追加することができます.たとえば
int a __attribute__((section(“list”))) = 0; この文の意味はint型の変数aをlistという入力セグメントに置くことである.
levelはこの入力セグメントの接尾辞として理解できるので、この入力セグメントの最終的な名前はlevelに依存し、ここでlevel=“0.end”であれば、このマクロを展開して得られる.
__attribute__((used,__section__(".fn_cmd.""0.end"))この時点で確認します.mapファイルはファイルの中に1つの名前があることを発見することができます.fn_cmd.0.endの入力セグメントは、levelがこの入力セグメントの後半であることがわかります.
ではSECTIONというマクロについて説明しますCMD_START_EXPORT(start_fun,"start_fun");このマクロを初歩的に展開すると、struct CMD_が定義されていることがわかります.LISTタイプの変数でありsectionを用いてその属性を修飾した.
const struct CMD_LIST cmd_fn_start_fun SECTION("0.end") = {start_fun,"start_fun"}; ここで注意したいのは、マクロ式の##記号です.この記号の役割は、2つの文字列をつなぎ合わせることです.例えば、
#define DEF_INT(a,b) int a##b = 0 DEF_INT(a,b); 最終的にこのマクロが実現するのはint型の変数を定義することを意味し、変数の名前はabと呼ばれ、このマクロを展開するのは
int ab = 0; では、わかりました.では、マクロ展開の結果、cmd_という名前を定義した理由がわかります.fn_start_funの変数です.次にマクロ展開をさらに行い,その中のSECTION展開を以下の式にする.
const struct CMD_LIST cmd_fn_start_fun __attribute__((used,__section__(".fn_cmd.""0.end"))) = {start_fun,"start_fun"}; この時に見るとstruct CMDが定義されていることに気づきますLISTタイプの変数、変数の名前はcmd_fn_start_fun、そしてこの変数は私たちが望む入力セグメントに配置された.fn_cmd.0.endが当たった.
では、sectionを使用してカスタム入力セグメントに変数を配置することは何の意味がありますか?従来のC言語プログラミングではプログラム構造がこうであることが知られている.
int main() { init_xx(); init_xx(); ... while(1) { ... }}まずいくつかの初期化プログラムを行い、ループでコードを実行します.このように開発するのはもちろんですが、初期化関数を書くたびにmain関数で呼び出され、非常に不便な点があります.しかしsectionを使用して、事前にすべての初期化関数を自分で定義した入力セグメントに追加してから、main関数でこの入力セグメントの初期化関数を順次取り出すと、main関数を変更せずにシステムの初期化を完了することができます.
ではsectionはどのようにしてこれらの初期化関数を入力セグメントに入れ、システムはこれらの初期化関数のアドレスを取得することができますか?説明する前に、以下で使用するいくつかのマクロ定義について説明します.
#define SECTION(level) __attribute__((used,__section__(".fn_cmd."level))) #define CMD_START_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("0.end") = {func,func_s} #define CMD_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("1") = {func,func_s} #define CMD_END_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("1.end")={func,func_s}これらのマクロが呼び出されるとcmd_という名前が生成されます.fn_xxの変数であり、この変数は呼び出されたマクロに基づいて対応する入力セグメントに配置される.例えばCMD_START_EXPORTというマクロ、このマクロは実は上で述べたように、このマクロを呼び出すとcmd_という名前が生成されます.fn_xxの変数は、この変数をカスタマイズした入力セグメントに配置します.fn_cmd.0.endが当たった.他の2つのマクロも実際には差が少なく、入力セグメントに違いがあります.
本題に戻り、sectionを使用して異なる関数を目的の入力セグメントに配置し、開始アドレスと終了アドレスを取得する方法について説明します.
私たちは各XXX_Init関数の後にマクロCMDが呼び出されます.EXPORT、このマクロを呼び出すとコンパイラがXXX_Initという関数は入力セグメントに組み込まれる.変数は入力セグメントのアドレスが連続しているため、section名01234で順番に並べられ、section内では関数名で並べられる.したがって、これらの初期化関数は、入力セグメントの順序に従って1つずつ呼び出され、システムの初期化を完了することができる.
具体的には、カスタムコマンドラインのアプリケーションに基づいて一部説明します.
/*コマンド関数セグメント開始位置*/int cmd_start(void) { return 0; } CMD_START_EXPORT(cmd_start,"int cmd_start(void)");/*コマンド関数セグメント終了位置*/int cmd_end(void) { return 0; } CMD_END_EXPORT(cmd_end,"int cmd_end(void)"); void test(void) { printf("hello world\r"); } CMD_EXPORT(test,"void test(void)"); void demo(void) { printf("hello world\r"); } CMD_EXPORT(demo,"void demo(void)"); startとend関数を定義し、それぞれCMD_を使用します.START_EXPORTとCMD_END_EXPORTは入力段に入れる.fn_cmd.0.end和.fn_cmd.1.endでは、上記の説明に従ってセグメントを入力.fn_cmd.0.endは入力セグメントに並ぶ.fn_cmd.1.endの前の、使用するCMD_EXPORTというマクロに対応する入力セグメント.fn_cmd.1は列に並んでいます.fn_cmd.0.end和.fn_cmd.1.endの間の、ここでコンパイルによって生成されたものを見ることができる.mapはもっとイメージがよくなります.具体的にMAPファイルの場所は以下の通りです
cmd_fn_cmd_start 0x080042f0 Data 8 serialcmd.o(.fn_cmd.0.end) cmd_fn_test 0x080042f8 Data 8 application.o(.fn_cmd.1) cmd_fn_demo 0x08004300 Data 8 application.o(.fn_cmd.1) cmd_fn_cmd_end 0x08004308 Data 8 serialcmd.o(.fn_cmd.1.1.end)は入力セグメントを見ることができる.fn_cmd.0.endのアドレスは確かに一番前ですが、fn_cmd.1.endのアドレスは確かに一番後ろに並んでいます.入力セグメントfn_cmd.1のデータは私たちが使うデータです.入力セグメントに格納された変数はstruct CMD_であるためLISTタイプは、8バイトで、以下のように定義されています.
typedef void (*fun)(); struct CMD_LIST { fun funs; const INT8 *cmd; }; したがって、各変数のサイズは8であり、すなわち、各変数のメモリ内のオフセットは8であるため、変数のアドレスが毎回8バイト増加する理由がわかる.
次の関数は,入力セグメントからデータを取得し,対応する処理を行う関数である.
const static struct CMD_LIST *CmdList; static UINT8 CmdSize = 0;/*コマンド関数初期化*/void SerialCmdInit(void){const struct CMD_LIST*cmd_ptr;CmdList=&cmd_fn_cmd_start;CmdList+;for(cmd_ptr=CmdList;cmd_ptr<&cmd_fn_cmd_end;cmd_ptr+);{/*ここで初期化に使用する場合、初期化関数を実行するには以下の方法を使用します.私のアプリケーションは初期化に使用されていないので、関数呼び出しは行われません.(*cmd_ptr->fun);*/CmdSize++;}そのうちcmd_fn_cmd_startはfn_cmd.0.endという入力セグメントのうち、各実行する関数は.fn_cmd.1この入力セグメントの、cmd_fn_cmd_endが終了の標識としている.fn_cmd.1.end入力セグメント.残りはあまり説明しないで、上から.mapファイルのアドレスは,開始アドレスと終了アドレスが得られた後,これらの関数をどのように1つずつ遍歴するかを容易に見ることができる.
最後に言う前にattribute__((used,__section__(".fn_cmd.")のusedの意味です.unused:関数または変数が使用されない可能性があることを示します.このプロパティは、コンパイラが警告情報を生成することを回避します.used:このコードをコンパイラに説明するのに役立ち、使用していない場合でもコンパイラは警告しません.
ここで言う必要がありますが、私がKEILを使ってコンパイルしたとき、usedを付けなかったため変数がコンパイラに最適化されたので、このキーワードを追加する必要がある場合があります.——————————————————
2、attribute_とタグ付け()の関数は、リンクが未使用のセクションを削除しないようにターゲットファイルにマークされます.
3、静的変数はusedとしてマークすることもできます.方法は__です.attribute__((used)).
4、ルーチン
static int lose_this(int);
static int keep_this(int) __attribute__((used)); // retained in object file
static int keep_this_too(int) __attribute__((used)); // retained in object file
1、section関数のプロパティを使用すると、画像の異なる部分にコードを配置できます.
2、この関数属性はARMコンパイラがサポートするGNUコンパイラ拡張です.
3、ルーチン
次の例ではFunction_Attributes_section_0はRO section new_に配置されます.sectionではなくtextで.
void Function_Attributes_section_0 (void) __attribute__((section ("new_section")));
void Function_Attributes_section_0 (void)
{
static int aStatic =0;
aStatic++;
}
,section #pragma arm section 。
#pragma arm section code="foo"
int f2()
{
return 1;
} // into the 'foo' area
__attribute__((section ("bar"))) int f3()
{
return 1;
} // into the 'bar' area
int f4()
{
return 1;
} // into the 'foo' area
#pragma arm section
GNUでもARMのコンパイラでも、サポートされています.attribute__指定されたコンパイル属性について、ここではKEIL環境での_attribute__に表示されます.
sectionキーワードは、指定する入力セグメントに変数を定義することができる、以下、具体的な例でsectionの使い方を説明する.
#define SECTION(level) __attribute__((used,__section__(".fn_cmd."level)))
#define CMD_START_EXPORT(func,func_s) 、
const struct CMD_LIST cmd_fn_##func SECTION("0.end") = {func,func_s}
CMD_START_EXPORT(start_fun,"start_fun");
このマクロ定義はやや複雑に見えますが、大丈夫です.ここでは一歩一歩分析します.
まずSECTIONというマクロ定義を見てみましょう.このマクロは変数を入力セグメントに追加することができます.たとえば
int a __attribute__((section(“list”))) = 0; この文の意味はint型の変数aをlistという入力セグメントに置くことである.
levelはこの入力セグメントの接尾辞として理解できるので、この入力セグメントの最終的な名前はlevelに依存し、ここでlevel=“0.end”であれば、このマクロを展開して得られる.
__attribute__((used,__section__(".fn_cmd.""0.end"))この時点で確認します.mapファイルはファイルの中に1つの名前があることを発見することができます.fn_cmd.0.endの入力セグメントは、levelがこの入力セグメントの後半であることがわかります.
ではSECTIONというマクロについて説明しますCMD_START_EXPORT(start_fun,"start_fun");このマクロを初歩的に展開すると、struct CMD_が定義されていることがわかります.LISTタイプの変数でありsectionを用いてその属性を修飾した.
const struct CMD_LIST cmd_fn_start_fun SECTION("0.end") = {start_fun,"start_fun"}; ここで注意したいのは、マクロ式の##記号です.この記号の役割は、2つの文字列をつなぎ合わせることです.例えば、
#define DEF_INT(a,b) int a##b = 0 DEF_INT(a,b); 最終的にこのマクロが実現するのはint型の変数を定義することを意味し、変数の名前はabと呼ばれ、このマクロを展開するのは
int ab = 0; では、わかりました.では、マクロ展開の結果、cmd_という名前を定義した理由がわかります.fn_start_funの変数です.次にマクロ展開をさらに行い,その中のSECTION展開を以下の式にする.
const struct CMD_LIST cmd_fn_start_fun __attribute__((used,__section__(".fn_cmd.""0.end"))) = {start_fun,"start_fun"}; この時に見るとstruct CMDが定義されていることに気づきますLISTタイプの変数、変数の名前はcmd_fn_start_fun、そしてこの変数は私たちが望む入力セグメントに配置された.fn_cmd.0.endが当たった.
では、sectionを使用してカスタム入力セグメントに変数を配置することは何の意味がありますか?従来のC言語プログラミングではプログラム構造がこうであることが知られている.
int main() { init_xx(); init_xx(); ... while(1) { ... }}まずいくつかの初期化プログラムを行い、ループでコードを実行します.このように開発するのはもちろんですが、初期化関数を書くたびにmain関数で呼び出され、非常に不便な点があります.しかしsectionを使用して、事前にすべての初期化関数を自分で定義した入力セグメントに追加してから、main関数でこの入力セグメントの初期化関数を順次取り出すと、main関数を変更せずにシステムの初期化を完了することができます.
ではsectionはどのようにしてこれらの初期化関数を入力セグメントに入れ、システムはこれらの初期化関数のアドレスを取得することができますか?説明する前に、以下で使用するいくつかのマクロ定義について説明します.
#define SECTION(level) __attribute__((used,__section__(".fn_cmd."level))) #define CMD_START_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("0.end") = {func,func_s} #define CMD_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("1") = {func,func_s} #define CMD_END_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("1.end")={func,func_s}これらのマクロが呼び出されるとcmd_という名前が生成されます.fn_xxの変数であり、この変数は呼び出されたマクロに基づいて対応する入力セグメントに配置される.例えばCMD_START_EXPORTというマクロ、このマクロは実は上で述べたように、このマクロを呼び出すとcmd_という名前が生成されます.fn_xxの変数は、この変数をカスタマイズした入力セグメントに配置します.fn_cmd.0.endが当たった.他の2つのマクロも実際には差が少なく、入力セグメントに違いがあります.
本題に戻り、sectionを使用して異なる関数を目的の入力セグメントに配置し、開始アドレスと終了アドレスを取得する方法について説明します.
私たちは各XXX_Init関数の後にマクロCMDが呼び出されます.EXPORT、このマクロを呼び出すとコンパイラがXXX_Initという関数は入力セグメントに組み込まれる.変数は入力セグメントのアドレスが連続しているため、section名01234で順番に並べられ、section内では関数名で並べられる.したがって、これらの初期化関数は、入力セグメントの順序に従って1つずつ呼び出され、システムの初期化を完了することができる.
具体的には、カスタムコマンドラインのアプリケーションに基づいて一部説明します.
/*コマンド関数セグメント開始位置*/int cmd_start(void) { return 0; } CMD_START_EXPORT(cmd_start,"int cmd_start(void)");/*コマンド関数セグメント終了位置*/int cmd_end(void) { return 0; } CMD_END_EXPORT(cmd_end,"int cmd_end(void)"); void test(void) { printf("hello world\r"); } CMD_EXPORT(test,"void test(void)"); void demo(void) { printf("hello world\r"); } CMD_EXPORT(demo,"void demo(void)"); startとend関数を定義し、それぞれCMD_を使用します.START_EXPORTとCMD_END_EXPORTは入力段に入れる.fn_cmd.0.end和.fn_cmd.1.endでは、上記の説明に従ってセグメントを入力.fn_cmd.0.endは入力セグメントに並ぶ.fn_cmd.1.endの前の、使用するCMD_EXPORTというマクロに対応する入力セグメント.fn_cmd.1は列に並んでいます.fn_cmd.0.end和.fn_cmd.1.endの間の、ここでコンパイルによって生成されたものを見ることができる.mapはもっとイメージがよくなります.具体的にMAPファイルの場所は以下の通りです
cmd_fn_cmd_start 0x080042f0 Data 8 serialcmd.o(.fn_cmd.0.end) cmd_fn_test 0x080042f8 Data 8 application.o(.fn_cmd.1) cmd_fn_demo 0x08004300 Data 8 application.o(.fn_cmd.1) cmd_fn_cmd_end 0x08004308 Data 8 serialcmd.o(.fn_cmd.1.1.end)は入力セグメントを見ることができる.fn_cmd.0.endのアドレスは確かに一番前ですが、fn_cmd.1.endのアドレスは確かに一番後ろに並んでいます.入力セグメントfn_cmd.1のデータは私たちが使うデータです.入力セグメントに格納された変数はstruct CMD_であるためLISTタイプは、8バイトで、以下のように定義されています.
typedef void (*fun)(); struct CMD_LIST { fun funs; const INT8 *cmd; }; したがって、各変数のサイズは8であり、すなわち、各変数のメモリ内のオフセットは8であるため、変数のアドレスが毎回8バイト増加する理由がわかる.
次の関数は,入力セグメントからデータを取得し,対応する処理を行う関数である.
const static struct CMD_LIST *CmdList; static UINT8 CmdSize = 0;/*コマンド関数初期化*/void SerialCmdInit(void){const struct CMD_LIST*cmd_ptr;CmdList=&cmd_fn_cmd_start;CmdList+;for(cmd_ptr=CmdList;cmd_ptr<&cmd_fn_cmd_end;cmd_ptr+);{/*ここで初期化に使用する場合、初期化関数を実行するには以下の方法を使用します.私のアプリケーションは初期化に使用されていないので、関数呼び出しは行われません.(*cmd_ptr->fun);*/CmdSize++;}そのうちcmd_fn_cmd_startはfn_cmd.0.endという入力セグメントのうち、各実行する関数は.fn_cmd.1この入力セグメントの、cmd_fn_cmd_endが終了の標識としている.fn_cmd.1.end入力セグメント.残りはあまり説明しないで、上から.mapファイルのアドレスは,開始アドレスと終了アドレスが得られた後,これらの関数をどのように1つずつ遍歴するかを容易に見ることができる.
最後に言う前にattribute__((used,__section__(".fn_cmd.")のusedの意味です.unused:関数または変数が使用されない可能性があることを示します.このプロパティは、コンパイラが警告情報を生成することを回避します.used:このコードをコンパイラに説明するのに役立ち、使用していない場合でもコンパイラは警告しません.
ここで言う必要がありますが、私がKEILを使ってコンパイルしたとき、usedを付けなかったため変数がコンパイラに最適化されたので、このキーワードを追加する必要がある場合があります.——————————————————