Linux二进制程序移植

Linux 在二进制程序移植方面表现并不是很出色。 不同 Linux 系统,二进制库千差万别;而且内核接口也多有变动。 尽管如此,构建一个可运行于多种系统环境的二进制程序还是可以做到的。 注意到, AdobeLinux 系统构建的二进制 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 并不提供! 该功能在 FedoraUbuntu 下默认是开启的, 因此为了避免 未知符号 ( missing symbol )错误, 需要通过编译参数 -fnostack-protector 显式关闭。

注解

栈保护 功能用于应对 栈攻击 : 一旦检测到便强行退出程序并输出错误信息。

下一步

订阅更新,获取更多学习资料,请关注我们的 微信公众号

../_images/wechat-mp-qrcode.png

小菜学编程