GCC在尝试编译64位代码时在OSX 10.6上崩溃

4

我有一个全新的OSX 10.6安装程序。 现在,我想将以下简单的C程序编译为64位二进制文件:

 #include <stdio.h>

 int main() 
 {
    printf("hello world");
    return 0;
 }

我这样调用gcc:

gcc -m64 hello.c

然而,这会出现以下错误:

未定义的符号:
  "___gxx_personality_v0", referenced from:
  _main in ccUAOnse.o
  CIE in ccUAOnse.o
  ld: symbol(s) not found
  collect2: ld returned 1 exit status

这里发生了什么?为什么gcc崩溃了? 不加-m64标志进行编译则可以正常工作。
4个回答

17

两件事情:

我认为您实际上没有使用gcc -m64 hello.c。您收到的错误通常是这样做的结果:gcc -m64 hello.cc——使用C编译器来编译C ++代码。

shell% gcc -m64 hello.c
shell% ./a.out
hello world [added missing newline]
shell% cp hello.c hello.cc
shell% gcc -m64 hello.cc
Undefined symbols:
  "___gxx_personality_v0", referenced from:
      _main in ccYaNq32.o
      CIE in ccYaNq32.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

您可以通过以下方式 "使其正常工作":

shell% gcc -m64 hello.cc -lstdc++
shell% ./a.out
hello world

其次,在Mac OS X上指定生成64位代码的首选方式不是使用-m64,而是使用-arch ARCH,其中ARCHppcppc64i386x86_64之一。根据你的工具设置情况,可能会有更多(或更少)的体系结构可用(即iPhone ARM、ppc64已弃用等)。另外,在10.6上,gcc默认为-arch x86_64或默认生成64位代码。

使用这种风格,编译器可以自动创建“fat二进制文件”-可以多次使用-arch。例如,创建“通用二进制文件”:

shell% gcc -arch x86_64 -arch i386 -arch ppc hello.c
shell% file a.out
a.out: Mach-O universal binary with 3 architectures
a.out (for architecture x86_64):    Mach-O 64-bit executable x86_64
a.out (for architecture i386):  Mach-O executable i386
a.out (for architecture ppc7400):   Mach-O executable ppc

编辑:以下是对OP问题“我犯了一个错误,将我的文件称为.cc而不是.c。我仍然困惑于这会有什么影响?”的回答。

那个……这是一个有点复杂的答案。我会给出一个简要的解释,但我希望你能相信“这实际上是有充分理由的”。

可以说,“编译程序”是一个相当复杂的过程。出于历史和实际原因,当你执行 gcc -m64 hello.cc时,它在幕后实际上被分成几个离散的步骤。每个步骤通常将每个步骤的结果馈送到下一个步骤中,大致如下:

  • 运行C预处理器cpp对正在编译的源代码进行预处理。此步骤负责执行所有#include语句、各种#define宏扩展和其他“预处理”工作。
  • 对C预处理结果运行C编译器本身。此步骤的输出是一个.s文件,即将C代码编译为汇编语言的结果。
  • .s源上运行as汇编器。这将汇编语言组合成一个.o对象文件。
  • .o文件中运行ld链接器,将各个已编译的对象文件和各种静态和动态链接库链接到一个可用的可执行文件中。

注意:这是大多数编译器的“典型”流程。单个编译器实现不必遵循上述步骤。一些编译器将多个步骤合并为一个步骤以提高性能。例如,现代版本的gcc不使用单独的cpp传递。另一方面,tcc编译器在一次操作中执行所有上述步骤,不使用任何其他外部工具或中间步骤。

在上述传统的编译器工具链流程中,cc命令(或者在我们的情况下是gcc)被称为“编译器驱动程序”。它是所有上述工具和步骤的“逻辑前端”,并知道如何智能地应用所有步骤和工具(如汇编器和链接器)以创建最终可执行文件。但是,为了做到这一点,它通常需要知道它正在处理的“文件类型”。例如,你实际上不能将已汇编的.o文件传递给C编译器。因此,有几个“标准”.*用于指定“文件类型”(有关更多信息,请参见man gcc):

  • .c.h:C源代码和头文件。
  • .m:Objective-C源代码。
  • .cc.cp.cpp.cxx.c++:C++源代码。
  • .hh:C++头文件。
  • .mm.M:Objective-C++源代码。
  • .s:汇编语言源代码。
  • .o:已汇编的目标代码。
  • .aar档案或静态库。
  • .dylib:动态共享库。

