如何将 C/C++ 应用程序移植到旧版 Linux 内核版本
how to port c/c++ applications to legacy linux kernel versions
好吧,这只是一个有趣的练习,但是对于一些较旧的Linux系统编译程序不会太难,或者可以吗?
我可以访问几个运行 Linux 的古老系统,也许看看它们在负载下的表现会很有趣。例如,我们想使用 Eigen 做一些线性代数,这是一个很好的仅标题库。有机会在目标系统上编译它吗?
user@ancient:~ $ uname -a
Linux local 2.2.16 #5 Sat Jul 8 20:36:25 MEST 2000 i586 unknown
user@ancient:~ $ gcc --version
egcs-2.91.66
也许不是...因此,让我们在当前系统上编译它。以下是我的尝试,主要是失败的尝试。任何更多的想法非常欢迎。
使用
-m32 -march=i386
编译user@ancient:~ $ ./a.out BUG IN DYNAMIC LINKER ld.so: dynamic-link.h: 53: elf_get_dynamic_info: Assertion `! "bad dynamic tag"' failed!
使用
-m32 -march=i386 -static
编译:在所有相当新的内核版本上运行,但如果它们稍旧并显示众所周知的错误消息,则失败user@ancient:~ $ ./a.out FATAL: kernel too old Segmentation fault
这是一个
glibc
错误,它支持的最低内核版本,例如我的系统上的内核 2.6.4:$ file a.out a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.4, not stripped
glibc
自己编译,支持最旧的内核。这篇文章更详细地描述了它,但本质上是这样的wget ftp://ftp.gnu.org/gnu/glibc/glibc-2.14.tar.bz2 tar -xjf glibc-2.14.tar.bz2 cd glibc-2.14 mkdir build; cd build ../configure --prefix=/usr/local/glibc_32 --enable-kernel=2.0.0 --with-cpu=i486 --host=i486-linux-gnu CC="gcc -m32 -march=i486" CXX="g++ -m32 -march=i486" make -j 4 make intall
不确定
--with-cpu
和--host
选项是否有什么作用,最重要的是强制使用 32 位构建-m32 -march=i486
编译器标志(不幸的是,-march=i386
一段时间后会因错误而摆脱困境(和--enable-kernel=2.0.0
使库与旧内核兼容。偶然地,在configure
期间我收到了警告WARNING: minimum kernel version reset to 2.0.10
我想这仍然是可以接受的。有关随不同内核而变化的内容的列表,请参阅
./sysdeps/unix/sysv/linux/kernel-features.h
。好的,让我们链接到新编译的
glibc
库,有点混乱,但在这里:$ export LIBC_PATH=/usr/local/glibc_32 $ export LIBC_FLAGS=-nostdlib -L${LIBC_PATH} ${LIBC_PATH}/crt1.o ${LIBC_PATH}/crti.o -lm -lc -lgcc -lgcc_eh -lstdc++ -lc ${LIBC_PATH}/crtn.o $ g++ -m32 -static prog.o ${LIBC_FLAGS} -o prog
由于我们正在进行静态编译,因此链接顺序很重要,并且可能需要一些试验和错误,但基本上我们从
gcc
给链接器的选项中学习:$ g++ -m32 -static -Wl,-v file.o
请注意,
crtbeginT.o
和crtend.o
也是链接的,我的程序不需要,所以我把它们排除在外。输出还包括类似--start-group -lgcc -lgcc_eh -lc --end-group
的行,指示库之间的相互依赖性,请参阅这篇文章。我刚刚在gcc
命令行中提到了两次-lc
,这也解决了相互依赖问题。是的,辛勤工作得到了回报,现在我得到了
$ file ./prog ./prog: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.0.10, not stripped
我想太棒了,现在在旧系统上尝试一下:
user@ancient:~ $ ./prog set_thread_area failed when setting up thread-local storage Segmentation fault
这又是来自
./nptl/sysdeps/i386/tls.h
的glibc
错误消息。我不明白细节,放弃了。在新系统上编译
g++ -c -m32 -march=i386
并在旧系统上链接。哇,这实际上适用于 C 和简单的 C++ 程序(不使用C++对象(,至少对于我测试过的少数几个。这并不奇怪,因为我只需要libc
printf
(也许还有一些数学(,其中的界面没有改变,但libstdc++
的界面现在非常不同。使用旧的 Linux 系统和 gcc 版本 2.95 设置虚拟盒子。然后编译 gcc 版本 4.x.x ...对不起,但现在懒得了...
???
已找到错误消息的原因:
user@ancient $ ./prog
set_thread_area failed when setting up thread-local storage
Segmentation fault
这是因为glibc
对仅从内核 2.4.20 开始可用的函数进行系统调用。在某种程度上,它可以被视为glibc
的错误,因为它错误地声称与内核 2.0.10 兼容,而它至少需要内核 2.4.20。
详情:
./glibc-2.14/nptl/sysdeps/i386/tls.h
[...]
/* Install the TLS. */
asm volatile (TLS_LOAD_EBX
"int $0x80nt"
TLS_LOAD_EBX
: "=a" (_result), "=m" (_segdescr.desc.entry_number)
: "0" (__NR_set_thread_area),
TLS_EBX_ARG (&_segdescr.desc), "m" (_segdescr.desc));
[...]
_result == 0 ? NULL
: "set_thread_area failed when setting up thread-local storagen"; })
[...]
这里最主要的是,它调用汇编函数int 0x80
这是对 linux 内核的系统调用,它根据 eax
的值决定做什么,设置为 __NR_set_thread_area
在这种情况下,定义在
$ grep __NR_set_thread_area /usr/src/linux-2.4.20/include/asm-i386/unistd.h
#define __NR_set_thread_area 243
但不在任何早期的内核版本中。
所以好消息是"3.使用 --enable-kernel=2.0.0
编译 glibc 可能会产生在所有 Linux 内核上运行的可执行文件>= 2.4.20。
使用旧内核进行此操作的唯一机会是禁用tls
(线程本地存储(,但这在 glibc 2.14 中是不可能的,尽管它是作为configure
选项提供的。
原始系统上编译它的原因可能与内核版本无关(可以,但 2.2 通常还不够旧,不会成为大多数代码的绊脚石(。 问题是工具链是古老的(至少是编译器(。 但是,没有什么能阻止您使用已安装的 egcs
生成较新版本的 G++。 一旦你这样做了,你也可能会遇到glibc
的问题,但你至少应该走那么远。
您应该执行的操作将如下所示:
- 使用
egcs
构建最新的 GCC - 使用刚刚构建的
gcc
重建最新的 GCC - 使用新编译器构建最新的二进制文件并
ld
现在,您有一个构建良好的现代编译器和(大部分(工具链,可用于构建示例应用程序。 如果运气不好,您可能还需要构建一个较新版本的glibc
,但这是您的问题 - 工具链 - 而不是内核。
- 在Linux Mint上使用多个版本的libboost
- 一个32位版本的应用程序,建立在CentOS 6 x64上,当在较新的Linux上启动时,在"dl_itera
- 在Mac OS X Mountain Lion(或更高版本)和Linux上使用X11/Xlib.h
- 如何链接到与 Linux 中不同版本的 boost 链接的共享库
- 如何在QT Creator开源版本中为嵌入式Linux交叉编译qt
- Linux 中 gcc 4.3 版本的编译问题
- 我如何在 Linux 中获取程序版本
- /usr/lib/x86_64-linux-gnu/libstdc++.so.6:未找到版本 CXXABI_1.3.8'
- 在旧版本的linux上运行我的代码
- 在我的Linux中调用了哪个版本的close(),来自posix lib或内核
- 在 VS 和 Linux 版本中包含相同的 #define
- 基于内核版本 X.XX 构建的 C++ Linux 应用是否会在早期版本上运行
- 你如何找到你的 linux 机器上安装了什么版本的 libstdc++ 库
- 无法在 Linux 版本中更改文件类型
- 如何使用linux系统调用编写一个c++程序,该调用提供有关可用内存、已用内存和内核版本的输出
- 如何在c++中获得包版本?(linux)
- 如何在 Linux 中检查正在运行的进程版本读取"ps aux"输出
- 如何将 C/C++ 应用程序移植到旧版 Linux 内核版本
- 在基于HPC的远程Linux上切换gcc版本(没有root权限)
- 如果我用较新版本的gcc编译二进制文件,我如何找到需要在客户机linux机器上安装的库?