使用 gcc 编译时,函数和变量会以“_”开头吗?
Will the functions and variables precede with an "_" when compiled using gcc?
我正在使用 GCC 在 Linux 环境中学习 OS 开发。我在 Bran's Kernel Development 中了解到,C 中的所有函数和变量名在编译时在其相应的汇编源文件中都以“_”(下划线)开头。
但是当我浏览一个已编译的 C 程序的汇编源代码时,我什至找不到“_main”函数。
我执行了以下操作。
cpp sample.c sample.i
gcc -S sample.I
早期确实如此。给定的 C 函数 foo
在汇编程序中将显示为 _foo
。这样做是为了避免与手动生成的 .s
文件发生冲突。
它也将被限制为总共 8 个字符 [链接器限制]。
几十年来都不是这样。现在,符号不再以 _
为前缀并且可以超过 8 个字符。
更新:
So, Nowadays GCC does not produce a _ in front of functions and variables?
在大多数情况下,没有。 IMO,您引用的参考文献在这一点上似乎有点过时了。
大多数 POSIX 系统(例如 linux、*BSD)使用 gcc
[或 clang
] 并且它们省略了 _
。
当我第一次开始使用 C 编程时 [大约 1981 年],_
仍在使用。这是在 AT&T Unix v7、System III 和 System V 上。
IIRC,它在 1990 年代初期对于较新的系统(如 linux)已经消失。就我个人而言,从那时起我就没有遇到过 _
前缀,但我 [主要] 使用 linux [有时使用 cygwin]。
一些 AT&T Unix 衍生系统可能为了向后兼容而保留了它,但最终,大多数人都在 "foo is foo" 上进行了标准化。我无法访问 OSX,所以我不能排除 Johnathan 对此的评论。
_
自 Unix 早期(大约 1970 年)就已经存在。这是在我之前,但是,IIRC,Unix 最初是用汇编语言编写的。它被转换为 C。_
用于划分用 C 编写的函数,或者可以从 C 函数调用的 asm 函数。
那些没有前缀的是 "asm only" [因为他们可能使用了非标准的调用约定]。在过去,一切都很宝贵:RAM、CPU 周期等
因此,asm 函数 could/would 使用 "tricks" 来节省资源。几个 asm 函数可以作为一个组工作,因为它们彼此了解。
如果可以从 C 调用给定的 asm 函数,_
前缀符号是 C 兼容的 "wrapper" [在 [=82= 中做了额外的 save/restore ]].
So, I can just call the main function of a C program as "call main" instead of "call _main"?
这是一个相当安全的赌注。
如果您从 C 调用给定函数,它会自动执行正确的操作(即是否添加前缀)。
只有在尝试从手动生成的汇编程序调用 C 函数时,问题 才可能 出现。
因此,对于 asm,我只做简单的事情 call main
。它适用于大多数 [如果不是全部] 系统。
如果您想 "bullet proof" 您的代码,您可以 可以 运行 通过 C 预处理器(通过 .S
文件)您的 asm并做(例如):
#ifdef C_USES_UNDERSCORE
#define CF(_x) _##_x
#else
#define CF(_x) _x
#endif
call CF(main)
但是,我认为这太过分了。
它还说明了 _
前缀的整个问题。在现代系统 [具有大量内存和 CPU 个周期] 上,为什么汇编程序函数必须知道它正在调用的 ABI 兼容函数是从 C 还是手写汇编程序生成的?
正如 Craig 所详述的那样,像 COFF 和 ELF 这样的现代 formats/ABIs 不再遵循这种约定。
在某些使用不同 ABI 的目标上,它仍在使用中。
例如 NeXT/OS X 的 Mach-O 或 16 位和 32 位 Windows。 64 位 Windows 不再使用下划线(尽管 GCC 继续这样做了一段时间,特别是直到 4.5.1)。
此外,下划线可能会作为更大前缀的一部分出现。例如 __declspec(dllimport)
符号中的 __imp_
或 Itanium ABI 中的 _Z
。
如果您出于某种原因需要影响 mangling,GCC 提供了一个 -f[no]leading-underscore
标志。这将破坏 ABI 兼容性。
部分链接:
- 调用约定
针对不同的 C++ 编译器和操作系统 作者:Agner Fog
- Why do C compilers prepend underscores to external names?
- The history of calling conventions 作者:陈峰
- x86 Calling Conventions 维基百科
我正在使用 GCC 在 Linux 环境中学习 OS 开发。我在 Bran's Kernel Development 中了解到,C 中的所有函数和变量名在编译时在其相应的汇编源文件中都以“_”(下划线)开头。 但是当我浏览一个已编译的 C 程序的汇编源代码时,我什至找不到“_main”函数。 我执行了以下操作。
cpp sample.c sample.i
gcc -S sample.I
早期确实如此。给定的 C 函数 foo
在汇编程序中将显示为 _foo
。这样做是为了避免与手动生成的 .s
文件发生冲突。
它也将被限制为总共 8 个字符 [链接器限制]。
几十年来都不是这样。现在,符号不再以 _
为前缀并且可以超过 8 个字符。
更新:
So, Nowadays GCC does not produce a _ in front of functions and variables?
在大多数情况下,没有。 IMO,您引用的参考文献在这一点上似乎有点过时了。
大多数 POSIX 系统(例如 linux、*BSD)使用 gcc
[或 clang
] 并且它们省略了 _
。
当我第一次开始使用 C 编程时 [大约 1981 年],_
仍在使用。这是在 AT&T Unix v7、System III 和 System V 上。
IIRC,它在 1990 年代初期对于较新的系统(如 linux)已经消失。就我个人而言,从那时起我就没有遇到过 _
前缀,但我 [主要] 使用 linux [有时使用 cygwin]。
一些 AT&T Unix 衍生系统可能为了向后兼容而保留了它,但最终,大多数人都在 "foo is foo" 上进行了标准化。我无法访问 OSX,所以我不能排除 Johnathan 对此的评论。
_
自 Unix 早期(大约 1970 年)就已经存在。这是在我之前,但是,IIRC,Unix 最初是用汇编语言编写的。它被转换为 C。_
用于划分用 C 编写的函数,或者可以从 C 函数调用的 asm 函数。
那些没有前缀的是 "asm only" [因为他们可能使用了非标准的调用约定]。在过去,一切都很宝贵:RAM、CPU 周期等
因此,asm 函数 could/would 使用 "tricks" 来节省资源。几个 asm 函数可以作为一个组工作,因为它们彼此了解。
如果可以从 C 调用给定的 asm 函数,_
前缀符号是 C 兼容的 "wrapper" [在 [=82= 中做了额外的 save/restore ]].
So, I can just call the main function of a C program as "call main" instead of "call _main"?
这是一个相当安全的赌注。
如果您从 C 调用给定函数,它会自动执行正确的操作(即是否添加前缀)。
只有在尝试从手动生成的汇编程序调用 C 函数时,问题 才可能 出现。
因此,对于 asm,我只做简单的事情 call main
。它适用于大多数 [如果不是全部] 系统。
如果您想 "bullet proof" 您的代码,您可以 可以 运行 通过 C 预处理器(通过 .S
文件)您的 asm并做(例如):
#ifdef C_USES_UNDERSCORE
#define CF(_x) _##_x
#else
#define CF(_x) _x
#endif
call CF(main)
但是,我认为这太过分了。
它还说明了 _
前缀的整个问题。在现代系统 [具有大量内存和 CPU 个周期] 上,为什么汇编程序函数必须知道它正在调用的 ABI 兼容函数是从 C 还是手写汇编程序生成的?
正如 Craig 所详述的那样,像 COFF 和 ELF 这样的现代 formats/ABIs 不再遵循这种约定。
在某些使用不同 ABI 的目标上,它仍在使用中。 例如 NeXT/OS X 的 Mach-O 或 16 位和 32 位 Windows。 64 位 Windows 不再使用下划线(尽管 GCC 继续这样做了一段时间,特别是直到 4.5.1)。
此外,下划线可能会作为更大前缀的一部分出现。例如 __declspec(dllimport)
符号中的 __imp_
或 Itanium ABI 中的 _Z
。
如果您出于某种原因需要影响 mangling,GCC 提供了一个 -f[no]leading-underscore
标志。这将破坏 ABI 兼容性。
部分链接:
- 调用约定 针对不同的 C++ 编译器和操作系统 作者:Agner Fog
- Why do C compilers prepend underscores to external names?
- The history of calling conventions 作者:陈峰
- x86 Calling Conventions 维基百科