1. 程式人生 > >利用gcc的__attribute__編譯屬性section子項構建初始化函式表

利用gcc的__attribute__編譯屬性section子項構建初始化函式表

gcc的__attribute__編譯屬性有很多子項,用於改變作用物件的特性。這裡討論section子項的作用。

__attribute__的section子項使用方式為:

__attribute__((section("section_name")))
其作用是將作用的函式或資料放入指定名為"section_name"的段。

看以下程式片段:

#include<unistd.h>
#include<stdint.h>
#include<stdio.h>

typedefvoid(*myown_call)(void);

extern myown_call _
myown_start; extern myown_call _myown_end; #define _init __attribute__((unused, section(".myown"))) #define func_init(func) myown_call _fn_##func _init = func staticvoidmspec1(void){ write(1, "aha!\n", 5); } staticvoidmspec2(void){ write(1, "aloha!\n", 7); } staticvoidmspec3(void
)
{ write(1, "hello!\n", 7); } func_init(mspec1); func_init(mspec2); func_init(mspec3); /* exactly like below: static myown_call mc1 __attribute__((unused, section(".myown"))) = mspec1; static myown_call mc2 __attribute__((unused, section(".myown"))) = mspec2; static myown_call mc3 __attribute__((unused, section(".myown"))) = mspec3; */
voiddo_initcalls(void){ myown_call *call_ptr = &_myown_start; do { fprintf (stderr, "call_ptr: %p\n", call_ptr); (*call_ptr)(); ++call_ptr; } while (call_ptr < &_myown_end); } intmain(void){ do_initcalls(); return 0; }
在自定義的.myown段依次填入mspec1/mspec2/mspec3的函式指標,並在do_initcalls中依次呼叫,從而達到構造並呼叫初始化函式列表的目的。

兩個extern變數:

extern myown_call _myown_start;
extern myown_call _myown_end;
來自ld的連結指令碼,可以使用:
ld --verbose
獲取內建lds指令碼,並在:
__bss_start = .;
之前新增以下內容:
_myown_start = .;
  .myown           : { *(.myown) } = 0x90000000
  _myown_end = .;
  code_segment    : { *(code_segment) }
即定義了.myown段及_myown_start/_myown_end變數(0x90000000這個數值可能需要調整)。

儲存修改後的連結器指令碼,假設程式為s.c,連結器指令碼儲存為s.lds,使用以下命令編譯:

gcc s.c -Wl,-Ts.lds
執行結果:
[[email protected] ]# ./a.out 
call_ptr: 0x8049768
aha!
call_ptr: 0x804976c
aloha!
call_ptr: 0x8049770
hello!

Have Fun!

其他參考:

http://blog.chinaunix.net/uid-27664726-id-4243961.html

http://www.cnblogs.com/lixiaofei1987/p/3198638.html