【问题标题】:GCC dies trying to compile 64bit code on OSX 10.6GCC 在尝试在 OSX 10.6 上编译 64 位代码时死了
【发布时间】:2010-01-14 05:40:33
【问题描述】:

我有一个全新的现成 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 标志的编译工作正常。

【问题讨论】:

    标签: c++ c macos gcc


    【解决方案1】:

    两件事:

    我认为您实际上并没有使用gcc -m64 hello.c。您得到的错误通常是由于使用 C 编译器编译 C++ 代码,而执行 gcc -m64 hello.cc- 之类的操作。

    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
    

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

    使用这种风格,可以让编译器自动创建“胖二进制文件”——你可以多次使用-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 源代码和 C 头文件。
    • .mObjective-C 源代码。
    • .cc, .cp, .cpp, .cxx, .c++ C++ 源代码。
    • .hhC++ 头文件。
    • .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 文件执行优化.这通常称为link time optimizationclang 是一个全新的 C 编译器,它还以 llvm 工具作为其输出,允许进行相同类型的优化。

    那么,就这样吧。关于为什么gcc -m64 hello.cc 对您失败的不那么简短的解释。 :)

    编辑:还有一件事......

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

    【讨论】:

    • 感谢您指出这一点。我确实犯了一个错误并调用我的文件 .cc 而不是 .c。我仍然很困惑为什么这很重要?我的意思是,为什么我不能给我的文件提供我想要的任何扩展名?如果代码不符合规则,编译器期望它应该终止并显示适当的错误消息。为什么 gcc 会尝试读懂我的想法?
    • gcc 可以编译不止一种语言。如果您的代码实际上是 C++ 代码,那么您会想要这种行为(我猜......我一直更喜欢使用 g++ 编译 C++ 和 gcc 编译 C,但有人认为这将是一个不错的功能)
    【解决方案2】:

    我不知道为什么会发生这种情况(对我来说很好),但是

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

    【讨论】:

      【解决方案3】:

      好的:

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

      【讨论】:

        【解决方案4】:

        在使用 CocoaPods 创建和使用包含 Objective-C++ 代码的 pod 时,我们遇到了类似的问题,所以我认为这也值得一提:

        您应该编辑包含 c++ 代码的 pod 的 .podspec,并添加如下行:

        s.xcconfig = {
            'OTHER_LDFLAGS' => '$(inherited) -lstdc++',
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-02
          • 2019-02-13
          • 1970-01-01
          • 2010-11-30
          相关资源
          最近更新 更多