好的,这只是一个有趣的练习,但是为一些较旧的 linux 系统编译程序不会太难,或者可以吗?
我可以访问几个都运行 linux 的古老系统,也许看看它们在负载下的表现会很有趣。举个例子,我们想用 Eigen 做一些线性代数。这是一个不错的仅 header 库。有机会在目标系统上编译它吗?
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
,尽可能支持最旧的内核。 This post更详细地描述它,但本质上是这样的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
选项是否有任何作用,最重要的是强制使用编译器标志-m32 - March=i486
用于 32 位构建(不幸的是-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
由于我们正在进行静态编译,link order很重要,可能需要一些试验和错误,但基本上我们从
gcc
提供给链接器的选项中学习:$ g++ -m32 -static -Wl,-v file.o
注意,
crtbeginT.o
和crtend.o
也与我的程序不需要的链接,因此我将它们排除在外。输出还包括类似--start-group -lgcc -lgcc_eh -lc --end-group
的行,表示库之间的相互依赖关系,参见 this post .我刚刚在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
(也许还有一些数学),其中的接口(interface)没有改变,但是libstdc++< 的接口(interface)
现在大不一样了。用旧的 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.4.20 时,它错误地声称与内核 2.0.10 兼容。
详情:
./glibc-2.14/nptl/sysdeps/i386/tls.h
[...]
/* Install the TLS. */ \
asm volatile (TLS_LOAD_EBX \
"int $0x80\n\t" \
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 storage\n"; })
[...]
这里主要的是,它调用汇编函数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 中是不可能的,尽管它是作为 配置
选项。
关于c++ - 如何将 c/c++ 应用程序移植到旧版 linux 内核版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8961622/