foo*_*oob 17 binary application portability cpu-architecture
我的目标是能够为嵌入式 Linux 进行开发。我有使用 ARM 的裸机嵌入式系统的经验。
我有一些关于针对不同 cpu 目标进行开发的一般问题。我的问题如下:
如果我有一个应用程序编译为在“ x86 目标,linux 操作系统版本 xyz ”上运行,我可以在另一个系统“ ARM 目标,linux 操作系统版本 xyz ”上运行相同的编译二进制文件吗?
如果上述情况不正确,唯一的方法是使用相关工具链“例如 arm-linux-gnueabi”获取应用程序源代码来重建/重新编译?
同样,如果我有一个可加载的内核模块(设备驱动程序)可以在“ x86 目标,linux 操作系统版本 xyz ”上运行,我是否可以在另一个系统“ ARM 目标,linux 操作系统版本 xyz ”上加载/使用相同的编译 .ko ?
如果以上不正确,唯一的方法是使用相关工具链“例如 arm-linux-gnueabi”获取驱动程序源代码来重建/重新编译?
Eli*_*fox 43
不可以。必须为目标体系结构(重新)编译二进制文件,而 Linux 没有像开箱即用的胖二进制文件那样提供任何东西。原因是代码被编译为特定架构的机器代码,而大多数处理器系列之间的机器代码非常不同(例如 ARM 和 x86 非常不同)。
编辑:值得注意的是,某些架构提供了向后兼容性级别(甚至更少见,与其他架构的兼容性);在 64 位 CPU 上,向后兼容 32 位版本是很常见的(但请记住:您的依赖库也必须是 32 位的,包括您的 C 标准库,除非您静态链接)。另外值得一提的是Itanium,它可以运行 x86 代码(仅限 32 位),尽管速度很慢;x86 代码执行速度不佳至少是它在市场上不太成功的部分原因。
请记住,即使在兼容模式下,您仍然不能在较旧的 CPU 上使用使用较新指令编译的二进制文件(例如,您不能在Nehalem x86 处理器上的 32 位二进制文件中使用 AVX ;CPU 只是不支持它。
请注意,必须为相关架构编译内核模块;此外,32 位内核模块不能在 64 位内核上工作,反之亦然。
有关交叉编译二进制文件的信息(因此您不必在目标 ARM 设备上安装工具链),请参阅下面的 Grochmal 综合回答。
gro*_*mal 16
Elizabeth Myers 是正确的,每个架构都需要针对相关架构的编译二进制文件。要为与系统运行的架构不同的架构构建二进制文件,您需要一个cross-compiler.
在大多数情况下,您需要编译交叉编译器。我只有经验gcc(但我相信llvm和其他编译器具有类似的参数)。甲gcc交叉编译器通过将实现--target到配置:
./configure --build=i686-arch-linux-gnu --target=arm-none-linux-gnueabi
Run Code Online (Sandbox Code Playgroud)
您需要编译gcc,glibc并binutils使用这些参数(并在目标机器上提供内核的内核头文件)。
实际上,这要复杂得多,并且在不同的系统上会出现不同的构建错误。
有几个关于如何编译 GNU 工具链的指南,但我会推荐Linux From Scratch,它不断维护并且在解释所提供的命令的作用方面做得非常好。
另一种选择是交叉编译器的引导编译。由于在不同架构上将交叉编译器编译为不同架构的努力crosstool-ng而产生。它提供了构建交叉编译器所需的工具链的引导程序。
crosstool-ng支持不同架构上的多个目标三元组,基本上它是一个引导程序,人们花时间整理交叉编译器工具链编译过程中发生的问题。
几个发行版提供交叉编译器作为包:
arch提供开箱即用的mingw 交叉编译器和arm eabi 交叉编译器。除了 AUR 中的其他交叉编译器。
fedora包含几个打包的交叉编译器。
ubuntu也提供了一个arm 交叉编译器。
debian有一个完整的跨工具链存储库
换句话说,检查您的发行版在交叉编译器方面可用的内容。如果您的发行版没有满足您需求的交叉编译器,您可以随时自行编译。
参考:
如果您手动编译交叉编译器,则您拥有编译内核模块所需的一切。这是因为您需要内核头文件来编译glibc.
但是,如果您使用的是发行版提供的交叉编译器,则需要在目标机器上运行的内核的内核头文件。
请注意,作为最后的手段(即当您没有源代码时),您可以使用qemu,dosbox或等模拟器在不同的体系结构上运行二进制文件exagear。一些模拟器旨在模拟 Linux 以外的系统(例如dosbox,旨在运行 MS-DOS 程序,并且有大量模拟器可用于流行的游戏机)。仿真具有显着的性能开销:仿真程序的运行速度比原生程序慢 2-10 倍。
如果您需要在非本机 CPU 上运行内核模块,则必须模拟整个操作系统,包括相同架构的内核。AFAIK 在 Linux 内核中运行外部代码是不可能的。
不仅二进制文件不能在 x86 和 ARM 之间移植,还有不同风格的 ARM。
您在实践中可能会遇到的是 ARMv6 与 ARMv7。Raspberry Pi 1 是 ARMv6,以后的版本是 ARMv7。因此,可以在后来在 Pi 1 上不起作用的代码上编译代码。
幸运的是,开源和自由软件的一个好处是拥有源代码,因此您可以在任何架构上重建它。虽然这可能需要一些工作。
(ARM 版本控制令人困惑,但如果数字前有 V,则表示指令集体系结构 (ISA)。如果没有,则是型号,如“Cortex M0”或“ARM926EJS”。型号没有任何意义使用 ISA 号码。)
你总是需要瞄准一个平台。在最简单的情况下,目标 CPU 直接运行二进制编译的代码(这大致对应于 MS DOS 的 COM 可执行文件)。让我们考虑一下我刚刚发明的两个不同的平台——Armistice 和 Intellio。在这两种情况下,我们都会有一个简单的 hello world 程序,它在屏幕上输出 42。我还将假设您以与平台无关的方式使用多平台语言,因此两者的源代码相同:
Print(42)
Run Code Online (Sandbox Code Playgroud)
在 Armistice 上,您有一个简单的设备驱动程序来处理打印数字,因此您所要做的就是输出到端口。在我们的可移植汇编语言中,这将对应于以下内容:
out 1234h, 42
Run Code Online (Sandbox Code Playgroud)
但是,或者Intellio系统没有这样的东西,所以还得经过其他层:
mov a, 10h
mov c, 42
int 13h
Run Code Online (Sandbox Code Playgroud)
哎呀,在我们甚至在使用机器代码之前,我们已经有了两者之间的显着差异!这大致对应于您在 Linux 和 MS DOS 之间或 IBM PC 和 X-Box 之间的差异(即使两者可能使用相同的 CPU)。
但这就是操作系统的用途。让我们假设我们有一个 HAL,它确保所有不同的硬件配置在应用程序层上以相同的方式处理 - 基本上,即使在停战协议上,我们也会使用 Intellio 方法,并且我们的“可移植程序集”代码最终是相同的。这被现代类 Unix 系统和 Windows 使用,通常甚至在嵌入式场景中。很好 - 现在我们可以在 Armistice 和 Intellio 上拥有相同的真正可移植的汇编代码。但是二进制文件呢?
正如我们所假设的,CPU 需要直接执行二进制文件。让我们看看mov a, 10hIntellio 上代码的第一行:
20 10
Run Code Online (Sandbox Code Playgroud)
哦。事实证明,它mov a, constant是如此流行,它有自己的指令,有自己的操作码。停战协议如何处理?
36 01 00 10
Run Code Online (Sandbox Code Playgroud)
唔。有操作码mov.reg.imm,所以我们需要另一个参数来选择我们分配给的寄存器。并且常量总是一个 2 字节的字,采用大端符号——这就是 Armistice 的设计方式,事实上,Armistice 中的所有指令都是 4 个字节长,没有例外。
现在想象在 Armistice 上运行 Intellio 的二进制文件:CPU 开始解码指令,找到 opcode 20h。在停战协议上,这对应于and.imm.reg指令。它尝试读取 2 字节字常量(读取10XX,已经有问题),然后是寄存器编号(另一个XX)。我们正在使用错误的参数执行错误的指令。更糟糕的是,下一条指令将完全是假的,因为我们实际上吃了另一条指令,认为它是数据。
应用程序没有机会工作,它很可能会立即崩溃或挂起。
现在,这并不意味着可执行文件总是需要说它在 Intellio 或 Armistice 上运行。您只需要定义一个独立于 CPU(例如bash在 Unix 上)或 CPU 和操作系统(例如 Java 或 .NET,现在甚至是 JavaScript 之类)的平台。在这种情况下,应用程序可以为所有不同的 CPU 和操作系统使用一个可执行文件,而目标系统上有一些应用程序或服务(直接针对正确的 CPU 和/或操作系统)将独立于平台的代码转换为CPU实际上可以执行。这可能会也可能不会对性能、成本或能力造成影响。
CPU 通常以系列形式出现。例如,x86 系列的所有 CPU 都有一组以完全相同方式编码的通用指令,因此每个 x86 CPU 都可以运行每个 x86 程序,只要它不尝试使用任何扩展(例如,浮点运算或向量运算)。在 x86 上,今天最常见的例子当然是 Intel 和 AMD。Atmel 是一家著名的设计 ARM 系列 CPU 的公司,在嵌入式设备中非常流行。例如,Apple 也有自己的 ARM CPU。
但是 ARM 与 x86 完全不兼容——它们有非常不同的设计要求,并且几乎没有共同点。这些指令具有完全不同的操作码,它们以不同的方式解码,内存地址的处理方式不同......通过使用一些安全操作,可以制作在 x86 CPU 和 ARM CPU 上运行的二进制文件区分两者并跳转到两个完全不同的指令集,但这仍然意味着您对两个版本都有单独的指令,只有一个引导程序在运行时选择正确的指令集。
| 归档时间: |
|
| 查看次数: |
15742 次 |
| 最近记录: |