如何用ld手动将目标文件链接到C标准库生成正确可执行文件?
问题:直接使用ld链接C目标文件的正确方式?
背景
测试用C代码如下:
// main.c #include <stdio.h> int main() { printf("Hello, world!"); return 0; }
使用gcc main.c可直接生成能正常输出Hello, world!的a.out,该过程包含两步:将main.c编译为目标代码,再将目标代码与标准库链接。
通过gcc -c main.c生成目标文件main.o后,尝试直接用ld链接时遇到以下问题:
- 执行
ld main.o:出现找不到_start符号的警告,以及printf未定义的错误; - 执行
ld main.o -lc:虽生成a.out,但仍有_start警告,且运行时会无限输出Hello, world!。
请问正确调用ld的方式是什么,以消除警告并生成正常的可执行文件?
解答
问题根源
- 缺少C运行时启动文件:C程序的真正入口并非
main函数,而是glibc提供的_start符号——这个符号由crt1.o、crti.o、crtn.o这类C运行时启动文件提供。它们负责初始化程序环境、调用main函数,以及在main返回后处理退出逻辑。直接链接main.o时没有这些文件,会导致_start符号缺失,程序无法正常执行。 - 未指定动态链接器:动态链接的程序需要明确指定动态链接器路径,否则无法正确加载标准库。
- 无限输出原因:未链接启动文件时,程序没有正确初始化stdio缓冲区,也没有正常的退出流程,导致
printf的输出未被刷新,程序进入异常循环。
正确的ld命令
以x86_64 Linux平台为例,正确链接命令如下:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 $(gcc -print-file-name=crt1.o) $(gcc -print-file-name=crti.o) main.o $(gcc -print-file-name=crtn.o) -lc
参数说明
-dynamic-linker /lib64/ld-linux-x86-64.so.2:指定动态链接器路径,32位平台需改为/lib/ld-linux.so.2;$(gcc -print-file-name=crt1.o)、$(gcc -print-file-name=crti.o)、$(gcc -print-file-name=crtn.o):通过gcc命令自动获取C运行时启动文件的绝对路径,避免手动查找路径的麻烦;-lc:链接标准C库(libc.so),提供printf等函数的实现。
执行上述命令后生成的a.out可正常输出Hello, world!,且无任何警告。
内容的提问来源于stack exchange,提问作者fmg
相关产品推荐
相关产品推荐

