通過一個簡單的例子介紹一下gcc的__attribute__ ((constructor))屬性的作用。gcc允許為函數設置__attribute__ ((constructor))和__attribute__ ((destructor))兩種屬性,顧名思義,就是將被修飾的函數作為構造函數或析構函數。程序員可以通過類似下面的方式為函數設置這些屬性:
void funcBeforeMain() __attribute__ ((constructor));
void funcAfterMain() __attribute__ ((destructor));
1 #include <stdio.h> 2 3 void __attribute__((constructor)) funcBeforeMain() 4 { 5 printf("%s...\n", __FUNCTION__); 6 } 7 8 void __attribute__((destructor)) funcAfterMain() 9 { 10 printf("%s...\n", __FUNCTION__); 11 } 12 13 int main() 14 { 15 printf("main...\n"); 16 return 0; 17 }
運行結果:
funcBeforeMain...
main...
funcAfterMain...
為什么有這么神奇的函數呢?它是怎么實現的呢?
通過翻看GNU的link文檔,我找到了答案:
在GNU link中,也就是你的系統中的XX.S文件,找到了詳細的答案,
當使用a.out文件來鏈接程序時,鏈接器使用一個與眾不同的關鍵字construct 來支持C++里面的全局constructors 和 destructors,當鏈接對象不支持任意剖分時,鏈接器可以通過名字來自動識別構造器和解析器。
link文件中的構造器格式如下:
1 __CTOR_LIST__ = .; 2 LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) 3 *(.ctors) 4 LONG(0) 5 __CTOR_END__ = .; 6 __DTOR_LIST__ = .; 7 LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) 8 *(.dtors) 9 LONG(0) 10 __DTOR_END__ = .;
符號__CTOR_LIST__標志者全局構造器的開始,符號__DTOR_LIST標志着構造器的結束。列表中的第一個關鍵字代表條目的個數,后面緊跟者是構造器和解析器的地址。最后是一個零字符。編譯器必須排隊去執行這些代碼。
GNU編譯器通常通過一個子程序__main函數前面調用constructor,__main在被調用時會自動的插入到main函數的起始代碼中。同樣的是,GNU通過運行atexit來調用destructors,或者是通過函數exit來直接調用。
參考文檔:
