一个很小的,非常简单的程序的可执行文件(例如下面所示的程序)是否可以在一种Linux上编译,并在另一种版本上运行?还是需要重新编译?
在这种情况下,机器体系结构是否重要?
int main()
{
return (99);
}
gcc -m32 -static
)。这样,任何i386或amd64 Linux都可以运行该可执行文件。
一个很小的,非常简单的程序的可执行文件(例如下面所示的程序)是否可以在一种Linux上编译,并在另一种版本上运行?还是需要重新编译?
在这种情况下,机器体系结构是否重要?
int main()
{
return (99);
}
gcc -m32 -static
)。这样,任何i386或amd64 Linux都可以运行该可执行文件。
Answers:
这取决于。为IA-32(Intel 32位)编译的某些程序可能会在amd64上运行,因为Intel上的Linux保留了与32位应用程序(安装了合适的软件)的向后兼容性。这是您code
在RedHat 7.3 32位系统(大约2002年,gcc版本2.96)上编译的文件,然后将二进制文件复制到Centos 7.4 64位系统上并在其上运行(大约2017年):
-bash-4.2$ file code
code: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped
-bash-4.2$ ./code
-bash: ./code: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
-bash-4.2$ sudo yum -y install glibc.i686
...
-bash-4.2$ ./code ; echo $?
99
古老的RedHat 7.3到Centos 7.4(本质上是RedHat Enterprise Linux 7.4)仍属于同一“发行版”系列,因此,与从2002年开始的一些随机“ Linux从头开始”安装到2018年的其他一些随机Linux发行版相比,它可能具有更好的可移植性。 。
为amd64编译的内容不能在仅32位版本的Linux上运行(旧硬件不了解新硬件)。对于打算在古老的旧事物上运行的,在现代系统上编译的新软件,这也是如此,因为库甚至系统调用可能都不是向后移植的,因此可能需要编译技巧,或者获取旧的编译器等等,或者相反在旧系统上编译。(这是保留古老旧物的虚拟机的一个很好的理由。)
建筑确实很重要;amd64(或IA-32)与ARM或MIPS有很大不同,因此不会期望其中之一的二进制文件可以在另一文件上运行。在汇编级别main
,IA-32上的代码部分将通过编译gcc -S code.c
至
main:
pushl %ebp
movl %esp,%ebp
movl $99,%eax
popl %ebp
ret
amd64系统可以处理的内容(在Linux系统上,相比之下,在amd64 上, OpenBSD 不支持32位二进制文件;与旧版本的向后兼容性确实为攻击者提供了摆动的空间,例如CVE-2014-8866和朋友)。同时,在大端MIPS系统上,其 main
编译为:
main:
.frame $fp,8,$31
.mask 0x40000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
addiu $sp,$sp,-8
sw $fp,4($sp)
move $fp,$sp
li $2,99
move $sp,$fp
lw $fp,4($sp)
addiu $sp,$sp,8
j $31
nop
英特尔处理器不知道该如何处理,MIPS上的英特尔组件也是如此。
您可能会使用QEMU或其他一些模拟器来运行外来代码(也许非常非常慢)。
然而!您的代码是非常简单的代码,因此可移植性问题比其他任何事物都要少。程序通常使用随时间变化的库(glibc,openssl等);对于那些人可能还需要安装各种库的旧版本(例如,RedHat通常将“ compat”放在包名称中的某个位置)
compat-glibc.x86_64 1:2.12-4.el7.centos
或可能担心使用glibc的旧方法的ABI更改(应用程序二进制接口),或者由于C ++ 11或其他C ++版本而导致的最近更改。一个人也可以编译静态文件(极大地增加磁盘上的二进制文件大小)来避免库问题,尽管这是否是一些旧的二进制文件取决于旧的Linux发行版是否编译了大多数动态文件(RedHat:是)。另一方面,类似的东西patchelf
可以重新调整动态(ELF,但可能不a.out
格式化)二进制文件以使用其他库。
然而!能够运行程序是一回事,而实际上可以对它做一些有用的事情。如果旧的32位Intel二进制文件依赖于其中包含一些可怕且未向后移植的安全问题的OpenSSL版本,或者该程序可能无法与现代Web服务器进行协商(例如,服务器拒绝旧协议和旧程序的密码),或者不再支持SSH协议版本1,或者...
for GNU/Linux 2.6.32
输出中的(或类似内容)file /usr/bin/ls
。
简而言之:如果您要使用相同(或兼容)的体系结构将已编译的二进制文件从一台主机转移到另一台主机,则将其带到另一发行版可能会很好。但是,随着代码复杂度的增加,链接到未安装的库的可能性也越来越大。安装在另一个位置;或以其他版本安装。以您的代码为例ldd
,gcc -o exit-test exit-test.c
在(从Debian派生的)Ubuntu Linux主机上进行编译时,该代码报告以下依赖项:
$ ldd exit-test
linux-gate.so.1 => (0xb7748000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757b000)
/lib/ld-linux.so.2 (0x8005a000)
显然,如果我将其插入Mac(./exit-test: cannot execute binary file: Exec format error
),该二进制文件将无法运行。让我们尝试将其移至RHEL框:
$ ./exit-test
-bash: ./exit-test: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
噢亲爱的。为什么会这样呢?
$ ls /lib/ld-l* # reference the `ldd` output above
ls: cannot access /lib/ld-l*: No such file or directory
即使在此用例中,由于缺少共享库,铲车也失败了。
但是,如果我使用进行编译gcc -static exit-test-static exit-test.c
,则可以在没有库的情况下将其移植到系统中。当然,以磁盘空间为代价:
$ ls -l ./exit-test{,-static}
-rwxr-xr-x 1 username groupname 7312 Jan 29 14:18 ./exit-test
-rwxr-xr-x 1 username groupname 728228 Jan 29 14:27 ./exit-test-static
另一个可行的解决方案是在新主机上安装必需的库。
与U&L宇宙中的许多事物一样,这是一只有许多皮肤的猫,上面概述了其中的两个。
amd64
二进制文件并在另一个amd64
发行版上运行,或者构建i386
二进制文件并在另一个i386
发行版上运行。
添加到出色的@thrig和@DopeGhoti答案中:传统上,Unix或类似Unix的操作系统(包括Linux)通常总是设计和对齐,以便于源代码的可移植性而不是二进制文件。
如果您的硬件没有特定的硬件或像示例中那样是简单的源代码,那么只要目标服务器安装了C开发包,就可以在几乎任何版本的Linux或体系结构之间将其毫无问题地作为源代码进行移动。必要的库,并安装了相应的开发库。
如果要从较远的Linux早期版本中移植更高级的代码,或者移植更特定的程序(例如针对不同内核版本的内核模块),则可能需要调整和修改源代码以解决已弃用的库/ API / ABI。
通过默认情况下,你几乎肯定会碰到与外部库的问题。其他一些答案将详细介绍这些问题,因此我不会重复他们的工作。
但是,您可以编译许多程序-甚至是平凡的程序-以便在Linux系统之间移植。关键是称为Linux Standard Base的工具箱。该LSB是专为刚刚创建这些类型的便携式应用。编译适用于LSB v5.0的应用程序,它将在实现LSB v5.0的任何其他Linux环境(具有相同体系结构)上运行。一些Linux发行版符合LSB,其他发行版包括LSB工具包/库作为可安装软件包。如果您使用LSB工具(如的lsbcc
包装程序gcc
)构建应用程序并链接到LSB版本的库,则将创建一个可移植的应用程序。
qemu
甚至可以运行针对不同体系结构编译的程序(性能不高,但是可以运行它们)
apt-get install
几个软件包即可。
也许。
容易破坏它的事情包括。
如果您避免使用任何快速变化的库,请避免更改体系结构并在要定位的最旧发行版上进行构建,您很有可能在多个发行版上使用一个二进制文件。
除了前面提到的某些内容外,可执行文件格式也进行了一些更改。在大多数情况下,Linux使用ELF,而较早的版本使用a.out或COFF。
Wikihole的开始:
https://zh.wikipedia.org/wiki/Comparison_of_executable_file_formats
也许有一种方法可以使旧版本运行新格式,但是我个人从未研究过。