簡(jiǎn)介
__attribute__ 是gcc編譯器支持的一個(gè)編譯特性(arm編譯器也支持此特性,比如我們常用的keil就是用的ARMGCC編譯器),也就是通過(guò)給函數(shù)或者變量聲明屬性值,以便讓編譯器能夠?qū)σ幾g的程序進(jìn)行優(yōu)化處理。
而對(duì)于 section 這個(gè)關(guān)鍵字,我們可以通過(guò)它將指定的變量定義到指定的輸入段中。
section 屬性指定變量必須放置在特定數(shù)據(jù)部分中,通常,ARM 編譯器將其生成的對(duì)象放在 .data 和 .bss 等部分中。但是,您可能需要其他數(shù)據(jù)部分,或者您可能希望變量出現(xiàn)在特殊部分中,例如,映射到特殊硬件。
如果使用 section 屬性,則只讀變量將放置在 RO 數(shù)據(jù)部分中,讀寫變量將放置在 RW 數(shù)據(jù)部分中,除非您使用 zero_init 屬性。在這種情況下,變量放置在 ZI 部分中。
/*inROsection*/ constintdescriptor[3]__attribute__((section("descr")))={1,2,3}; /*inRWsection*/ longlongrw_initialized[10]__attribute__((section("INITIALIZED_RW")))={5}; /*inRWsection*/ longlongrw[10]__attribute__((section("RW"))); /*inZIsection*/ longlongaltstack[10]__attribute__((section("STACK"),zero_init));
用法詳解
先來(lái)看一段代碼(摘自CSDN,如有侵權(quán),聯(lián)系刪除):
#include#defineSEC__attribute__((__section__("ss"),aligned(sizeof(void*)))) voidfunc_1(inta,intb) { printf("%s%d%d ",__func__,__LINE__,a+b); } voidfunc_2(inta,intb) { printf("%s%d%d ",__func__,__LINE__,a*b); } //編譯器會(huì)自動(dòng)提供__start_ss,__stop_ss標(biāo)志段ss的起止地址 externsize_t__start_ss; externsize_t__stop_ss; typedefstruct { void(*p)(int,int); }node_t; //結(jié)構(gòu)體變量a位于自定義段ss SECnode_ta={ .p=func_1, }; SECnode_tb={ .p=func_2, }; intmain(intargc,char**argv) { inta=3,b=4; node_t*p; //遍歷段ss,執(zhí)行node_t結(jié)構(gòu)中的p指向的函數(shù) for(p=(node_t*)&__start_ss;p(node_t?*)&__stop_ss;?p++) ????{ ????????p->p(a,b); a+=1; b+=2; } }
來(lái)看一下運(yùn)行的結(jié)果:
1、section關(guān)鍵字可以將變量定義到指定的輸入段中,
#defineSECTION(level)__attribute__((used,__section__(".fn_cmd."level))) #defineCMD_START_EXPORT(func,func_s)conststructCMD_LISTcmd_fn_##funcSECTION("0.end")={func,func_s} CMD_START_EXPORT(start_fun,"start_fun"); conststructCMD_LISTcmd_fn_##funcSECTION("0.end")={func,func_s} conststructCMD_LISTcmd_fn_funcSECTION("0.end")={start_fun,start_fun} conststructCMD_LISTcmd_fn_func__attribute__((used,__section__(".fn_cmd.""0.end")))={start_fun,start_fun}
這個(gè)時(shí)候再來(lái)看會(huì)發(fā)現(xiàn)其實(shí)就是定義了一個(gè)struct CMD_LIST 類型的變量,變量的名字是cmd_fn_start_fun,并且這個(gè)變量被放到了我們所希望的一個(gè)輸入段.fn_cmd.0.end中了
typedefvoid(*fun)(); structCMD_LIST { funfuns; constINT8*cmd; };
2、使用section將變量放到我們自定義的輸入段中有什么意義呢?
#defineSECTION(level)__attribute__((used,__section__(".fn_cmd."level))) #defineCMD_START_EXPORT(func,func_s)conststructCMD_LISTcmd_fn_##funcSECTION("0.end")={func,func_s} #defineCMD_EXPORT(func,func_s)conststructCMD_LISTcmd_fn_##funcSECTION("1")={func,func_s} #defineCMD_END_EXPORT(func,func_s)conststructCMD_LISTcmd_fn_##funcSECTION("1.end")={func,func_s}
當(dāng)這幾個(gè)宏被調(diào)用時(shí)將會(huì)產(chǎn)生名為cmd_fn_xx的變量,并且這個(gè)變量根據(jù)被調(diào)用的宏來(lái)把這個(gè)變量放到相應(yīng)的輸入段。例如CMD_START_EXPORT這個(gè)宏,這個(gè)宏其實(shí)上面已經(jīng)講過(guò)了,調(diào)用這個(gè)宏的時(shí)候會(huì)產(chǎn)生一個(gè)名為cmd_fn_xx的變量,并且把這個(gè)變量放到了我們自定義的輸入段.fn_cmd.0.end中了。
其他兩個(gè)宏的其實(shí)也是差不多的,不同之處就是輸入段有些區(qū)別。言歸正傳,現(xiàn)在繼續(xù)來(lái)講如何使用section將不同的函數(shù)放到我們想要的輸入段中,并且獲得他們的起始地址和結(jié)束地址。
我們可以在每個(gè)XXX_Init函數(shù)后面都調(diào)用宏CMD_EXPORT,在調(diào)用這個(gè)宏時(shí)編譯器會(huì)將XXX_init這個(gè)函數(shù)加入到輸入段中,由于變量在輸入段中的地址是連續(xù)的,并且順序先按 section 名 01234排一遍,section 內(nèi)再按函數(shù)名稱排。
所以可以按照輸入段中順序來(lái)逐個(gè)調(diào)用這些初始化函數(shù)來(lái)完成系統(tǒng)的初始化。具體實(shí)現(xiàn)我會(huì)根據(jù)我的一個(gè)自定義命令行的應(yīng)用來(lái)進(jìn)行部分的說(shuō)明。
/*命令函數(shù)段起始位置*/ intcmd_start(void) { return0; } CMD_START_EXPORT(cmd_start,"intcmd_start(void)"); /*命令函數(shù)段結(jié)束位置*/ intcmd_end(void) { return0; } CMD_END_EXPORT(cmd_end,"intcmd_end(void)"); voidtest(void) { printf("helloworld "); } CMD_EXPORT(test,"voidtest(void)"); voiddemo(void) { printf("helloworld "); } CMD_EXPORT(demo,"voiddemo(void)");
先定義start和end函數(shù)并且分別使用CMD_START_EXPORT和CMD_END_EXPORT來(lái)將其放到輸入段.fn_cmd.0.end和.fn_cmd.1.end中,按照上面的說(shuō)明輸入段.fn_cmd.0.end是排在輸入段.fn_cmd.1.end前面的,而使用的CMD_EXPORT這個(gè)宏對(duì)應(yīng)的輸入段.fn_cmd.1是排在.fn_cmd.0.end和.fn_cmd.1.end之間的,這里可以看一下編譯產(chǎn)生的.map會(huì)更加的形象一些。具體在MAP文件的位置如下所示
cmd_fn_cmd_start0x080042f0Data8serialcmd.o(.fn_cmd.0.end) cmd_fn_test0x080042f8Data8application.o(.fn_cmd.1) cmd_fn_demo0x08004300Data8application.o(.fn_cmd.1) cmd_fn_cmd_end0x08004308Data8serialcmd.o(.fn_cmd.1.end)
typedefvoid(*fun)(); structCMD_LIST { funfuns; constINT8*cmd; }; conststaticstructCMD_LIST*CmdList; staticUINT8CmdSize=0; /*命令函數(shù)初始化*/ voidSerialCmdInit(void) { conststructCMD_LIST*cmd_ptr; CmdList=&cmd_fn_cmd_start; CmdList++; for(cmd_ptr=CmdList;cmd_ptr&cmd_fn_cmd_end;cmd_ptr++) ?{ ????????/*這里如果用于初始化的話可以使用下面這種方式來(lái)執(zhí)行初始化函數(shù),因?yàn)槲业膽?yīng)用并不是用于初始???????????? ?????????化,所以就沒(méi)有進(jìn)行函數(shù)調(diào)用。 ?????????(*cmd_ptr->fun)(); */ CmdSize++; } }
其中cmd_fn_cmd_start是在.fn_cmd.0.end這個(gè)輸入段中的,而各個(gè)要執(zhí)行的函數(shù)是在.fn_cmd.1這個(gè)輸入段中的,cmd_fn_cmd_end作為結(jié)束的標(biāo)志在.fn_cmd.1.end輸入段中。
其余的就不做過(guò)多講解了,從上面的.map文件中的地址很容易就可以看出在得到了起始地址和結(jié)束地址之后怎樣一個(gè)個(gè)遍歷這些函數(shù)。
然而上述功能只能對(duì) GCC 平臺(tái)有效, 如果是 ARMCC 或是其他平臺(tái), 因?yàn)榫幾g器不同, 方法可能不一樣, 為了跨平臺(tái), 就不得不添加平臺(tái)檢測(cè)的宏, 比如將下面的代碼替換獲取 myfun_section 所在的內(nèi)存區(qū)間部分即可支持 ARMCC 平臺(tái)。
#ifdef__ARMCC_VERSION/*ARMCCompiler*/ externtest_command_tmyfun_section$$Base; externtest_command_tmyfun_section$$Limit; test_command_t*myfun_section_begin=&(myfun_section$$Base); test_command_t*myfun_section_end=&(myfun_section$$Limit); #elifdefined(__GNUC__) externtest_command_t__start_myfun_section; externtest_command_t__stop_myfun_section; test_command_t*myfun_section_begin=&__start_myfun_section; test_command_t*myfun_section_end=&__stop_myfun_section; #else #error"Theplatformisnotsupported" #endif
結(jié)語(yǔ)
以上就是關(guān)于attribute(section)在GCC和ARMGCC中的使用。
審核編輯:劉清
-
ARM
+關(guān)注
關(guān)注
134文章
9054瀏覽量
366827 -
GNU
+關(guān)注
關(guān)注
0文章
143瀏覽量
17479 -
CMD命令
+關(guān)注
關(guān)注
0文章
28瀏覽量
8300 -
gcc編譯器
+關(guān)注
關(guān)注
0文章
78瀏覽量
3363
原文標(biāo)題:鮮為人知的__attribute__((__section__(section_name))),你知道怎么用嗎?
文章出處:【微信號(hào):小飛哥玩嵌入式,微信公眾號(hào):小飛哥玩嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論