弱符號表示在連結執行檔和可連結格式(ELF)對象檔案期間特別注釋的符號。默認情況下,沒有任何注釋,目標檔案中的符號很強。 在連結期間,強符號可以覆蓋同名的弱符號。相反,共享名稱的兩個強符號在連結時產生連結錯誤。 連結二進制執行檔時,弱聲明的符號不需要定義。相比之下,(默認情況下)沒有定義的聲明的強符號會觸發未定義的符號連結錯誤。
C或C ++語言標準沒有提到弱符號;因此,將它們插入代碼並不是非常便攜。 即使兩個平台支持用於將符號標記為弱的相同或類似語法,語義也可能在細微點上不同,例如, 動態連結期間弱符號是否會失去語義。
基本介紹
- 中文名:弱符號
- 外文名:Weak symbol
語法,編譯,屬性,支持平台,實例,靜態實例,共享實例,套用,相關方法,
語法
GNU編譯器集合和Solaris Studio C編譯器共享相同的語法,用於將符號注釋為弱,即特殊的#pragma,#pragma weak,以及函式和變數屬性__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;
支持平台
nm命令標識目標檔案,庫和執行檔中的弱符號。 在Linux上,如果弱默認定義可用,則弱函式符號標記為“W”,如果不可用,則標記為“w”。 弱定義的變數符號標有“V”和“v”。 在Solaris上,“nm”為弱符號列印“WEAK”而不是“GLOB”。
實例
靜態實例
main.c:
#include <stdio.h>#include <stdlib.h> #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); #endif
power_slow.c:
#include <stdio.h>#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 <stdio.h>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屬性並重新執行build命令時,最後一個失敗並顯示以下錯誤訊息(在Linux上):
multiple definition of `power2'
倒數第二個仍然成功,而./slow具有相同的輸出。
共享實例
從前面的例子中取main.c並添加:
#ifndef NO_USER_HOOKvoid user_hook(void){ fprintf(stderr, "main: user_hook()\n");}#endif
用以下內容替換power_slow.c:
#include <stdio.h>#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;}
構建命令:
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 -L`pwd` -Wl,-R`pwd` -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 -L`pwd` -Wl,-R`pwd` -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 -L`pwd` -Wl,-R`pwd` -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 -L`pwd` -Wl,-R`pwd` -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屬性並重新執行構建命令不會產生構建錯誤,並導致main和main2的輸出(在Linux上)相同。 main3的構建命令導致以下警告和錯誤訊息(在Linux上):
warning: the address of ‘user_hook’ will always evaluate as ‘true’libpowerslow.so: undefined reference to `user_hook'
警告由編譯器發出,因為它可以靜態地確定if(user_hook)表達式user_hook總是評估為true,因為它包含ELF跳轉表條目。 錯誤訊息由連結器發出。 main4的構建包含相同的警告但沒有連結錯誤。
套用
弱符號可以用作提供功能的默認實現的機制,其可以在連結時由更專業(例如,最佳化的)替換。 然後將默認實現聲明為弱,並且在某些目標上,具有強聲明符號的目標檔案將添加到連結器命令行。
如果庫將符號定義為弱符號,則連結該庫的程式可以自由地提供強大的符號,例如,用於自定義目的。
弱符號的另一個用例是維護二進制向後兼容性。
相關方法
C預處理器(CPP)條件結構也可用於在符號的不同版本之間切換。 與弱符號的區別在於弱符號由連結器解釋。 CPP在C編譯器之前編譯每個轉換單元期間運行。
構建過程(例如make)可以以條件方式實現,使得僅創建符號的不同版本或者根據目標使用和連結不同(專用)庫。