Linux二进制程序移植¶
Linux 在二进制程序移植方面表现并不是很出色。 不同 Linux 系统,二进制库千差万别;而且内核接口也多有变动。 尽管如此,构建一个可运行于多种系统环境的二进制程序还是可以做到的。 注意到, Adobe 为 Linux 系统构建的二进制 Flash 播放器似乎可以在各种系统上运行。 最近我需要 在一个系统上构建二进制程序,并在其他系统上运行 。 由于我只使用 C 库函数,因此预计难度不大。 然而并不是如此。 由于 Linux 二进制是向下兼容的,因此 最保险的做法是在老系统编译代码 ( chroot 到一个老版本的 Linux 环境亦可)。 当然了,还是存在一些潜在的问题需要避开。
浮点异常¶
新系统编译的二进制程序在老系统上执行可能因 浮点异常 ( SIGFPE )而崩溃。 根源在于:新版链接器生成新的 .gnu.hash 节,而不是原来的 .hash 节。 老 Linux 系统不知道如何链接二进制程序,导致了这个异常。 为 GCC 指定参数 -Wl,–hash-style=both 便可解决这个问题。
静态链接C++标准库¶
如果用到 C++ 标准库,程序可能依赖一个目标系统上没有的 libstdc++ 版本。 虽然比较繁琐,但还是可以通过 静态链接libstdc++ 来避免。
glibc不兼容¶
Linux 采用 符号版本 ( symbol versioning )来保护 C 库向下兼容性。 如果在新系统构建二进制程序,程序可能会依赖新版的 glibc 。 为了确认二进制程序依赖的版本,可以运行 readelf 命令:
$ readelf -V [binary]
命令输出信息大致如下:
Version needs section '.gnu.version_r' contains 5 entries:
Addr: 0x000000000804901c Offset: 0x00101c Link to section: 7 (.dynstr)
000000: Version: 1 File: libpthread.so.0 Cnt: 1
0x0010: Name: GLIBC_2.0 Flags: none Version: 11
0x0020: Version: 1 File: librt.so.1 Cnt: 1
0x0030: Name: GLIBC_2.2 Flags: none Version: 8
0x0040: Version: 1 File: ld-linux.so.2 Cnt: 1
0x0050: Name: GLIBC_2.3 Flags: none Version: 6
0x0060: Version: 1 File: libgcc_s.so.1 Cnt: 3
0x0070: Name: GCC_4.2.0 Flags: none Version: 9
0x0080: Name: GCC_3.3 Flags: none Version: 5
0x0090: Name: GCC_3.0 Flags: none Version: 3
0x00a0: Version: 1 File: libc.so.6 Cnt: 4
0x00b0: Name: GLIBC_2.4 Flags: none Version: 10
0x00c0: Name: GLIBC_2.2 Flags: none Version: 7
0x00d0: Name: GLIBC_2.3.2 Flags: none Version: 4
0x00e0: Name: GLIBC_2.0 Flags: none Version: 2
如果发现有不对的版本,可以通过 nm 命令查出依赖它的符号:
$ nm [binary] | grep [name]
输出大致如下:
U __stack_chk_fail@@GLIBC_2.4
GCC 栈保护 ( stack protector )功能需要用到这个函数,而老版本的 glibc 并不提供! 该功能在 Fedora 和 Ubuntu 下默认是开启的, 因此为了避免 未知符号 ( missing symbol )错误, 需要通过编译参数 -fnostack-protector 显式关闭。
注解
栈保护 功能用于应对 栈攻击 : 一旦检测到便强行退出程序并输出错误信息。