如何将c/c++应用程序移植到遗留的linux内核版本

时间:2022-07-02 15:06:23

Ok, this is just a bit of a fun exercise, but it can't be too hard compiling programmes for some older linux systems, or can it?

好的,这只是一个有趣的练习,但是为一些旧的linux系统编译程序不会太难,对吗?

I have access to a couple of ancient systems all running linux and maybe it'd be interesting to see how they perform under load. Say as an example we want to do some linear algebra using Eigen which is a nice header-only library. Any chance to compile it on the target system?

我可以访问一些运行linux的古老系统,看看它们在负载下的表现会很有趣。举个例子,我们想用特征来做一些线性代数这是一个很好的单头库。有机会在目标系统上编译它吗?

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

Maybe not... So let's compile it on a current system. Below are my attempts, mainly failed ones. Any more ideas very welcome.

也许不是…让我们在当前系统上编译它。下面是我的尝试,主要是失败的尝试。欢迎有更多的想法。

  1. Compile with -m32 -march=i386

    编译和两个同伴m32 3月= i386

    user@ancient:~ $ ./a.out
    BUG IN DYNAMIC LINKER ld.so: dynamic-link.h: 53: elf_get_dynamic_info: Assertion `! "bad dynamic tag"' failed!
    
  2. Compile with -m32 -march=i386 -static: Runs on all fairly recent kernel versions but fails if they are slightly older with the well known error message

    用-m32 -march=i386 -static编译:运行在所有最近的内核版本上,但是如果它们稍微旧了一点,并带有众所周知的错误消息,就会失败

    user@ancient:~ $ ./a.out
    FATAL: kernel too old
    Segmentation fault
    

    This is a glibc error which has a minimum kernel version it supports, e.g. kernel 2.6.4 on my system:

    这是一个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
    
  3. Compile glibc myself with support for the oldest kernel possible. This post describes it in more detail but essentially it goes like this

    我自己编译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
    

    Not sure if the --with-cpu and --host options do anything, most important is to force the use of compiler flags -m32 -march=i486 for 32-bit builds (unfortunately -march=i386 bails out with errors after a while) and --enable-kernel=2.0.0 to make the library compatible with older kernels. Incidentially, during configure I got the warning

    不确定-with-cpu和-host选项是否有作用,最重要的是强制使用32位构建的编译器标记-m32 -march=i486(不幸的是-march=i386稍后出现错误)和-enable-kernel=2.0.0,以使库与旧内核兼容。在配置过程中,我得到了警告

    WARNING: minimum kernel version reset to 2.0.10
    

    which is still acceptable, I suppose. For a list of things which change with different kernels see ./sysdeps/unix/sysv/linux/kernel-features.h.

    我想这还是可以接受的。对于不同内核的更改列表,请参阅。/sysdeps/unix/sysv/linux/kernel-features.h。

    Ok, so let's link against the newly compiled glibc library, slightly messy but here it goes:

    好了,让我们链接到新编译的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
    

    Since we're doing a static compile the link order is important and may well require some trial and error, but basically we learn from what options gcc gives to the linker:

    由于我们正在进行静态编译,所以链接顺序很重要,可能需要一些尝试和错误,但基本上我们可以从gcc提供给链接器的选项中学到:

    $ g++ -m32 -static -Wl,-v file.o
    

    Note, crtbeginT.o and crtend.o are also linked against which I didn't need for my programmes so I left them out. The output also includes a line like --start-group -lgcc -lgcc_eh -lc --end-group which indicates inter-dependence between the libraries, see this post. I just mentioned -lc twice in the gcc command line which also solves inter-dependence.

    注意,crtbeginT。o和crtend。o也与我不需要的课程相关,所以我把它们排除在外。输出还包括一个类似于start-group -lgcc -lgcc_eh -lc的行,它表示库之间的相互依赖关系,参见本文。我刚刚在gcc命令行中两次提到-lc,它也解决了相互依赖。

    Right, the hard work has paid off and now I get

    对,辛苦的工作得到了回报,现在我得到了

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

    Brilliant I thought, now try it on the old system:

    太棒了,我想,现在在旧系统上试试:

    user@ancient:~ $ ./prog
    set_thread_area failed when setting up thread-local storage
    Segmentation fault
    

    This, again, is a glibc error message from ./nptl/sysdeps/i386/tls.h. I fail to understand the details and give up.

    这又是来自./nptl/sysdeps/i386/tls.h的glibc错误消息。我不明白细节,只好放弃。

  4. Compile on the new system g++ -c -m32 -march=i386 and link on the old. Wow, that actually works for C and simple C++ programmes (not using C++ objects), at least for the few I've tested. This is not too surprising as all I need from libc is printf (and maybe some maths) of which the interface hasn't changed but the interface to libstdc++ is very different now.

    编译新系统g+ -c -m32 -march=i386,链接旧系统。哇,这实际上适用于C和简单的c++程序(不使用c++对象),至少对于我测试过的少数几个来说是这样的。这并不奇怪,因为我从libc需要的只是printf(可能还有一些数学),其中的接口没有更改,但是libstdc++ +的接口现在非常不同。

  5. Setup a virtual box with an old linux system and gcc version 2.95. Then compile gcc version 4.x.x ... sorry, but too lazy for that right now ...

    使用旧的linux系统和gcc 2.95版本设置一个虚拟框。然后编译gcc版本4.x。x……对不起,现在太懒了……

  6. ???

    ? ? ?

2 个解决方案

#1


8  

Have found the reason for the error message:

发现错误信息的原因:

user@ancient $ ./prog
set_thread_area failed when setting up thread-local storage
Segmentation fault

It's because glibc makes a system call to a function which is only available since kernel 2.4.20. In a way it can be seen as a bug of glibc as it wrongly claims to be compatible with kernel 2.0.10 when it requires at least kernel 2.4.20.

这是因为glibc使系统调用的函数只在内核2.4.20之后才可用。在某种程度上,它可以被视为glibc的一个缺陷,因为它错误地声称在至少需要2.4.20内核时与内核2.0.10兼容。

The details:

细节:

./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"; })
[...]

The main thing here is, it calls the assembly function int 0x80 which is a system call to the linux kernel which decides what to do based on the value of eax, which is set to __NR_set_thread_area in this case and is defined in

这里的主要内容是,它调用汇编函数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

but not in any earlier kernel versions.

但在任何早期的内核版本中都没有。

So the good news is that point "3. Compiling glibc with --enable-kernel=2.0.0" will probably produce executables which run on all linux kernels >= 2.4.20.

好消息是这一点是3。编译glibc和-enable-kernel=2.0.0“可能会产生运行在所有linux内核上的可执行文件(>= 2.4.20)。

The only chance to make this work with older kernels would be to disable tls (thread-local storage) but which is not possible with glibc 2.14, despite the fact it is offered as a configure option.

使用旧内核进行此工作的惟一机会是禁用tls(线程本地存储),但是使用glibc 2.14是不可能的,尽管它是作为配置选项提供的。

#2


3  

The reason you can't compile it on the original system likely has nothing to do with kernel version (it could, but 2.2 isn't generally old enough for that to be a stumbling block for most code). The problem is that the toolchain is ancient (at the very least, the compiler). However, nothing stops you from building a newer version of G++ with the egcs that is installed. You may also encounter problems with glibc once you've done that, but you should at least get that far.

您不能在原始系统上编译它的原因可能与内核版本无关(它可以,但是2.2通常还不够成熟,不足以成为大多数代码的绊脚石)。问题是工具链是古老的(至少是编译器)。但是,没有什么可以阻止您使用已安装的egcs构建一个新的g++版本。一旦您这样做了,您可能还会遇到glibc的问题,但是您至少应该走到这一步。

What you should do will look something like this:

你应该做的是:

  • Build latest GCC with egcs
  • 使用egcs构建最新的GCC
  • Rebuild latest GCC with the gcc you just built
  • 使用刚刚构建的GCC重新构建最新的GCC
  • Build latest binutils and ld with your new compiler
  • 用新的编译器构建最新的binutils和ld。

Now you have a well-built modern compiler and (most of a) toolchain with which to build your sample application. If luck is not on your side you may also need to build a newer version of glibc, but this is your problem - the toolchain - not the kernel.

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

#1


8  

Have found the reason for the error message:

发现错误信息的原因:

user@ancient $ ./prog
set_thread_area failed when setting up thread-local storage
Segmentation fault

It's because glibc makes a system call to a function which is only available since kernel 2.4.20. In a way it can be seen as a bug of glibc as it wrongly claims to be compatible with kernel 2.0.10 when it requires at least kernel 2.4.20.

这是因为glibc使系统调用的函数只在内核2.4.20之后才可用。在某种程度上,它可以被视为glibc的一个缺陷,因为它错误地声称在至少需要2.4.20内核时与内核2.0.10兼容。

The details:

细节:

./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"; })
[...]

The main thing here is, it calls the assembly function int 0x80 which is a system call to the linux kernel which decides what to do based on the value of eax, which is set to __NR_set_thread_area in this case and is defined in

这里的主要内容是,它调用汇编函数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

but not in any earlier kernel versions.

但在任何早期的内核版本中都没有。

So the good news is that point "3. Compiling glibc with --enable-kernel=2.0.0" will probably produce executables which run on all linux kernels >= 2.4.20.

好消息是这一点是3。编译glibc和-enable-kernel=2.0.0“可能会产生运行在所有linux内核上的可执行文件(>= 2.4.20)。

The only chance to make this work with older kernels would be to disable tls (thread-local storage) but which is not possible with glibc 2.14, despite the fact it is offered as a configure option.

使用旧内核进行此工作的惟一机会是禁用tls(线程本地存储),但是使用glibc 2.14是不可能的,尽管它是作为配置选项提供的。

#2


3  

The reason you can't compile it on the original system likely has nothing to do with kernel version (it could, but 2.2 isn't generally old enough for that to be a stumbling block for most code). The problem is that the toolchain is ancient (at the very least, the compiler). However, nothing stops you from building a newer version of G++ with the egcs that is installed. You may also encounter problems with glibc once you've done that, but you should at least get that far.

您不能在原始系统上编译它的原因可能与内核版本无关(它可以,但是2.2通常还不够成熟,不足以成为大多数代码的绊脚石)。问题是工具链是古老的(至少是编译器)。但是,没有什么可以阻止您使用已安装的egcs构建一个新的g++版本。一旦您这样做了,您可能还会遇到glibc的问题,但是您至少应该走到这一步。

What you should do will look something like this:

你应该做的是:

  • Build latest GCC with egcs
  • 使用egcs构建最新的GCC
  • Rebuild latest GCC with the gcc you just built
  • 使用刚刚构建的GCC重新构建最新的GCC
  • Build latest binutils and ld with your new compiler
  • 用新的编译器构建最新的binutils和ld。

Now you have a well-built modern compiler and (most of a) toolchain with which to build your sample application. If luck is not on your side you may also need to build a newer version of glibc, but this is your problem - the toolchain - not the kernel.

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