JCDeen Asked: 2018-01-30 12:47:47 +0800 CST2018-01-30 12:47:47 +0800 CST 2018-01-30 12:47:47 +0800 CST 在 Linux 的一种“风格”上编译的 Linux 可执行文件会在不同的 Linux 上运行吗? 772 一个非常简单的小程序的可执行文件,例如下面显示的,在一种 Linux 上编译的,是否会在另一种 Linux 上运行?还是需要重新编译? 在这种情况下,机器架构是否重要? int main() { return (99); } linux compiling 6 个回答 Voted DopeGhoti 2018-01-30T13:24:08+08:002018-01-30T13:24:08+08:00 简而言之:如果您使用相同(或兼容)架构将编译后的二进制文件从一台主机传输到另一台主机,那么将它带到另一个发行版可能会非常好。然而,随着代码复杂性的增加,链接到未安装的库的可能性;安装在其他位置;或安装在不同的版本,增加。以您的代码为例,在(Debian 派生的)Ubuntu Linux 主机上ldd编译时报告以下依赖项:gcc -o exit-test exit-test.c $ 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 宇宙中的许多事物一样,这是一只拥有许多皮肤的猫,其中两种已在上文中概述。 Best Answer thrig 2018-01-30T13:13:14+08:002018-01-30T13:13:14+08:00 这取决于。为 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 Ancient 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,或者... Rui F Ribeiro 2018-01-30T13:21:25+08:002018-01-30T13:21:25+08:00 除了出色的@thrig 和@DopeGhoti 答案之外:Unix 或类 Unix 操作系统,包括 Linux,传统上总是为源代码的可移植性而不是二进制文件而设计和对齐。 如果没有任何特定的硬件或像您的示例那样是简单的源代码,那么只要目标服务器安装了 C 开发包,您就可以在几乎任何版本的 Linux 或体系结构之间作为源代码毫无问题地移动它,必要的库,以及安装的相应开发库。 至于从较早版本的 Linux 移植更高级的代码,或者更具体的程序(如针对不同内核版本的内核模块),您可能必须调整和修改源代码以解决不推荐使用的库/API/ABI。 bta 2018-01-30T15:37:14+08:002018-01-30T15:37:14+08:00 默认情况下,您几乎肯定会遇到外部库的问题。其他一些答案更详细地介绍了这些问题,所以我不会重复他们的工作。 但是,您可以编译许多程序——甚至是不平凡的程序——以便在 Linux 系统之间移植。关键是称为Linux Standard Base的工具包。LSB专为创建这些类型的便携式应用程序而设计。为 LSB v5.0 编译一个应用程序,它将在任何其他实现 LSB v5.0 的 Linux 环境(相同架构)上运行。一些 Linux 发行版与 LSB 兼容,而其他发行版包括 LSB 工具包/库作为可安装包。lsbcc如果您使用 LSB 工具(如包装器)构建应用程序gcc并链接到 LSB 版本的库,您将创建一个可移植的应用程序。 plugwash 2018-01-31T06:35:08+08:002018-01-31T06:35:08+08:00 也许。 倾向于破坏它的事情包括。 不同的架构。显然,完全不同的架构是行不通的(除非你有类似用户模式 qemu 和 binfmt_misc 的东西,但这几乎不是正常的配置)。x86 二进制文件可以在 amd64 上运行,但前提是所需的 32 位库可用。 库版本。如果转换错误,那么它根本找不到库。如果 soversion 相同,但二进制文件是针对比运行时更新的库版本构建的,那么它可能由于新符号或符号的新版本而无法加载。特别是 glibc 是符号版本控制的重度用户,因此针对较新 glibc 构建的二进制文件很可能在较旧的 glibc 上失败。 如果您避免使用任何快速变化的库,避免更改架构并在您想要定位的最古老的发行版上构建,那么您很有可能使一个二进制文件在许多发行版上工作。 Ben 2018-02-02T04:55:06+08:002018-02-02T04:55:06+08:00 除了前面提到的一些事情之外,可执行文件格式也发生了一些变化。大多数情况下,linux 使用 ELF,但旧版本使用 a.out 或 COFF。 维基洞的开始: https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats 可能有一种方法可以让旧版本运行新格式,但我个人从未研究过。
简而言之:如果您使用相同(或兼容)架构将编译后的二进制文件从一台主机传输到另一台主机,那么将它带到另一个发行版可能会非常好。然而,随着代码复杂性的增加,链接到未安装的库的可能性;安装在其他位置;或安装在不同的版本,增加。以您的代码为例,在(Debian 派生的)Ubuntu Linux 主机上
ldd
编译时报告以下依赖项:gcc -o exit-test exit-test.c
显然,如果我把它踢到 Mac (
./exit-test: cannot execute binary file: Exec format error
) 上,这个二进制文件就不会运行。让我们尝试将其移动到 RHEL 盒子中:哦亲爱的。为什么会这样?
即使对于这个用例,由于缺少共享库,叉车也失败了。
但是,如果我用 编译它
gcc -static exit-test-static exit-test.c
,将它移植到没有库的系统就可以了。当然,以磁盘空间为代价:另一个可行的解决方案是在新主机上安装必要的库。
与 U&L 宇宙中的许多事物一样,这是一只拥有许多皮肤的猫,其中两种已在上文中概述。
这取决于。为 IA-32(Intel 32 位)编译的东西可以在 amd64 上运行,因为 Intel 上的 Linux 保留了与 32 位应用程序的向后兼容性(安装了合适的软件)。这是您
code
在 RedHat 7.3 32 位系统(大约 2002 年,gcc 版本 2.96)上编译的,然后将二进制文件复制到 Centos 7.4 64 位系统(大约 2017 年)并在其上运行:Ancient 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
为amd64 系统可以处理(在 Linux 系统上——相比之下,在 amd64 上的 OpenBSD不支持 32 位二进制文件;与旧拱门的向后兼容性确实为攻击者提供了回旋余地,例如CVE-2014-8866和朋友)。同时在大端 MIPS 系统
main
上编译为:英特尔处理器将不知道如何处理,同样对于 MIPS 上的英特尔组件。
您可以使用 QEMU 或其他一些模拟器来运行外部代码(可能非常非常慢)。
然而!你的代码是非常简单的代码,所以移植性问题比其他任何东西都要少;程序通常使用随时间变化的库(glibc、openssl、...);对于那些可能还需要安装各种库的旧版本(例如,RedHat 通常将“compat”放在包名称中的某个位置)
或者可能担心使用 glibc 的旧事物的 ABI 更改(应用程序二进制接口),或者最近由于 C++11 或其他 C++ 版本而发生的更改。也可以编译静态(大大增加磁盘上的二进制文件大小)来避免库问题,尽管某些旧的二进制文件是否这样做取决于旧的 Linux 发行版是否正在编译大多数动态(RedHat:是)。另一方面,
patchelf
可以重新调整动态(ELF,但可能不是a.out
格式)二进制文件以使用其他库。然而!能够运行一个程序是一回事,实际上用它做一些有用的事情是另一回事。旧的 32 位 Intel 二进制文件可能存在安全问题,如果它们依赖的 OpenSSL 版本存在一些可怕且未向后移植的安全问题,或者程序可能根本无法与现代 Web 服务器进行协商(如现代服务器拒绝旧程序的旧协议和密码),或者不再支持 SSH 协议版本 1,或者...
除了出色的@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 工具包/库作为可安装包。
lsbcc
如果您使用 LSB 工具(如包装器)构建应用程序gcc
并链接到 LSB 版本的库,您将创建一个可移植的应用程序。也许。
倾向于破坏它的事情包括。
如果您避免使用任何快速变化的库,避免更改架构并在您想要定位的最古老的发行版上构建,那么您很有可能使一个二进制文件在许多发行版上工作。
除了前面提到的一些事情之外,可执行文件格式也发生了一些变化。大多数情况下,linux 使用 ELF,但旧版本使用 a.out 或 COFF。
维基洞的开始:
https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats
可能有一种方法可以让旧版本运行新格式,但我个人从未研究过。