gcc/g++中weak弱符號及alias別名

最近查看linux內核代碼時,表現了一些編譯器選項如__attribute_((weak))、__attribute__( (alias("target"))),一開始不瞭解,後來本身查看資料及書籍算是對gcc的這個編譯屬性有了必定的認識。

1、先了解weak屬性。
__attribute__((weak))表示爲弱符號屬性,所謂的弱符號是針對於強符號來講的,咱們定義的全局已初始化變量及全局函數等都是屬於強符號,在連接時若是有多個強符號就會報錯誤;而弱符號主要指未初始化的全局變量或經過__attribute__((weak))來顯式申明的變量或函數。

html

如下代碼示例:
/* 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__); }

 

 

一、使用編譯命令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的符號,說明連接時強弱符號都存在時以強符號爲準;

二、再使用編譯命令而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的函數),說明鏈接時若是隻有弱符號時以弱符號爲準。

三、繼續編譯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指針。

四、弱符號還有一個規則,就是兩個都是弱符號時,之內存佔用大小較大的那個符號爲準好比未初始化的char var和long var同時存在時,連接器以實際sizeof(long)的大小來給var分配空間,實例就不講述了,碰見這種狀況須要額外當心。

小結:weak屬性基本已講述完成,其實弱符號在實際中也有不少應用。好比說在一個庫裏面實現某個函數,申明爲弱符號,在某種狀況下咱們能夠用本身的代碼去覆蓋庫的實現從而從新去實現某個函數,達到定製化的目的。



2、接下去講述alias屬性,alias屬性比較簡單,從字面意思理解就是給符號設置一個別名,至關於取一個外號。使用方法以下:
void func(void);
void alias_func(void) __attribute__((alias("func")));  須要注意c++的符號修飾機制!

這樣的意思就是函數func的別名或外號是alias_func,那麼就是調用alias_func()和func()的效果是同樣的,有興趣的話能夠本身寫代碼驗證。這時須要主意func函數必須是要有定義的,不然會編譯報錯的。
linux


3、最後還有一個屬性是weakref活weakref("target")
__attribute__((weakref))爲弱引用,請注意引用與定義的區別。weakref就是申明某個引用爲弱引用,弱引用時若是需引用符號不存在也不會連接出錯,而是將須要引用的符號定義爲WEAK屬性及0地址(跟前面的WEAK屬性很類似吧)。
weakref的用法有點特別,必需要配合alias使用及必須是static定義。__attribute__((weak("target")))至關於__attribute__((weakref,alias("target"))),如下看個實例:
c++

/*
** 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")。
注意到前面alias屬性若是func不存在時申明alias會出錯,經過weakref方法,可讓func未定義就能夠編譯經過,使用static void alias_func(void) __attribute__((weakref, alias("func")))時即便func未定義也能連接經過,只不過func或alias_func的地址爲0,能夠去掉func的實現,驗證一下 便可。


上面講述的關於weak、alias、weakref屬性都是本身的一些總結,有不少不合理之處,還望你們指出,一塊兒探討。
gcc版本信息:gcc version 4.4.7
相關文章
相關標籤/搜索