【问题标题】:Cross Compile or Compile Native for CPU Arch为 CPU Arch 交叉编译或编译本机
【发布时间】:2013-05-06 16:58:15
【问题描述】:

在编写依赖于 CPU 架构的软件时,例如在 x86 上运行的 C 代码或在 ARM cpus 上运行的 C 代码。通常有两种方法可以编译此代码,或者交叉编译到 ARM CPU 架构(例如,如果您正在 x86 系统上开发)或将代码复制到本机架构 cpu 系统并天真地编译。

我想知道本机方法与交叉编译方法相比是否有好处?我注意到 Fedora ARM 团队正在使用一个由慢速/低功耗 ARM 设备组成的构建服务器集群来“天真地”编译他们的 Fedora ARM spin……当然,由 Red Hat 支持的项目可以访问一些运行 x86 cpus 的强大构建服务器这可以在 1/2 的时间内完成工作......那么他们为什么选择呢?交叉编译我的软件是否遗漏了什么?

【问题讨论】:

  • 如果您认为 C 代码是 CPU 架构相关的,那么您肯定错过了一些东西。我想你会发现许多优秀的开发人员会尝试将他们依赖于实现的代码与他们的可移植、严格兼容的代码分开,并且通常会尝试尽可能多地编写可移植的代码。跨度>
  • @undefinedbehaviour 我认为他的问题的精神在于构建一个复杂的包是否缺少某些东西。
  • 我的猜测是他们想测试机器是否可以为自己构建工作二进制文件。买一台 linux 机器会很讨厌,只是注意到你也需要一台 PC 来构建/更新内核和二进制文件。这也是测试您构建的操作系统稳定性的一种很好的方法,完整的编译会强调多个系统。
  • 就这个问题而言,我专门谈论的是依赖于 CPU 架构的软件。如gcc -march=armv6 等。
  • @JoachimIsaksson 好点;尽管您可以使用qemu 进行测试。但随后他们缓慢的过程开始发挥作用。

标签: c arm native cross-compiling


【解决方案1】:

主要的好处是所有./configure 脚本在本机运行时都不需要调整。如果您使用影子 rootfs,那么您仍然有运行 uname 的配置来检测 CPU 类型等。例如,请参阅 this questionpkgconfig 和其他工具试图简化 cross-building,但包通常会在 x86 上得到 native-building 首先正确,然后可能 ARM 上的 em>native-building。 交叉构建可能会很痛苦,因为每个包都可能需要单独调整。

最后,如果您正在执行 profile guided optimizations 并按照 Joachim 运行 测试套件,那么在 cross 中执行此操作几乎是不可能的构建 环境。

ARM 上的编译速度明显快于人工包构建器,阅读configure,编辑configure,重新运行配置、编译、链接周期.

这也很适合continuous integration 策略。可以快速构建/部署/测试各种包,尤其是的测试可能涉及数百个依赖包。 Arm Linux 发行版 在升级和修补基础库时通常需要更改原型,而基础库可能有 数百 个至少需要重新测试的依赖包。由计算机完成的慢循环总是比快速编译然后人工干预要好。

【讨论】:

    【解决方案2】:

    从技术上讲,在 .c -> .o -> a.out (或其他)的上下文中交叉编译不会遗漏任何东西;交叉编译器将为您提供与本机编译器相同的二进制文件(尽管有版本等)

    原生构建的“优势”来自编译后测试和管理复杂系统。

    1) 如果我可以在编译后快速运行单元测试,我可以快速解决任何错误/问题,那么这个周期可能比交叉编译周期短;

    2) 如果我正在编译一些具有它使用的第三方库的目标软件,那么在本机平台上构建、部署然后使用它们来构建我的目标可能会更容易;我不想处理它们的交叉编译构建,因为其中一半的构建过程是由疯狂的猴子编写的,这使得交叉编译它们很痛苦。

    通常,对于大多数事情,人们会尝试进行基本构建,然后本地编译其余的。除非我有一个病态的设置,我的交叉编译器速度非常快,而且我节省的时间值得进行设置以使其余的事情(例如单元测试和依赖项管理)更容易。

    至少这些是我的想法

    【讨论】:

      【解决方案3】:

      本地编译的唯一好处是您不必将程序转移到目标平台,因为它已经存在。

      但是,考虑到与现代 x86 PC 相比,大多数目标平台的功能严重不足,这并不是一个很大的好处。内存量、更快的 CPU 和特别是更快的磁盘使 PC 上的编译时间快了很多倍。以至于原生建筑的优势不再是真正的优势。

      【讨论】:

      • 所以本质上最终结果是相同的二进制文件?所以我不应该担心尝试为自己寻找一个 ARM 构建设备来编译我的软件,因为除了能够说“我的软件在 ARM 上原生编译”之外没有任何好处
      • @SnakeDoc 如果交叉编译器设置正确,则生成的二进制文件将完全没有差异。
      【解决方案4】:

      这很大程度上取决于编译器。工具链如何处理原生编译和交叉编译之间的差异。是否只是工具链始终认为它被构建为交叉编译器的一种情况,但是构建它的一种方法是让配置脚本自动检测主机而不是您手动进行(并自动设置前缀等)?

      不要仅仅因为它是为本地编译器而构建的,就认为它是真正的本地编译器。在许多情况下,发行版会降低其本机编译器(以及内核和其他二进制文件),以便该发行版可以在更广泛的系统上运行。例如,在 ARMv6 系统上,您可能正在运行默认为 ARMv4 的编译器。

      这引出了一个与您自己类似的问题,如果我使用一个默认架构构建工具链,那么指定另一个与为目标架构构建工具链有什么不同?

      理想情况下,您希望经过大部分调试的编译器/工具链能够为您提供相同的结果,无论您是本机编译还是交叉编译并且独立于默认架构。现在我在旧的 llvm 上看到 llvm-gcc 在 64 位主机上运行时,交叉编译到 arm 会将所有 int 构建为 64 位,为代码添加了很多,相同的编译器版本,相同的源代码在 32 位主机会给出不同的结果(32 位整数)。基本上 -m32 开关不适用于 llvm-gcc(当时),我不知道是否仍然如此,因为我在进行 llvm 工作时切换到 clang 并且从未回头查看 llvm-gcc ...llvm/clang例如,一直以来主要是一个交叉编译器,链接器是唯一看起来是特定于主机的东西,您可以使用现成的 llvm 并为任何主机系统上的任何目标进行编译(前提是您的构建没有禁用任何当然是支持的目标)。

      【讨论】:

        【解决方案5】:

        虽然很多人认为“本地编译”比“交叉编译”更有好处或者至少没有区别,但事实恰恰相反。

        对于在较低级别工作的人,即 linux 内核,他们通常会遭受编译平台周围的复制。以 x86 和 ARM 为例,直接的想法是构建 ARM 编译库,但这是个坏主意。

        二进制有时不一样,例如,

        # diff hello_x86.ko hello_arm.ko
        Binary files hello_x86.ko and hello_arm.ko differ
        # diff hello_x86_objdump.txt hello_arm_objdump.txt
        2c8
        < hello_x86.ko:     file format elf64-littleaarch64
        ---
        > hello_arm.ko:     file format elf64-littleaarch64
        26,27c26,27
        <    8: 91000000        add     x0, x0, #0x0
        <    c: 910003fd        mov     x29, sp
        ---
        >    8: 910003fd        mov     x29, sp
        >    c: 91000000        add     x0, x0, #0x0
        

        一般较高级别的应用可以同时使用两者,较低级别(硬件相关)的工作建议使用x86 "cross compile",因为它有更好的工具链。

        反正编译是一个关于 GCC Glibc 和 lib.so 的工作,如果熟悉这些,任何一种方式都应该容易上手。

        PS:以下是源码

        # cat hello.c
        #include <linux/module.h>      /* Needed by all modules */
        #include <linux/kernel.h>      /* Needed for KERN_ALERT */
        #include <linux/init.h>        /* Needed for the macros */
        
        
        
        static int hello3_data __initdata = 3;
        
        
        static int __init hello_3_init(void)
        {
           printk(KERN_ALERT "Hello, world %d\n", hello3_data);
           return 0;
        }
        
        
        static void __exit hello_3_exit(void)
        {
           printk(KERN_ALERT "Goodbye, world 3\n");
        }
        
        
        module_init(hello_3_init);
        module_exit(hello_3_exit);
        
        MODULE_LICENSE("GPL"); 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-05-09
          • 1970-01-01
          • 2020-02-05
          • 2020-12-28
          • 2019-05-21
          • 2011-07-24
          • 2014-12-05
          相关资源
          最近更新 更多