最近查看linux內核代碼時,表現了一些編譯器選項如__attribute_((weak))、__attribute__( (alias("target"))),一開始不了解,後來自己查看資料及書籍算是對gcc的這個編譯屬性有了一定的認識。
一、先了解weak屬性。
__attribute__((weak))表示為弱符號屬性,所謂的弱符號是針對於強符號來說的,我們定義的全局已初始化變量及全局函數等都是屬於強符號,在鏈接時如果有多個強符號就會報錯誤;而弱符號主要指未初始化的全局變量或通過__attribute__((weak))來顯式申明的變量或函數。
/* file:weak_test.c */ void weak_func_test(void) __attribute__((weak)); /* 顯式申明為weak,屬於弱符號(函數) */ int weak_var_test; /* 未初始化的全局變量,屬於弱符號 */ #ifdef WEAK_SYM void weak_func_test(void) {
printf("%s:%s.c in\n", __FILE__, __func__); } #endif int main() { printf("weak_var_test:%d\n", weak_var_test); weak_func_test(); return 0; } /* file:symbol.c */ int weak_var_test = 6666; /* 已初始化的全局變量,屬於強符號 */ /* 全局函數屬於強符號 */ void weak_func_test(void) { printf("%s:%s() in\n", __FILE__, __func__); }
1、使用編譯命令gcc weak_test.c symbol.c DWEAK_SYM -o weak_test,執行./weak_test,打印weak_var_test為6666,函數weak_func_test()打印symblo.c:weak_func_test() in(注意是symbol.c的函數),從執行結果看symbol.c的weak_var_test及weak_func_test覆蓋了weak_test.c的符號,說明鏈接時強弱符號都存在時以強符號為准;
2、再使用編譯命令而gcc weak_test.c -DWEAK_SYM -o weak_test,執行./weak_test,打印weak_var_test為0(未初始化的全局變量編譯器默認為0),函數執行打印weak_test.c:weak_func_test() in(這時是weak_test.c的函數),說明連接時如果只有弱符號時以弱符號為准。
3、繼續編譯gcc weak_test.c -o
weak_test,這時可能大家會有疑問,weak_func_test函數沒有實現,那麼鏈接的時候應該會報錯吧;實際上肯定是不會的,就是因為我們
將這個函數顯式的申明為weak symbol,申明為weak
symbol的函數在.o目標文件裡面是以WEAK及UND形式存在的,符號的地址為0,具體可以用readelf -s 命令查看。
那麼這種情況下只有弱符號weak_func_test存在,最終鏈接時也以弱符號為准,只不過此函數的地址為0,所以這時我們去執行./weak_test的時候必然會有segement fault的錯誤產生,就是因為去訪問了null指針。
4、弱符號還有一個規則,就是兩個都是弱符號時,以內存占用大小較大的那個符號為准。比如未初始化的char var和long var同時存在時,鏈接器以實際sizeof(long)的大小來給var分配空間,實例就不講述了,遇見這種情況需要額外小心。
小結:weak屬性基本已講述完成,其實弱符號在實際中也有很多應用。比如說在一個庫裡面實現某個函數,申明為弱符號,在某種情況下我們可以用自己的代碼去覆蓋庫的實現從而重新去實現某個函數,達到定制化的目的。
二、接下去講述alias屬性,alias屬性比較簡單,從字面意思理解就是給符號設置一個別名,相當於取一個外號。使用方法如下:
void func(void);
void alias_func(void) __attribute__((alias("func"))); 需要注意c++的符號修飾機制!
這樣的意思就是函數func的別名或外號是alias_func,那麼就是調用alias_func()和func()的效果是一樣的,有興趣的話可以自己寫代碼驗證。這時需要主意func函數必須是要有定義的,否則會編譯報錯的。
三、最後還有一個屬性是weakref活weakref("target")
__attribute__((weakref))為弱引用,請注意引用與定義的區別。weakref就是申明某個引用為弱引用,弱引用時如果需引用符號不存在也不會鏈接出錯,而是將需要引用的符號定義為WEAK屬性及0地址(跟前面的WEAK屬性很相似吧)。
weakref的用法有點特別,必須要配合alias使用及必須是static定義。__attribute__((weak("target")))相當於__attribute__((weakref,alias("target"))),以下看個實例:
/* ** weakref_test.c */ /* 申明func_alias函數func的帶弱引用的別名 */ void func(void) { printf("func:%s in\n", __FUNC__); }
static void func_alias(void) __attribute__((weakref,alias("func"))); int main() { func_alias(); /* 相當於調用func */ return 0; }
編譯運行,會發現實際運行的就是func函數。func_alias相當於是func的一個帶有weakref屬性的另一份申明,可以這樣理解:void *func = func;void *func_alias = func("weakref")。