试图理解GCC和Windows的主要功能

试图理解GCC和Windows的主要功能,c,windows,gcc,export,main,C,Windows,Gcc,Export,Main,他们说main()与任何其他函数一样,是一个函数,但“标记”为二进制文件中的入口点,操作系统可能会找到这个入口点(不知道如何启动程序)。所以,我想了解更多关于这个函数的信息。我做了什么?我创建了一个简单的.C文件,其中包含以下代码: int main(int argc, char **argv) { return (0); } 我保存了该文件,安装了GCC编译器(在Windows、MingW环境中),并创建了如下批处理文件: gcc -c test.c -nostartfiles -

他们说main()与任何其他函数一样,是一个函数,但“标记”为二进制文件中的入口点,操作系统可能会找到这个入口点(不知道如何启动程序)。所以,我想了解更多关于这个函数的信息。我做了什么?我创建了一个简单的.C文件,其中包含以下代码:

int main(int argc, char **argv) {
     return (0);
}
我保存了该文件,安装了GCC编译器(在Windows、MingW环境中),并创建了如下批处理文件:

gcc -c test.c -nostartfiles -nodefaultlibs -nostdlib -nostdinc -o test.o
gcc -o test.exe -nostartfiles -nodefaultlibs -nostdlib -nostdinc -s -O2 test.o
@%comspec%
我这样做是为了得到一个非常简单的编译器和链接器,没有库,没有头,只有编译器。因此,编译进行得很顺利,但链接因以下错误而停止:

test.c:(.text+0xa): undefined reference to '___main'
collect2.exe: error: Id returned 1 exit status
我原以为主函数是由链接器导出的,但我相信您不需要任何关于它的附加信息库。但看起来确实如此。在我的例子中,我假设它一定是标准的GCC库,所以我下载了它的源代码并打开了这个文件:libgcc2.c
现在,我不知道这是否是主函数被构造成由GCC链接的文件。事实上,我不明白GCC是如何使用主函数的。为什么链接器需要gcc标准库想知道main的情况吗?我希望这能让我的问题变得非常具体和清晰。谢谢

发生此错误是因为您使用了以下两个选项:
-nodefaultlibs-nostdlib

这些命令告诉GCC,它不应该将代码链接到
libc.a
/
c.lib
,后者包含真正调用
main()
的代码。简而言之,每个操作系统都略有不同,大多数操作系统都不关心C和
main()
。每种方法都有自己特殊的启动流程的方式,其中大多数都与C API不兼容

因此,C开发人员的解决方案是将“粘合代码”放入C标准库
libc。a
包含操作系统期望的接口,创建标准C环境(设置内存分配结构,以便
malloc()
映射操作系统的内存管理功能,设置stdio等)并最终调用
main()

对于C开发人员来说,这意味着他们可以为自己的操作系统(以及编译器二进制文件)获得一个
libc.a
,他们不需要关心安装程序是如何工作的


另一个引起混淆的原因是引用的名称。在大多数系统上,
main()
的符号名是
\u main
(即一个下划线),而
\u main
是由设置代码调用的内部函数的名称,该设置代码最终调用实
main()
发生错误的原因是您使用了以下两个选项:
-nodefaultlibs-nostlib

这些命令告诉GCC,它不应该将代码链接到
libc.a
/
c.lib
,后者包含真正调用
main()
的代码。简而言之,每个操作系统都略有不同,大多数操作系统都不关心C和
main()
。每种方法都有自己特殊的启动流程的方式,其中大多数都与C API不兼容

因此,C开发人员的解决方案是将“粘合代码”放入C标准库
libc。a
包含操作系统期望的接口,创建标准C环境(设置内存分配结构,以便
malloc()
映射操作系统的内存管理功能,设置stdio等)并最终调用
main()

对于C开发人员来说,这意味着他们可以为自己的操作系统(以及编译器二进制文件)获得一个
libc.a
,他们不需要关心安装程序是如何工作的


另一个引起混淆的原因是引用的名称。在大多数系统上,
main()
的符号名是
\u main
(即一个下划线),而
\u main
是设置代码调用的内部函数的名称,当gcc将所有对象文件放在一起(test.o)时,设置代码最终调用真正的
main()
为了形成二进制文件,它还预先添加了一个小对象(通常是crt0.o或crt1.o),该对象负责调用
main()
。当您在命令行上添加
-v
时,您可以看到gcc正在做什么:

$gcc-v-o test.exe test.o


crt0/crt1执行一些设置,然后调用main。但是链接器最终负责根据操作系统构建可执行文件。使用
-v
还可以看到目标系统的一个选项。在我的例子中,它是针对64位Linux的:
-melf\ux86\u64
。对于您的系统来说,这将类似于
-m windows
-m mingw
当gcc将所有对象文件(test.o)和库组合在一起形成二进制文件时,它还会预处理一个小对象(通常是crt0.o或crt1.o),该对象负责调用
main()
。当您在命令行上添加
-v
时,您可以看到gcc正在做什么:

$gcc-v-o test.exe test.o


crt0/crt1执行一些设置,然后调用main。但是链接器最终负责根据操作系统构建可执行文件。使用
-v
还可以看到目标系统的一个选项。在我的例子中,它是针对64位Linux的:
-melf\ux86\u64
。对于您的系统,这将类似于
-m windows
-m mingw

您可以从windows运行
nm test.o
?它将告诉您
test.o中包含的符号
是的,有一个“\uuuuuuuuuu main”和“\u main”符号(这很奇怪),您可以从windows运行
nm test.o
?它将告诉您测试中包含的符号。o是的,有一个“\uuuuuuuuuuu main”和“\uu main”符号(很奇怪)谢谢,这真的很有帮助。好的。所以crt0.o包含附加到可执行文件的平台相关信息,这样Windows或任何其他o.S.都可以根据自己的规则找到入口点,不是吗?谢谢,这是rea