如何将c/c ++应用程序移植到旧版Linux内核版本 [英] how to port c/c++ applications to legacy linux kernel versions

查看:75
本文介绍了如何将c/c ++应用程序移植到旧版Linux内核版本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,这只是一个有趣的练习,但是对于某些较旧的linux系统来说,编译程序不是太难吗?

我可以访问几个都运行linux的古老系统,也许很有趣的是看看它们在负载下的性能如何.举个例子,我们想使用 Eigen 做一些线性代数,这是一个不错的仅标头库.有机会在目标系统上进行编译吗?

  user @ ancient:〜$ uname -aLinux local 2.2.16#5 Sat Jul 8 20:36:25 MEST 2000 i586未知user @ ancient:〜$ gcc --versionegcs-2.91.66 

也许不是...所以让我们在当前系统上进行编译.以下是我的尝试,主要是失败的尝试.任何其他想法都非常欢迎.

  1. 使用 -m32 -march = i386

    进行编译

      user @ ancient:〜$ ./a.out动态链接器中的错误ld.so:dynamic-link.h:53:elf_get_dynamic_info:声明`!错误的动态代码"失败! 

  2. 使用 -m32 -march = i386 -static 进行编译:在所有最新的内核版本上运行,但如果它们版本稍旧且出现众所周知的错误消息,则将失败

      user @ ancient:〜$ ./a.out严重:内核太旧分段故障 

    这是一个 glibc 错误,它具有支持的最低内核版本,例如我系统上的2.6.4内核:

      $文件a.outa.out:ELF 32位LSB可执行文件,Intel 80386版本1(SYSV),静态链接,用于GNU/Linux 2.6.4,未剥离 

  3. 自己编译 glibc 并支持最早的内核.这篇文章对其进行了更详细的描述,但本质上是这样的

      wget ftp://ftp.gnu.org/gnu/glibc/glibc-2.14.tar.bz2tar -xjf glibc-2.14.tar.bz2光盘glibc-2.14mkdir构建;光盘制作../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"使-j 4高大 

    不确定-with-cpu -host 选项是否有任何作用,最重要的是强制使用编译器标志 -m32-march = i486 (用于32位版本)(不幸的是 -march = i386 一段时间后因错误而失败)和-enable-kernel = 2.0.0 使该库与较早的内核兼容.偶然地,在 configure 期间,我得到了警告

     警告:最低内核版本重置为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 的行,该行指示库之间的相互依赖关系,请参见解决方案

已找到错误消息的原因:

  user @ ancient $ ./prog设置线程本地存储时,set_thread_area失败分段故障 

这是因为 glibc 对该函数进行了系统调用,该函数仅从内核2.4.20开始可用.在某种程度上,它可以看作是 glibc 的错误,因为它至少需要内核2.4.20时错误地声称与内核2.0.10兼容.

详细信息:

 <代码> ./glibc-2.14/nptl/sysdeps/i386/tls.h[...]/*安装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吗?空值                                                      \:设置线程本地存储时,set_thread_area失败\ 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#定义__NR_set_thread_area 243 

,但没有任何早期的内核版本中.

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

使用较早版本的内核执行此操作的唯一机会是禁用 tls (线程本地存储),但glibc 2.14不可能做到这一点,尽管事实上它是作为提供的>配置选项.

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?

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?

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

    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

    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:

    $ 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

    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

    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.

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

    $ 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:

    $ 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.

    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.

  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.

  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 ...

  6. ???

解决方案

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.

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

$ 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.

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.

这篇关于如何将c/c ++应用程序移植到旧版Linux内核版本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