在一种“风味”的Linux上编译的Linux可执行文件是否可以在另一种Linux上运行?


59

一个很小的,非常简单的程序的可执行文件(例如下面所示的程序)是否可以在一种Linux上编译,并在另一种版本上运行?还是需要重新编译?

在这种情况下,机器体系结构是否重要?

int main()
{
  return (99);
}

2
感谢大家的出色回答!我学到了比预期更多的东西。我故意使代码变得人为简单,以便尽可能少地依赖库。但我真的应该事先声明。我跨平台的大多数C ++编码都涉及使用Microsoft工具(例如Visual Studio)进行开发,然后将代码移植到* nix系统并重新编译。
JCDeen

4
这里表达的许多方面和考虑因素令我惊讶!老实说,我希望我可以选择几个作为答案。再次感谢大家!真诚的
JCDeen

2
Android也是基于Linux的操作系统。不过,祝您好运,运行在此基础上编译的任何代码glibc,反之亦然。当然,这并非完全不可能
凌晨

2
为了最大程度地提高命令行工具的兼容性,您可能需要使用uClibc,musl或Dietlibc代替glibc,并静态链接您的32位可执行文件(gcc -m32 -static)。这样,任何i386或amd64 Linux都可以运行该可执行文件。

10
您应该返回42!:)
Homunculus Reticulli,

Answers:


49

这取决于。为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,或者...


14
关于第一段:不,英特尔将其称为“ Intel 64”(最近几天,在使用其他名称之后)。IA-64指的是Itanium,不兼容x86。
hobbs

1
@hobbs谢谢,我已将这些引用替换为amd64; 我将事情的名称留给英特尔市场部门。

3
为什么不提及静态链接?
dcorking

2
改变的不仅仅是库ABI,内核的syscall接口也随着时间的推移而扩展。请注意的for GNU/Linux 2.6.32输出中的(或类似内容)file /usr/bin/ls
查尔斯·达菲

1
@Wilbert你可能缺少thrig指的是Red Hat Linux的是来自Red Hat不同的企业 Linux操作系统。
鲍勃

68

简而言之:如果您要使用相同(或兼容)的体系结构将已编译的二进制文件从一台主机转移到另一台主机,则将其带到另一发行版可能会很好。但是,随着代码复杂度的增加,链接到未安装的库的可能性也越来越大。安装在另一个位置;或以其他版本安装。以您的代码为例lddgcc -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宇宙中的许多事物一样,这是一只有许多皮肤的猫,上面概述了其中的两个。


4
确实,我忘记了静态二进制文件。一些供应商采用静态二进制文件,一些恶意软件作者也为了最大程度地提高同一体系结构的
Rui F Ribeiro

8
叉车...?
user253751 '18

2
@immibis我认为这意味着将数据(可执行文件)从一个环境(发行版)复制到另一个环境(该数据不是为目标环境设计的)。
wjandrea

13
不幸的是,您的Linux示例是人为的,它说明了有关体系结构而不是发行版的观点:您在Debian上构建了32位二进制文​​件,并试图在64位RHEL上运行它。那些是不同的架构...具有很少库依赖关系的相同架构的二进制文件可以很好地复制。
斯蒂芬·基特

7
@MSalters我并不是说这是不合理的,我想这是一个不好的例子,因为DopeGhoti试图做到这一点(您不能将二进制文件从一种发行版复制到另一种发行版-这是错误的)。当然,采用适当的基础架构,Intel上的64位Linux也支持运行32位可执行文件。在这种情况下,IMO的一个有效示例是构建amd64二进制文件并在另一个amd64发行版上运行,或者构建i386二进制文件并在另一个i386发行版上运行。
斯蒂芬·基特

25

添加到出色的@thrig和@DopeGhoti答案中:传统上,Unix或类似Unix的操作系统(包括Linux)通常总是设计和对齐,以便于源代码的可移植性而不是二进制文件。

如果您的硬件没有特定的硬件或像示例中那样是简单的源代码,那么只要目标服务器安装了C开发包,就可以在几乎任何版本的Linux或体系结构之间将其毫无问题地作为源代码进行移动。必要的库,并安装了相应的开发库。

如果要从较远的Linux早期版本中移植更高级的代码,或者移植更特定的程序(例如针对不同内核版本的内核模块),则可能需要调整和修改源代码以解决已弃用的库/ API / ABI。


19

通过默认情况下,你几乎肯定会碰到与外部库的问题。其他一些答案将详细介绍这些问题,因此我不会重复他们的工作。

但是,您可以编译许多程序-甚至是平凡的程序-以便在Linux系统之间移植。关键是称为Linux Standard Base的工具箱。该LSB是专为刚刚创建这些类型的便携式应用。编译适用于LSB v5.0的应用程序,它将在实现LSB v5.0的任何其他Linux环境(具有相同体系结构)上运行。一些Linux发行版符合LSB,其他发行版包括LSB工具包/库作为可安装软件包。如果您使用LSB工具(如的lsbcc包装程序gcc)构建应用程序并链接到LSB版本的库,则将创建一个可移植的应用程序。


并且qemu甚至可以运行针对不同体系结构编译的程序(性能不高,但是可以运行它们)
Jasen

1
我什至不知道Linux Standard Base工具箱,所以谢谢!我很早以前就开始使用C / C ++,所以这些答案中的许多信息对我来说都是新的。而且非常有帮助。
JCDeen

1
维基百科上的文章说Debian和Ubuntu没有实现LSB(并且无意这样做)
。– BlackJack

2
@ BlackJack-发行版本身并未将其100%作为核心操作系统的一部分来实现,但是您可以将LSB兼容库和工具包作为可选软件包安装。我已经使用Ubuntu(例如)来构建与LSB兼容的程序,然后在Suse和Centos上运行,您只需要apt-get install几个软件包即可。
bta

10

也许。

容易破坏它的事情包括。

  1. 不同的体系结构。显然,完全不同的体系结构将无法工作(除非您使用binfmt_misc这样的用户模式qemu这样的东西,但这几乎不是正常配置)。x86二进制文件可在amd64上运行,但前提是所需的32位库可用。
  2. 库版本。如果soversion错误,那么它将根本找不到该库。如果soversion相同,但是二进制文件是针对比该库所运行的库更高的版本构建的,则可能由于新符号或符号的新版本而无法加载。特别地,glibc是符号版本控制的繁重用户,因此针对较新glibc构建的二进制文件很可能会因较旧的glibc而失败。

如果您避免使用任何快速变化的库,请避免更改体系结构并在要定位的最旧发行版上进行构建,您很有可能在多个发行版上使用一个二进制文件。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.