как перенести приложения c / c ++ на устаревшие версии ядра Linux - PullRequest
14 голосов
/ 22 января 2012

Хорошо, это просто забавное упражнение, но это не может быть слишком сложная компиляция программ для некоторых старых систем 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 себя с поддержкой самого старого возможного ядра. Этот пост описывает его более подробно, но по сути это выглядит так

    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
    

    Поскольку мы выполняем статическую компиляцию, порядок ссылок важен и может потребовать некоторых проб и ошибок, но в основном мы узнаем, какие опции gcc предоставляет компоновщику:

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

    Заметьте, crtbeginT.o и crtend.o также связаны, которые мне не нужны для моих программ, поэтому я их пропустил. Вывод также включает строку типа --start-group -lgcc -lgcc_eh -lc --end-group, которая указывает на взаимозависимость между библиотеками, см. этот пост . Я только что упомянул -lc дважды в командной строке gcc, которая также решает взаимозависимость.

    Правильно, тяжелая работа окупилась, и теперь я получаю

    $ 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
    

    Это опять-таки сообщение об ошибке glibc от ./nptl/sysdeps/i386/tls.h. Я не понимаю деталей и сдаюсь.

  4. Компилировать на новой системе g++ -c -m32 -march=i386 и ссылаться на старой. Вау, это на самом деле работает для C и простых программ на C ++ (не использующих объекты C ++), по крайней мере, для немногих, которые я тестировал. Это не слишком удивительно, так как все, что мне нужно от libc, это printf (и, возможно, некоторые математические вычисления), интерфейс которого не изменился, но интерфейс к libstdc++ теперь сильно отличается.

  5. Установить виртуальный ящик со старой системой Linux и gcc версии 2.95. Затем скомпилируйте gcc версии 4.x.x ... извините, но слишком лениво для этого прямо сейчас ...

  6. ???

Ответы [ 2 ]

9 голосов
/ 23 января 2012

Нашли причину сообщения об ошибке:

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

Это потому, что glibc выполняет системный вызов функции, которая доступна только с ядра 2.4.20.В некотором смысле это можно считать ошибкой glibc, поскольку она ошибочно утверждает, что она совместима с ядром 2.0.10, когда требуется хотя бы ядро ​​2.4.20.

Подробности:

./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_areacase и определен в

$ grep __NR_set_thread_area /usr/src/linux-2.4.20/include/asm-i386/unistd.h
#define __NR_set_thread_area    243

, но не в каких-либо более ранних версиях ядра.

Итак, хорошая новость заключается в том, что пункт "3. Компиляция glibc с --enable-kernel=2.0.0", вероятно, приведет к выполнению исполняемых файлов, которыеработать на всех ядрах Linux> = 2.4.20.

Единственный шанс заставить эту работу работать со старыми ядрами - это отключить tls (локальное хранилище потока), но это невозможно с glibc 2.14, несмотря нафакт, что это предлагается как configure опция.

3 голосов
/ 22 января 2012

Причина, по которой вы не можете скомпилировать ее в исходной системе, скорее всего, не имеет ничего общего с версией ядра (возможно, но 2.2, как правило, недостаточно для того, чтобы это было камнем преткновения для большей части кода). Проблема в том, что toolchain является древним (по крайней мере, компилятором). Однако ничто не мешает вам создать более новую версию G ++ с установленным egcs. Вы также можете столкнуться с проблемами с glibc, как только вы это сделаете, но вы должны, по крайней мере, получить это далеко.

То, что вы должны сделать, будет выглядеть примерно так:

  • Сборка последней версии GCC с egcs
  • Восстановите последний GCC с gcc, который вы только что построили
  • Сборка последних версий binutils и ld с вашим новым компилятором

Теперь у вас есть хорошо построенный современный компилятор и (большая часть) набор инструментов, с помощью которого можно создать пример приложения. Если удача не на вашей стороне, вам также может понадобиться создать более новую версию glibc, но это ваша проблема - набор инструментов, а не ядро.

...