Подтвердить что ты не робот

Как переносить приложения c/С++ в устаревшие версии ядра Linux

Хорошо, это всего лишь забавное упражнение, но это не может быть слишком сложная компиляция программ для некоторых старых Linux-систем, или это возможно?

У меня есть доступ к нескольким древним системам, в которых работает весь Linux, и, возможно, было бы интересно посмотреть, как они работают под нагрузкой. Например, мы хотим сделать некоторую линейную алгебру, используя Eigen, которая является хорошей библиотекой только для заголовка. Есть ли возможность скомпилировать его в целевой системе?

[email protected]:~ $ uname -a
Linux local 2.2.16 #5 Sat Jul 8 20:36:25 MEST 2000 i586 unknown
[email protected]:~ $ gcc --version
egcs-2.91.66

Возможно, нет... Так что давайте скомпилируем его в текущей системе. Ниже приведены мои попытки, в основном неудачные. Любые идеи очень приветствуются.

  • Скомпилировать с помощью -m32 -march=i386

    [email protected]:~ $ ./a.out
    BUG IN DYNAMIC LINKER ld.so: dynamic-link.h: 53: elf_get_dynamic_info: Assertion `! "bad dynamic tag"' failed!
    
  • Скомпилировать с помощью -m32 -march=i386 -static: выполняется на всех довольно недавних версиях ядра, но не удается, если они немного старше с известным сообщением об ошибке

    [email protected]:~ $ ./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
    
  • Скомпилируйте 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
    

    Блестящий я подумал, теперь попробуй его на старой системе:

    [email protected]:~ $ ./prog
    set_thread_area failed when setting up thread-local storage
    Segmentation fault
    

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

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

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

  • ???

4b9b3361

Ответ 1

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

[email protected] $ ./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_area в этом случае и определяется в

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

Ответ 2

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

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

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

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