【问题标题】:how to port c/c++ applications to legacy linux kernel versions如何将 c/c++ 应用程序移植到旧版 linux 内核版本
【发布时间】:2012-02-16 05:04:40
【问题描述】:

好的,这只是一个有趣的练习,但是为一些较旧的 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

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

  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!
    
  2. 使用-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
    
  3. 自己编译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.ocrtend.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.hglibc 错误消息。看不懂细节就放弃了。

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

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

  6. ???

【问题讨论】:

  • 编译?一个只有标题的模板库?
  • 编译一个使用这个模板库的测试程序。这只是一些由于 gcc 版本过时而无法在目标机器上编译的代码示例。
  • 啊,好吧。抱歉,刚才误会了。
  • 一般来说相关但不是重复的,我会说。还有更多相关问题,例如thisthis。这里的贡献是实际上重新编译了 glibc 以支持 2.0.10 的内核,但在 2.2.x 内核上运行时仍然收到错误 set_thread_area failed when setting up thread-local storage

标签: c++ linux gcc linux-kernel glibc


【解决方案1】:

已经找到错误信息的原因:

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 中这是不可能的,尽管它是作为 configure 选项提供的。

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 2012-07-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多