gcc使用术语“体系结构”来表示特定CPU的“指令集”,“目标”涵盖CPU和体系结构的组合以及其他变量,例如ABI,libc,endian-ness等(可能包括“裸机”)。典型的编译器具有有限的目标组合集(可能是一个ABI,一个CPU系列,但可能是32位和64位)。交叉编译器通常是指目标不是其运行系统的编译器,或者是具有多个目标或ABI的编译器(另请参见this)。
二进制文件可以跨不同的CPU架构移植吗?
一般来说,没有。传统意义上的二进制是特定CPU或CPU系列的本机目标代码。但是,在某些情况下,它们可能中等到高度可移植:
- 一种架构是另一种架构的超集(通常,x86二进制文件针对的是i386或i686,而不是最新,最出色的x86,例如
-march=core2
)
- 一种架构提供本机仿真或另一种架构的翻译(您可能听说过Crusoe),或者提供兼容的协处理器(例如PS2)
- 操作系统和运行时支持多体系结构(例如,能够在x86_64上运行32位x86二进制文件),或者使VM / JIT无缝(使用Dalvik或ART的 Android )
- 支持“胖”二进制文件,该文件实际上包含每种受支持体系结构的重复代码
如果您设法解决了这个问题,那么无数库版本的另一个可移植二进制问题(我正在看的glibc)将介绍自己。(大多数嵌入式系统至少可以使您免受该特定问题的困扰。)
如果您还没有,现在是时候运行gcc -dumpspecs
并gcc --target-help
查看您遇到的挑战。
胖二进制文件有很多缺点,但仍有潜在用途(EFI)。
但是,其他答案缺少两个注意事项:ELF和ELF解释器,以及Linux内核对任意二进制格式的支持。尽管可以将它们视为“本机”并执行Java或已编译的Python字节码二进制文件,但我不会在这里详细介绍非真实处理器的二进制文件或字节代码,此类二进制文件与硬件体系结构无关(但取决于硬件)在相关的VM版本上运行,最终运行本机二进制文件)。
任何现代的Linux系统都将使用ELF二进制文件(此PDF中的技术细节),对于动态ELF二进制文件,内核负责将映像加载到内存中,但这是ELF中设置的“解释器”的工作。标头要做繁重的工作。通常,这涉及确保所有相关的动态库都可用(借助于“动态”部分,该部分列出了库以及列出所需符号的其他结构),但这几乎是通用的间接层。
$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses \
shared libs), stripped
$ readelf -p .interp /bin/ls
String dump of section '.interp':
[ 0] /lib/ld-linux.so.2
(/lib/ld-linux.so.2
也是ELF二进制文件,它没有解释程序,并且是本机二进制代码。)
ELF的问题在于,二进制文件(readelf -h /bin/ls
)中的标头将其标记为特定的体系结构,类(32位或64位),字节序和ABI(Apple的“通用”胖二进制文件使用替代的二进制格式Mach-O而是解决了这个问题,它起源于NextSTEP。这意味着ELF可执行文件必须与要在其上运行的系统匹配。一个逃生舱口是解释器,它可以是任何可执行文件(包括提取或映射原始二进制文件的体系结构特定子部分并调用它们的可执行文件),但是您仍然受到系统允许运行的ELF类型的限制。(FreeBSD有一种有趣的处理Linux ELF文件的方式,它brandelf
修改了ELF ABI字段。)
在Linux上(使用binfmt_misc
)对Mach-O的支持,那里有一个示例,向您展示了如何创建和运行胖(32位和64位)二进制文件。如最初在Mac上所做的那样,资源派生/ ADS可能是一种解决方法,但是没有本机Linux文件系统支持此功能。
内核模块或多或少都具有相同的含义,.ko
文件也是ELF(尽管没有设置解释器)。在这种情况下,会有一个额外的层uname -r
在搜索路径中使用内核版本(),理论上可以在带有版本控制的ELF中完成此操作,但我认为这样做有些复杂且几乎没有收益。
如其他地方所述,Linux本身并不支持胖二进制文件,但是有一个活跃的胖二进制项目:FatELF。它已经存在多年了,部分由于专利问题(现已过期)而从未集成到标准内核中。目前,它需要内核和工具链支持。它不使用binfmt_misc
方法,这回避了ELF标头问题,也允许使用胖内核模块。
- 如果我有一个编译为可以在“ x86目标,Linux OS版本xyz”上运行的应用程序,我可以在另一个系统“ ARM目标,Linux OS版本xyz”上运行相同的编译二进制文件吗?
如果没有ELF,它将无法让您执行此操作。
- 如果上述情况不成立,唯一的方法是使用相关工具链“例如arm-linux-gnueabi”来获取应用程序源代码以进行重建/重新编译?
简单的答案是。(复杂的答案包括仿真,中间表示,转换器和JIT;除了将i686二进制文件“降级”以仅使用i386操作码的情况外,它们在这里可能没有什么用处,而且ABI修复可能与翻译本机代码一样困难。 )
- 同样,如果我有一个可加载的内核模块(设备驱动程序)可以在“ x86目标,Linux OS版本xyz”上工作,我是否可以在另一个系统“ ARM目标,Linux OS版本xyz”上加载/使用相同的已编译.ko? ?
不,ELF不允许您这样做。
- 如果上述情况不成立,唯一的方法是使用相关工具链“例如arm-linux-gnueabi”来获取驱动程序源代码进行重建/重新编译?
简单的答案是。我相信FatELF可以让您构建一个.ko
多架构的架构,但是在某个时候,必须为每种受支持的架构创建一个二进制版本。需要内核模块的内容通常随源一起提供,并根据需要进行构建,例如VirtualBox就是这样做的。
这已经是一个漫长的漫漫答案,只有一个弯路。内核已经内置了一个虚拟机,尽管它是专用的:用于匹配数据包的BPF VM。人类可读的过滤器“ host foo而不是端口22”被编译为字节码,内核数据包过滤器执行该字节码。新的eBPF不仅适用于数据包,从理论上讲,VM代码可在任何现代linux上移植,而llvm支持它,但出于安全原因,它可能不适合管理规则以外的任何其他条件。
现在,根据您对二进制可执行文件的定义的慷慨程度,您可以(滥用)使用binfmt_misc
Shell脚本和ZIP文件作为容器格式来实现对二进制文件的支持:
#!/bin/bash
name=$1
prog=${1/*\//} # basename
prog=${prog/.woz/} # remove extension
root=/mnt/tmpfs
root=$(TMPDIR= mktemp -d -p ${root} woz.XXXXXX)
shift # drop argv[0], keep other args
arch=$(uname -m) # i686
uname_s=$(uname -s) # Linux
glibc=$(getconf GNU_LIBC_VERSION) # glibc 2.17
glibc=${glibc// /-} # s/ /-/g
# test that "foo.woz" can unzip, and test "foo" is executable
unzip -tqq "$1" && {
unzip -q -o -j -d ${root} "$1" "${arch}/${uname_s}/${glibc}/*"
test -x ${root}/$prog && (
export LD_LIBRARY_PATH="${root}:${LD_LIBRARY_PATH}"
#readlink -f "${root}/${prog}" # for the curious
exec -a "${name}" "${root}/${prog}" "$@"
)
rc=$?
#rm -rf -- "${root}/${prog}" # for the brave
exit $rc
}
将此称为“ wozbin”,并使用类似以下内容进行设置:
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
printf ":%s:%s:%s:%s:%s:%s:%s" \
"woz" "E" "" "woz" "" "/path/to/wozbin" "" > /proc/sys/fs/binfmt_misc/register
这会.woz
向内核注册文件,wozbin
而是通过将其第一个参数设置为被调用.woz
文件的路径来调用脚本。
要获取可移植(胖).woz
文件,只需创建test.woz
具有目录层次结构的ZIP文件,即可:
i686/
\- Linux/
\- glibc-2.12/
armv6l/
\- Linux/
\- glibc-2.17/
在每个arch / OS / libc目录中(任意选择),放置特定于体系结构的test
二进制文件和组件(例如.so
文件)。调用它时,所需的子目录被提取到tmpfs内存文件系统中(在/mnt/tmpfs
此处)并被调用。