1. 程式人生 > >gcc擴充套件__attribute__((constructor))詳解和在.a庫中的使用方法

gcc擴充套件__attribute__((constructor))詳解和在.a庫中的使用方法

gcc對c語言做了很多擴充套件,使得c語言的表現力得到了很大的增強,本文主要介紹一下constructor擴充套件,這個擴充套件和C++的建構函式很像,它會在main函式之前由程式載入器自動呼叫,與之相對的是destructor,它會在main函式執行結束或者exit的時候自動呼叫,由於兩個擴充套件是一對,destructor這裡就不介紹了。ANSI C標準還引入了atexit函式,這個是在程序結束的時候自動呼叫。和destructor也比較相似。  
constructor擴充套件的用法如下     


```
void func() __attribute__((constructor));                              
```


他有如下規則
- 建構函式先於main函式而執行
- 不同建構函式如果在同一個檔案中,則先出現的函式後執行
- 對於不同檔案中的建構函式,編譯命令中後出現的.c檔案的建構函式先執行


利用constructor屬性,我們可以定義一些巨集來實現模組的自動註冊機制。也就是說我們用巨集自動構造註冊函式,然後把註冊函式賦予constructor屬性,這樣我們在新增新的模組的時候就不需要顯示的呼叫註冊函式來,只需要在模組檔案內加上一個巨集呼叫即可。例子如下:


```
#define INITIALIZER(f) \
   static void f(void) __attribute__((constructor)); \
   static void f(void)


#define FAP_REGISTER(name)                                              \
  INITIALIZER(fap_register_ ## name) {                                  \
    fileaccess_register_entry(&fa_protocol_ ## name);\
  }
```


然後我們只要在模組內呼叫`FAP_REGISTER(fs);`即可,這樣做的好處是新增新的模組更加方便,程式碼維護也更簡單,那麼問題來了,假如我們現在有這樣一套註冊機制的模組,我們把它編譯成.a庫整合到現在的應用方案中,應用沒有在任何地方呼叫我們的註冊函式,編譯出來的結果能正確自動呼叫註冊函式嗎?答案是可能會出問題,定義constructor屬性的函式會放在elf檔案的constructor區域,在連結的時候由於我們的註冊函式沒被呼叫,所以編譯器很可能做優化,不去連結我們的註冊函式,這樣elf檔案的constructor區域就沒有我們的註冊函式,自然註冊函式就不會自動的呼叫了,解決辦法就是在連結的時候強制把.a裡所有的函式都連結到可執行檔案中。我們使用-Wl,--whole-archive和 -Wl,--no-whole-archive這兩個連結命令來做這件事情。--whole-archive 可以把在其後面出現的靜態庫包含的函式和變數輸出到結果中,--no-whole-archive 則關掉這個特性。你不想把--whole-archive後邊所有.a庫的函式和變數都連結到輸出結果的時候就需要用--no-whole-archive了,這裡需要注意的是-wl,--whole-archive 和 --no-whole-archive 是ld專有的命令列引數,gcc 並不認識,要通gcc傳遞到 ld,需要在他們前面加 -Wl,例子如下:


```
gcc main.o  -lb.a -wl,--whole-archive -lmode_fs.a -wl, --no-whole-archive -lc.a -lz.a -o man 
```