版權(quán)歸原作者所有,如有侵權(quán),請(qǐng)聯(lián)系我們

[科普中國(guó)]-弱符號(hào)

科學(xué)百科
原創(chuàng)
科學(xué)百科為用戶提供權(quán)威科普內(nèi)容,打造知識(shí)科普陣地
收藏

弱符號(hào)表示在鏈接可執(zhí)行文件和可鏈接格式(ELF)對(duì)象文件期間特別注釋的符號(hào)。默認(rèn)情況下,沒(méi)有任何注釋,目標(biāo)文件中的符號(hào)很強(qiáng)。 在鏈接期間,強(qiáng)符號(hào)可以覆蓋同名的弱符號(hào)。相反,共享名稱的兩個(gè)強(qiáng)符號(hào)在鏈接時(shí)產(chǎn)生鏈接錯(cuò)誤。 鏈接二進(jìn)制可執(zhí)行文件時(shí),弱聲明的符號(hào)不需要定義。相比之下,(默認(rèn)情況下)沒(méi)有定義的聲明的強(qiáng)符號(hào)會(huì)觸發(fā)未定義的符號(hào)鏈接錯(cuò)誤。

C或C ++語(yǔ)言標(biāo)準(zhǔn)沒(méi)有提到弱符號(hào);因此,將它們插入代碼并不是非常便攜。 即使兩個(gè)平臺(tái)支持用于將符號(hào)標(biāo)記為弱的相同或類似語(yǔ)法,語(yǔ)義也可能在細(xì)微點(diǎn)上不同,例如, 動(dòng)態(tài)鏈接期間弱符號(hào)是否會(huì)失去語(yǔ)義。

語(yǔ)法GNU編譯器集合和Solaris Studio C編譯器共享相同的語(yǔ)法,用于將符號(hào)注釋為弱,即特殊的#pragma,#pragma weak,以及函數(shù)和變量屬性__attribute __((weak))。

編譯// function declaration#pragma weak power2 int power2(int x);屬性// function declaration int __attribute__((weak)) power2(int x); // or int power2(int x) __attribute__((weak)); // variable declaration;extern int __attribute__((weak)) global_var;支持平臺(tái)nm命令標(biāo)識(shí)目標(biāo)文件,庫(kù)和可執(zhí)行文件中的弱符號(hào)。 在Linux上,如果弱默認(rèn)定義可用,則弱函數(shù)符號(hào)標(biāo)記為“W”,如果不可用,則標(biāo)記為“w”。 弱定義的變量符號(hào)標(biāo)有“V”和“v”。 在Solaris上,“nm”為弱符號(hào)打印“WEAK”而不是“GLOB”。1

實(shí)例靜態(tài)實(shí)例main.c:

#include #include #include "power_slow.h" int main(int argc, char **argv){ fprintf(stderr, "power3() = %d\n", power3(atoi(argv[1]))); return 0;}power_slow.h:

#ifndef POWER2_SLOW_H#define POWER2_SLOW_H // alternative syntax// #pragma weak power2int __attribute__((weak)) power2(int x) // alternatively after symbol // __attribute__((weak)) ; int power3(int x); #endifpower_slow.c:

#include #include "power_slow.h" int power2(int x){ fprintf(stderr, "slow power2()\n"); return x*x;} int power3(int x){ return power2(x)*x;}power.c:

#include int power2(int x){ fprintf(stderr, "fast power2()\n"); return x*x;}Build commands:

cc -g -c -o main.o main.ccc -g -c -o power_slow.o power_slow.ccc -g -c -o power.o power.ccc main.o power_slow.o -o slowcc main.o power_slow.o power.o -o fast輸出

$ ./slow 3slow power2power3() = 27$ ./fast 3fast power2power3() = 27刪除weak屬性并重新執(zhí)行build命令時(shí),最后一個(gè)失敗并顯示以下錯(cuò)誤消息(在Linux上):

multiple definition of `power2'倒數(shù)第二個(gè)仍然成功,而./slow具有相同的輸出。

共享實(shí)例從前面的例子中取main.c并添加:

#ifndef NO_USER_HOOKvoid user_hook(void){ fprintf(stderr, "main: user_hook()\n");}#endif用以下內(nèi)容替換power_slow.c:

#include #include "power_slow.h" void __attribute__((weak)) user_hook(void);#ifdef ENABLE_DEFvoid user_hook(void){ fprintf(stderr, "power_slow: user_hook()\n");}#endif int power2(int x){ if (user_hook) // only needed ifndef ENABLE_DEF user_hook(); return x*x;} int power3(int x){ return power2(x)*x;}構(gòu)建命令:

cc -g -c -o main.o main.ccc -g -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o power_slow.po -Lpwd -Wl,-Rpwd -lpowerslow -o main cc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o power_slow.po -Lpwd -Wl,-Rpwd -lpowerslow -o main2 cc -g -DNO_USER_HOOK -c -o main.o main.ccc -g -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o power_slow.po -Lpwd -Wl,-Rpwd -lpowerslow -o main3 cc -g -DNO_USER_HOOK -c -o main.o main.ccc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o power_slow.po -Lpwd -Wl,-Rpwd -lpowerslow -o main4 輸出

$ ./main 3main: user_hook()power3() = 27$ ./main2 3main: user_hook()power3() = 27$ ./main3 3power3() = 27$ ./main4 3power_slow: user_hook()power3() = 27刪除weak屬性并重新執(zhí)行構(gòu)建命令不會(huì)產(chǎn)生構(gòu)建錯(cuò)誤,并導(dǎo)致main和main2的輸出(在Linux上)相同。 main3的構(gòu)建命令導(dǎo)致以下警告和錯(cuò)誤消息(在Linux上):

warning: the address of ‘user_hook’ will always evaluate as ‘true’libpowerslow.so: undefined reference to `user_hook'警告由編譯器發(fā)出,因?yàn)樗梢造o態(tài)地確定if(user_hook)表達(dá)式user_hook總是評(píng)估為true,因?yàn)樗珽LF跳轉(zhuǎn)表?xiàng)l目。 錯(cuò)誤消息由鏈接器發(fā)出。 main4的構(gòu)建包含相同的警告但沒(méi)有鏈接錯(cuò)誤。

應(yīng)用弱符號(hào)可以用作提供功能的默認(rèn)實(shí)現(xiàn)的機(jī)制,其可以在鏈接時(shí)由更專業(yè)(例如,優(yōu)化的)替換。 然后將默認(rèn)實(shí)現(xiàn)聲明為弱,并且在某些目標(biāo)上,具有強(qiáng)聲明符號(hào)的目標(biāo)文件將添加到鏈接器命令行。

如果庫(kù)將符號(hào)定義為弱符號(hào),則鏈接該庫(kù)的程序可以自由地提供強(qiáng)大的符號(hào),例如,用于自定義目的。

弱符號(hào)的另一個(gè)用例是維護(hù)二進(jìn)制向后兼容性。

相關(guān)方法C預(yù)處理器(CPP)條件結(jié)構(gòu)也可用于在符號(hào)的不同版本之間切換。 與弱符號(hào)的區(qū)別在于弱符號(hào)由鏈接器解釋。 CPP在C編譯器之前編譯每個(gè)轉(zhuǎn)換單元期間運(yùn)行。

構(gòu)建過(guò)程(例如make)可以以條件方式實(shí)現(xiàn),使得僅創(chuàng)建符號(hào)的不同版本或者根據(jù)目標(biāo)使用和鏈接不同(專用)庫(kù)。

本詞條內(nèi)容貢獻(xiàn)者為:

王慧維 - 副研究員 - 西南大學(xué)