gcc编译链接异常:main入口失效与文件找不到问题排查
以下是极简测试代码:
#include <stdio.h> void myFunc() { puts("Greetings, Code 'Mon ..."); } int main(int argc, char *argv[]) { puts("zMain 01.\n"); myFunc(); puts("zMain 02.\n"); }
通过Makefile生成的main_test2.run在Ubuntu 20.04.6 LTS环境下出现异常:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401030
...
/main_test/yikes01$ ./main_test2.run
bash: ./main_test2.run: No such file or directory
而在MinGW/Windows 10环境下,main_test2.run虽能运行,但未将main()作为程序入口点:
c:\tmp\main_test\yikes01>main_test2.run
Greetings, Code 'Mon ...c:\tmp\main_test\yikes01>main_test.run
zMain 01.Greetings, Code 'Mon ...
zMain 02.
Makefile内容如下:
cc=gcc -v opts = link = ld ifdef OS libs = -LC:/Windows/System32 -lkernel32 -lmingw32 -lcrtdll else libs = -lc endif all: main_test2.run main_test.run Makefile # First function becomes void main(void) ... main_test2.run: main_test.c $(cc) $(opts) -c main_test.c $(link) main_test.o -o main_test2.run $(libs) rm -f main_test.o main_test.run: main_test.c gcc main_test.c -o main_test.run clean: rm -f *.o rm -f *.run
请问问题出在哪里?如何正确链接gmon_start?
注:调整参数会产生细微差异,但直接通过命令行编译的main_test.run始终正常。
核心问题:手动调用ld链接时缺失C运行时启动代码
直接用ld链接目标文件,没有引入C运行时(CRT)的启动代码,这会引发两个关键问题:
- 找不到程序入口
_start:C程序的真正执行入口不是main,而是CRT提供的_start(Linux)或_mainCRTStartup(Windows/MinGW)。这段启动代码负责初始化运行环境、调用main函数,以及在main返回后完成资源清理。你手动用ld链接时,仅关联了自定义目标文件和libc,但未包含CRT启动对象,导致ld无法找到_start符号。 - MinGW下错误执行
myFunc:当ld找不到合法入口时,会默认将目标文件中第一个出现的函数作为入口点。你的代码里myFunc定义在main之前,因此被错误当作入口执行。
另外Ubuntu下提示的" No such file or directory"并非文件不存在,而是动态链接器无法找到依赖库。手动用ld链接时未正确指定动态链接器路径(如/lib64/ld-linux-x86-64.so.2),导致系统无法加载必要的动态库。
正确的链接方式
推荐用gcc驱动链接——gcc会自动处理CRT启动代码、动态链接器路径、依赖库顺序等问题,避免手动调用ld的诸多适配问题。
方案1:修改Makefile用gcc完成链接
调整main_test2.run的规则,替换ld为gcc:
main_test2.run: main_test.c $(cc) $(opts) -c main_test.c gcc main_test.o -o main_test2.run rm -f main_test.o
此方式会自动包含所有必要的启动代码和库依赖,行为与main_test.run完全一致。
方案2:手动用ld时补充启动代码(仅作了解,不推荐)
若一定要使用ld,需明确引入CRT启动对象:
- Linux/Ubuntu:需链接
crt1.o、crti.o、crtn.o等启动文件,并指定动态链接器:ld /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o main_test.o /usr/lib/x86_64-linux-gnu/crtn.o -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main_test2.run - MinGW/Windows:需链接
crt0.o和MinGW启动库:ld C:/MinGW/lib/crt0.o main_test.o -lmingw32 -lkernel32 -lcrtdll -o main_test2.run
该方式需适配不同平台的文件路径,维护成本高,不适合日常开发。
关于gmon_start的正确链接
gmon_start是性能分析工具gprof的依赖符号,只需在编译时添加-pg参数,gcc会自动处理相关链接逻辑:
cc=gcc -v -pg opts = # 链接时仍用gcc,自动包含gprof所需代码 main_test2.run: main_test.c $(cc) $(opts) -c main_test.c gcc $(opts) main_test.o -o main_test2.run rm -f main_test.o
内容的提问来源于stack exchange,提问作者Randall Nagy

