我正在使用GCC在Linux环境中学习OS开发。我在Bran的Kernel Development中了解到,编译时C中的所有函数和变量名都在其对应的汇编源文件中以" _"(下划线)开头。 但是当我浏览已编译的C程序的汇编源代码时,我甚至无法找到" _main"功能。 我执行了以下操作。
cpp sample.c sample.i
gcc -S sample.I
答案 0 :(得分:6)
早期的情况确实如此。给定的C函数foo
将在汇编程序中显示为_foo
。这样做是为了避免与手工生成的.s
文件发生冲突。
它也将被限制为总共8个字符[链接器限制]。
几十年来,情况并非如此。现在,符号不再以_
为前缀,并且可能长于8个字符。
<强>更新强>
那么,现在GCC不会在函数和变量前面生成_?
在大多数情况下,没有。 IMO,你引用的参考,在这一点上,确实有点过时了。
大多数POSIX系统(例如linux,* BSD)使用gcc
[或clang
],他们不使用_
。
当我第一次开始用C编程[大约1981年]时,_
仍然被使用。这是在AT&amp; T Unix v7,System III和System V上。
_
前缀,但我[大多]使用了linux [有时候是cygwin]。
一些AT&amp; T Unix衍生系统可能会保留它以实现向后兼容,但最终,大多数人都标准化了“foo is foo”。我无法访问OSX,所以我不能排除Johnathan对此的评论。
自从Unix早期(大约1970年)以来,_
一直存在。这是在我的时间之前,但是,IIRC,Unix最初是用汇编语言编写的。它被转换为C. _
用于划分用C语言编写的函数,或用于从C函数调用的函数。
那些没有前缀的是“仅限asm”[因为他们可能使用了非标准的调用约定]。回到白天,一切都很珍贵:RAM,CPU周期等等。
因此,asm函数可以/将使用“技巧”来节省资源。几个asm函数可以作为一个组工作,因为他们彼此了解。
如果可以从C调用给定的asm函数,_
前缀符号是C兼容的“包装器”[在prolog / epilog中进行了额外的保存/恢复]。
那么,我可以将C程序的主要功能称为“call main”而不是“call _main”?
这是一个相当安全的赌注。
如果您从C调用给定的函数,它将自动执行正确的操作(即添加前缀或不添加前缀)。
只有在尝试从手动生成的汇编程序调用C函数时,问题可能才会出现。
所以,对于asm,我只是做了一件简单的事情并做call main
。它适用于大多数[如果不是全部]系统。
如果你想“防弹”你的代码,你可以通过C预处理器(通过.S
文件)运行你的asm并做(例如):
#ifdef C_USES_UNDERSCORE
#define CF(_x) _##_x
#else
#define CF(_x) _x
#endif
call CF(main)
但是,我觉得这太过分了。
它还说明了_
前缀的整个问题。在具有大量内存和CPU周期的现代系统中,为什么汇编程序函数必须知道它调用的ABI兼容函数是从C还是手写汇编程序生成的?
答案 1 :(得分:1)
正如Craig所详述的那样,COFF和ELF等现代格式/ ABI不再遵循惯例。
在一些使用不同ABI的目标上,它仍在使用中。 示例是NeXT / OS X的Mach-O或16位和32位Windows。 64位Windows不再使用下划线(虽然GCC一直这样做,直到4.5.1为止)。
此外,下划线可能会显示为更大前缀的一部分。例如,__imp_
符号中的__declspec(dllimport)
或安腾ABI中的_Z
。
如果由于某种原因,需要影响重整,GCC会提供-f[no]leading-underscore
标志。这将打破ABI兼容性。
一些链接: