问题
在阅读《程序员的自我修养:链接、装载与库》的4.1节时,书中实例通过将a.o,b.o直接链接到一起形成一个新的可执行文件ab来分析重定位过程,但是在实际运行命令ld a.o b.o -e main -o ab,会报错: ld: a.o: in function main': a.c:(.text+0x4f): undefined reference to
__stack_chk_fail’
/*a.c*/
extern int shared;
int main()
{
int a = 100;
swap(&a, &shared);
return 0;
}
/*b.c*/
int shared = 1;
void swap(int* a, int* b)
{
*a ^= *b ^= *a ^= *b;
}
原因
如果想直接使用ld命令手动链接而不使用GCC编译器进行链接,就需要在链接命令中显式地包含所需的库。
在上面的错误中,缺少对__stack_chk_fail函数的引用。__stack_chk_fail函数位于C运行时库中,通常是libc库。
在高版本的gcc编译器中,进行编译的时候会默认开启栈溢出保护机制。导致编译生成的目标文件a.o和b.o中会插入对__stack_chk_fail函数的引用。
解决方法1:链接libc库
因为缺少引用的__stack_chk_fail函数位于libc库中,可以通过链接libc库来引入__stack_chk_fail函数,但这种方法会额外的引入libc库的符号,不利于学习,所以更推荐方法2.
ld a.o b.o -lc -o ab
解决方法2:禁用栈溢出保护机制
要禁用栈溢出保护机制,可以使用以下方法之一:
- 在编译命令中使用-fno-stack-protector选项:
gcc -c a.c -o a.o -fno-stack-protector
gcc -c b.c -o b.o -fno-stack-protector
ld a.o b.o -o ab -e mainobj
上述命令将使用GCC编译器分别对a.c和b.c进行编译,并在编译命令中使用-fno-stack-protector选项来禁用栈溢出保护机制。然后使用ld链接器将a.o和b.o文件链接在一起。
2. 修改环境变量来设置默认编译选项:
export CFLAGS="-fno-stack-protector"
gcc -c a.c -o a.o
gcc -c b.c -o b.o
ld a.o b.o -o ab -e mainobj
上述命令首先通过设置CFLAGS环境变量来将-fno-stack-protector选项设置为默认的编译选项。然后使用GCC编译器对a.c和b.c进行编译,并使用ld链接器将a.o和b.o文件链接在一起。