如何将c/c ++应用程序移植到旧版Linux内核版本

use*_*432 14 c++ linux gcc glibc linux-kernel

好吧,这只是一个有趣的练习,但是对于一些较旧的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
Run Code Online (Sandbox Code Playgroud)

也许不是......所以让我们在当前系统上编译它.以下是我的尝试,主要是失败的.更多的想法非常受欢迎.

  1. 编译 -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!
    
    Run Code Online (Sandbox Code Playgroud)
  2. 编译-m32 -march=i386 -static:运行在所有相当新的内核版本上,但如果它们稍微更旧并且具有众所周知的错误消息则会失败

    user@ancient:~ $ ./a.out
    FATAL: kernel too old
    Segmentation fault
    
    Run Code Online (Sandbox Code Playgroud)

    这是一个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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 编译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
    
    Run Code Online (Sandbox Code Playgroud)

    不确定--with-cpu--host选项是否做了什么,最重要的是强制使用编译器标志-m32 -march=i486进行32位构建(不幸的-march=i386是,在一段时间后出现错误)--enable-kernel=2.0.0并使库与旧内核兼容.在configure我收到警告的过程中

    WARNING: minimum kernel version reset to 2.0.10
    
    Run Code Online (Sandbox Code Playgroud)

    我想这仍然是可以接受的.有关使用不同内核更改的内容列表,请参阅./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
    
    Run Code Online (Sandbox Code Playgroud)

    由于我们正在进行静态编译,因此链接顺序很重要,可能需要一些试验和错误,但基本上我们会从gcc链接器的选项中学习:

    $ g++ -m32 -static -Wl,-v file.o
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,crtbeginT.o并且crtend.o我也不需要为我的程序链接,因此我将它们排除在外.输出还包括一行--start-group -lgcc -lgcc_eh -lc --end-group,表示库之间的相互依赖性,请参阅此文章.我刚刚-lcgcc命令行中提到了两次,这也解决了相互依赖.

    是的,努力工作得到了回报,现在我得到了

    $ file ./prog
    ./prog: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
    statically linked, for GNU/Linux 2.0.10, not stripped
    
    Run Code Online (Sandbox Code Playgroud)

    很棒我想,现在在旧系统上尝试:

    user@ancient:~ $ ./prog
    set_thread_area failed when setting up thread-local storage
    Segmentation fault
    
    Run Code Online (Sandbox Code Playgroud)

    这再次是glibc来自的错误消息./nptl/sysdeps/i386/tls.h.我无法理解细节并放弃.

  4. 编译新系统g++ -c -m32 -march=i386并链接旧系统.哇,这实际上适用于C和简单的C++程序(不使用C++对象),至少对于我测试过的少数几个.这并不太令人惊讶,因为我需要的libcprintf(也许是一些数学)接口没有改变但接口libstdc++现在非常不同.

  5. 使用旧的linux系统和gcc版本2.95设置虚拟盒.然后编译gcc版本4.xx ...对不起,但现在太懒了...

  6. ???

use*_*432 9

找到了错误消息的原因:

user@ancient $ ./prog
set_thread_area failed when setting up thread-local storage
Segmentation fault
Run Code Online (Sandbox Code Playgroud)

这是因为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"; })
[...]
Run Code Online (Sandbox Code Playgroud)

这里的主要内容是,它调用汇编函数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
Run Code Online (Sandbox Code Playgroud)

但不是在任何早期的内核版本中.

所以好消息是"3.编译glibc --enable-kernel=2.0.0"可能会产生可运行在所有Linux内核上的可执行文件> = 2.4.20.

使这项工作与旧内核的唯一机会是禁用tls(线程本地存储),但是,这是不可能用glibc 2.14,尽管它提供的事实configure选项.


Nic*_*tin 4

无法在原始系统上编译它的原因可能与内核版本无关(它可以,但 2.2 通常还不够老,不足以成为大多数代码的绊脚石)。问题在于工具链很古老(至少是编译器)。但是,没有什么可以阻止您使用已安装的 G++ 构建更新版本egcsglibc完成此操作后,您可能还会遇到问题,但您至少应该做到这一点。

你应该做的事情看起来像这样:

  • 构建最新的 GCCegcs
  • 使用gcc您刚刚构建的重建最新的 GCC
  • ld使用新编译器构建最新的 binutils

现在,您拥有一个构建良好的现代编译器和(大部分)工具链,可用于构建示例应用程序。如果运气不好,您可能还需要构建更新版本的glibc,但这您的问题 - 工具链 - 而不是内核。