逐步进行C编译导致segfault错误

4

我正在尝试理解C语言编译

给定以下简单的C代码在main.c文件中:

int main() {
    int a;
    a = 42;
    return 0;
}

我进行了以下操作:

cpp main.c main.i
/usr/lib/gcc/x86_64-linux-gnu/9/cc1 main.i -o main.s
as -o main.o main.s
ld -o main.exe main.o

执行main.exe时,我遇到了分段错误。

在这个例子中,如何获得良好的内存寻址?


请使用您的gcc平台。 - 0___________
1
如果将您的 main() 函数重命名为 start(),会发生什么? - Jabberwocky
@Jabberwocky 替换 C 启动代码并不是一件简单的事情。使用函数 _start() 可以消除链接器警告,但程序在从 _start() 返回后会出现段错误。 - Bodo
@Bodo,这正是我所怀疑的会发生的事情。 - Jabberwocky
@johnnnn - 当main()返回时,会发生“分段错误”,因为堆栈上没有返回地址。通常,main()是从C运行时代码中调用的。通过gcc -v -o main.exe main.o命令可以了解链接步骤中所做的工作。 - Armali
1个回答

7

当我在一个x86_64 Ubuntu 19.10系统上尝试您问题中的命令序列时,ld会发出警告:

ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000

这是表示出现了问题的迹象。
这个错误意味着链接器没有找到符号"_start",而是使用了默认地址。运行程序时,它会尝试在此地址执行代码,显然无效。
从C代码编译出来的可执行程序不仅包含您的代码。编译器指示链接器添加C运行时库和启动代码。启动代码负责初始化并调用您的主函数。
例如,运行:
gcc -v -o main.exe main.o

查看程序中添加了哪些其他文件。在我的系统上,这将显示一些名称以crt开头的文件,这意味着“C运行时库”。

如果您不使用gcc链接程序,而是直接使用ld,则必须以类似编译器自动完成的方式手动添加所有必要的目标文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接