当然,也可以使用各种编译器标志来覆盖“自动确定的文件类型”(请参见man gcc以了解如何操作),但通常最好遵循标准约定,这样所有内容都可以自动“协调运作”。

顺便说一下,如果您在原始示例中使用了C++“编译器驱动程序”或g++,则不会遇到此问题:

shell% g++ -m64 hello.cc
shell% ./a.out
hello world
这是因为gcc基本上说:“在驱动工具链时使用C规则”,而g++则表示:“在驱动工具链时使用C++规则”。g++知道要创建一个可工作的可执行文件,需要将-lstdc++传递给链接器阶段,而gcc显然认为这不是必要的,即使它知道在“编译源代码”阶段要使用C++编译器是因为文件扩展名是.cc

Mac OS X 10.6默认提供的其他C/C++编译器包括:gcc-4.0gcc-4.2g++-4.0g++-4.2llvm-gccllvm-g++llvm-gcc-4.0llvm-g++-4.0llvm-gcc-4.2llvm-g++-4.2clang。这些工具(通常)交换了工具链流程中的前两个步骤,并使用相同的低级别工具,如汇编程序和链接器。llvm-编译器使用gcc前端解析C代码,并将其转换为中间表示,然后使用llvm工具将该中间表示转换为代码。由于llvm工具以“低级虚拟机”作为最终输出,因此它允许使用更丰富的优化策略,其中最显著的是它可以在不同的已编译的.o文件之间执行优化。这通常称为“链接时优化”。clang是一个全新的C编译器,它也将llvm工具作为其输出目标,从而允许进行相同类型的优化。

所以,这就是为什么gcc -m64 hello.cc对您来说失败的不那么短的解释。:)

编辑:还有一件事...

“编译器驱动程序技术”的常见做法是将命令如gccg++符号链接到同一个“多合一”编译器驱动程序可执行文件。然后,在运行时,编译器驱动程序会检查用于创建进程的路径和文件名,并根据文件名是否以gccg++(或等效名称)结尾动态切换规则。这使得编译器的开发人员可以重用大部分前端代码,然后只需更改两者之间需要的少量差异。


谢谢您指出这个问题。我确实犯了一个错误,把我的文件命名为.cc而不是.c。 我仍然困惑为什么这很重要?我的意思是,为什么我不能随便给我的文件任何扩展名呢?如果代码不符合编译器期望的规则,它应该会因为适当的错误信息而失败。为什么gcc要试图读取我的思维? - Daniel
gcc可以编译多种语言。如果你的代码实际上是C++代码,那么你会希望它有这种行为(我猜... 我总是更喜欢使用g++编译C++和gcc编译C,但某个地方的人决定这将是一个不错的特性)。 - David Rodríguez - dribeas

1

我不知道为什么会这样(对我来说很好用),但是

  1. 尝试使用g++编译或链接到libstdc++___gxx_personality_v0是GNU C++用于设置析构函数的SjLj回调的符号,因此C++代码以某种方式渗入您的C代码。或者
  2. 删除-m64标志。据我所知,10.6上GCC 4.2生成的二进制文件默认为64位。您可以通过file输出来检查,并确保它读取“Mach-O 64位可执行文件x86_64”。或者
  3. http://developer.apple.com/technology/xcode.html重新安装最新的Xcode。

0

好的:

  • 添加-m64不会有任何作用,普通的gcc没有选项就是64位编译
  • 如果你真的只是从光盘上安装的话,那么你应该更新并安装一个新的xcode
  • 在10.6.2上,你的程序对我来说无论是否使用-m64都可以正常工作

0
我们在使用 CocoaPods 创建和使用包含 Objective-C++ 代码的 pod 时遇到了类似的问题,因此我认为值得一提:
您应该编辑包含 C++ 代码的 pod 的 .podspec 文件,并添加一行类似于:
s.xcconfig = {
    'OTHER_LDFLAGS' => '$(inherited) -lstdc++',
}

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