弱符号(Weak symbol)是链接器(ld)在生成ELF(Executable and Linkable Format,缩写为ELF,可执行和可链接格式,是一种用于可执行文件、目标文件、共享库和核心转储的标准文件格式。ELF文件有两种索引:程序标头中记载了运行时所需的段,而段首地址表记载了二进制文件中段的首地址。)文件的过程中使用的一种特殊属性符号。默认情况下,如果没有特别声明,目标文件里面的符号都是强符号(Strong symbol)。在链接过程中,一个强符号会优先于一个同名的弱符号。相比之下,两个同名强符号一起链接会出现链接错误即重复定义错误。当链接一个可执行文件,弱符号可以不定义。但对于强符号,如果没有定义,连接器会产生一个“符号未定义”错误 (undefined symbol)。使用弱符号的目的是,当不确定这个符号是否被定义的情况下,链接器也可以成功链接出ELF文件,适用于某些模块还未实现的情况下,其他模块的先行调试。弱符号在C和C++的规范里面没有被提及,所以使用弱符号的代码,移植性不是非常好,这个跟编译器相关。
弱符号通常来源于未初始化的全局变量。而默认情况下,编译器将函数和初始化了全局变量作为强符号 。可以通过GCC的__attribute__((weak))来定义任何一个强符号为弱符号。不同的目标文件中不能有同名的强符号,否则不能链接在一起。如果一个符号在某个目标文件中是强符号,在其它文件中都是弱符号,那么该名称在链接时选择强符号。如果一个符号在所有的目标文件中都是弱符号,则选择占用空间(字节数)最大的一个,如果占用空间相同,则按照链接顺序选择第一个。
以下是测试代码:
main.c:
#include <stdio.h>
int __attribute__((weak)) x = 1; // weak symbol
int y = 2; // strong symbol
int z; // weak symbol, COM
extern int a; // neither weak symbol nor strong symbol
extern int __attribute__((weak)) b; // weak symbol
static int c; // neither weak symbol nor strong symbol
void __attribute__((weak)) fun1() // weak symbol
{
fprintf(stdout, "fun1 Line: %d\n", __LINE__);
}
void __attribute__((weak)) fun2(); // weak symbol
void fun3() // strong symbol
{
fprintf(stdout, "fun3 Line: %d\n", __LINE__);
}
int main()
{
fun1();
fun3();
if (fun2) {
fprintf(stdout, "run fun2\n");
fun2();
}
fprintf(stdout, "x = %d, y = %d, z = %d\n", x, y, z);
fprintf(stdout, "c = %d\n", c);
return 0;
}
build.sh:
#! /bin/bash
if [[ -e build ]]; then
echo "##### rm build dir"
rm -rf build
fi
mkdir build
cd build
echo -e "\n##### start build and link:"
gcc -c ../main.c
gcc -o main main.o
echo -e "\n##### read elf:"
readelf --syms main.o
echo -e "\n##### run:"
./main
执行结果如下:$ ./build.sh