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 命令:

  1. $ readelf -V [binary]

命令输出信息大致如下:

  1. Version needs section '.gnu.version_r' contains 5 entries:
  2. Addr: 0x000000000804901c Offset: 0x00101c Link to section: 7 (.dynstr)
  3. 000000: Version: 1 File: libpthread.so.0 Cnt: 1
  4. 0x0010: Name: GLIBC_2.0 Flags: none Version: 11
  5. 0x0020: Version: 1 File: librt.so.1 Cnt: 1
  6. 0x0030: Name: GLIBC_2.2 Flags: none Version: 8
  7. 0x0040: Version: 1 File: ld-linux.so.2 Cnt: 1
  8. 0x0050: Name: GLIBC_2.3 Flags: none Version: 6
  9. 0x0060: Version: 1 File: libgcc_s.so.1 Cnt: 3
  10. 0x0070: Name: GCC_4.2.0 Flags: none Version: 9
  11. 0x0080: Name: GCC_3.3 Flags: none Version: 5
  12. 0x0090: Name: GCC_3.0 Flags: none Version: 3
  13. 0x00a0: Version: 1 File: libc.so.6 Cnt: 4
  14. 0x00b0: Name: GLIBC_2.4 Flags: none Version: 10
  15. 0x00c0: Name: GLIBC_2.2 Flags: none Version: 7
  16. 0x00d0: Name: GLIBC_2.3.2 Flags: none Version: 4
  17. 0x00e0: Name: GLIBC_2.0 Flags: none Version: 2

如果发现有不对的版本,可以通过 nm 命令查出依赖它的符号:

  1. $ nm [binary] | grep [name]

输出大致如下:

  1. U __stack_chk_fail@@GLIBC_2.4

GCC栈保护 ( stack protector )功能需要用到这个函数,而老版本的 glibc 并不提供!该功能在 FedoraUbuntu 下默认是开启的,因此为了避免 未知符号 ( missing symbol )错误,需要通过编译参数 -fnostack-protector 显式关闭。

注解

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

下一步

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

../_images/wechat-mp-qrcode.png小菜学编程

参考文献