Установка GCC для кросс компиляции

В данной статье рассказывается как установить GCC для кросс-платформенной компиляции. Прежде всего эта статья имеет скорее базовое, чем узко-специализированное направление.

В вопросах кросс компиляции не имеет значения ни гостевая система, ни целевая. Все описанное здесь можно проделать и для своей любимой машины под GNU/Linux, Windows, MacOS. Границ нет! Для лучшего усвоения материала написанного здесь, рекомендую предварительно прочитать работу Martin C. Brown, Build a GCC-based cross compiler for Linux. Для сборки нам обязательно потребуются системные заголовочные файлы и скомпилированные библиотеки целевой системы. Сборка будет производится под 32-битную архитектуру. Если возникнет необходимость собрать для обоих архитектур у вас не должно вызвать сложностей после прочтения данного HowTo.

Что у нас есть и что мы хотим:

 host: i386-freebsd-freebsd6.2
 target: sparc-sun-solaris2.8
 binutils-2.20
 GCC-3.4.6

Первым делом нам необходимо собрать binutils, в наборе которых нам понадобится компановщик ld, ассемблер as и ряд других утилит для сборки GCC, способных выдавать байт-код для платформы SPARC.

Приготовления

Желательно иметь пересобранный GNU GCC (3.4.6) из портов. А также необходимо установить binutils, GNU make (gmake), mprf (опционально), gmp (опционально). Установка их не должна вызвать проблем.

Переходим в удобную для нас директорию для сборки из исходников. В моей случае это будет /tmp/cross. Все наши архивы исходников будут находиться в /tmp/cross/src. Все исходники для сборки расположим в /tmp/cross/build. Далее, для сборки binutils у нас будет отдельная директория /tmp/cross/build/build-binutils. Файлы от целовой системы мы поместим в /tmp/cross/sunlh, об этом этапе будет подробно рассказано позже. В конечном виде это будет выглядеть так:

$ mkdir /tmp/cross
$ cd /tmp/cross && mkdir src build sunlh
$ cd build && mkdir build-binutils build-gcc

Теперь надо скопировать и распаковать файлы для binutils и gcc. Мы сделаем следующим образом:

$ cd /tmp/cross/src
$ fetch http://ftp.gnu.org/gnu/binutils/binutils-2.20.tar.bz2
$ fetch http://ftp.gnu.org/gnu/gcc/gcc-3.4.6/gcc-3.4.6.tar.bz2
$ tar xjvf binutils-2.20.tar.bz2
$ tar xjvf gcc-3.4.6.tar.bz2
$ mv binutils-2.20 ../build
$ cp -r gcc-3.4.6 ../build

В двух словах объясню почему мы во-первых, собираем все в отдельных директориях вроде build-binutils, во-вторых, зачем еще копировать распакованный GCC. Процесс сборки кросс компилированных программ процесс требующей терпения, поэтому придется часто чистить директорию от генерируемых конфигов. В моем случае это будет происходить примерно так:

$ cd /tmp/cross/build/build-binutils
$ rm -r ./*

Ответ на второй вопрос не так очевиден. Дело в том, что при конфигурировании GCC с дополнительными библиотеками и заголовочными файлами, последние могут скопироваться в директорию сборки или еще куда-то.

Все эти два пункта - подстраховка и удобство. Вы можете их пропустить, если пожелаете.

Чтобы не говорили на других сайтах о сборке GCC, описывающих процесс без заголовочных файлов и библиотек целевой системы, это не возможно! Не выполнив этот пункт, вы не сможете собрать GCC для кросс компиляции. Далее я привожу список директорий, которые вы должны скопировать из целевой системы, Sun Solaris 8:

/platform, /usr/platform - Заголовочные файлы для всех типов процессоров SPARC.
/usr/include - Системные заголовочные файлы.
/usr/lib - Бинарные библиотечные файлы.

Выставляем переменные окружения

С переменными окружения имеется ряд особенностей в контексте сборки GCC. Остановимся на следующих договоренностяx:

$ export cross_target="sparc-sun-solaris2.8"
$ export cross_dir="/usr/local/suncc-2.8"
$ export cross_prefix="sparc-sun-solaris2.8-"

cross_target - в пояснениях не требуется.
cross_dir - директория куда будет установлены бинарники, библиотеки кросс платформенных утилит и GCC в их числе.
cross_prefix - Это фишка для сборки GCC. В противном случае придется руками указывать необходимые утилиты из binutils.

Сборка binutils

Этот этап проходит без особых проблем, нам потребуется лишь установленные утилиты GNU, о которых говорилось ранее. Никаких дополнительных файлов не потребуется. Поехали:

$ export CC=gcc34
$ export LDFLAGS=" -L/usr/local/lib"
$ export CFLAGS=" -I/usr/local/include"
$ cd /tmp/cross/build/build-binutils
$ /tmp/cross/build/binutils-2.20/configure --disable-nls --with-system-zlib --target=$cross_target --disable-werror --prefix=$cross_dir --program-prefix=$cross_prefix --with-mpfr=/usr/local --with-gmp=/usr/local
$ gmake all
$ su
# gmake install

Если вы не собрали GCC из портов, то должно быть так:

$ export CC=gcc
$ export LDFLAGS=" -L/usr/lib"
$ export CFLAGS=" -I/usr/include"

Однако я не ручаюсь, что все получится.

Сборка GCC

Эта самая сложная и захватывающая часть, т.к. над сборкой “монстра” GCC будут работать вместе два компоновщика, два ассемблера и ряд других дублированных утилит из комплектов родных binutils и свежособранных для кросс компиляции. Вместе они любят ругаться, но мы их усмирим:

$ export PATH="${cross_dir}/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/X11R6/bin"
$ export CC=gcc34
$ export CXX=g++34
$ export CFLAGS="-I/usr/local/include -I/tmp/cross/sunlh/usr/platform"
$ export CXXFLAGS="-I/usr/local/include -I/tmp/cross/sunlh/usr/platform"
$ export LDFLAGS="-L/usr/local/lib -L${cross_dir}/lib -L/tmp/cross/sunlh/libs -L/tmp/cross/sunlh/usr/lib"

Я не пробовал собирать GCC без GCC из портов. Если вы захотите пойти этим путем. Измените соответствующим образом CC, CXX, CFLAGS и т.д. на те, что были указаны по аналогии для сборки binutils.

$ cd /tmp/cross/build/build-gcc
$ /tmp/cross/build/gcc-3.4.6/configure --disable-nls --target=$cross_target --prefix=$cross_dir --program-prefix=$cross_prefix --with-headers=/tmp/cross/sunlh/usr/include --with-libs=/tmp/cross/sunlh/usr/lib --with-gnu-ld --with-gnu-as --with-gnu-nm --disable-multilib

Во-первых, обращаем внимание, что мы указали путь к системным заголовочным и библиотечным файлам целевой системы, они скопируются в $cross_dir/sys-include. Опция –disable-multilib говорит, что мы собираем все под ту же архитектуру, что имеется на гостевой системе. В моем случае она 32-битная. По желанию вы можете указать ключ –enable-languages для сборки только того, что вам необходимо.

Подбираем конфигурацию

В набор поставки GCC входит не только компиляторы для C, C+, Java, но и библиотека libstdc++, а также ряд других полезных утилит. Выбор за вами, за справками обращаться в руководство по установке GCC и справку по установке утилит GCC.

Для сборки всего что связано с GCC, выполняем следующее:

$ gmake
$ su
# gmake install

Для сборки только набора компиляторов:

$ gmake all-gcc
$ su
# gmake install-gcc

Последний вариант самый легкий в вопросах сборки. Если вам не удается сразу собрать весь GCC, попробуйте собрать только компиляторы.

Основные ошибки

Здесь я расскажу о тех случаях, когда сборка не удается. А именно наиболее частых проблемах и их решениях.

Файлы от разных систем: сould not read symbols: File in wrong format

Пример:

/usr/bin/ld: dictHash.o: Relocations in generic ELF (EM: 2)
dictHash.o: could not read symbols: File in wrong format
collect2: ld returned 1 exit status

Причины:
Неверно указаны пути в CFLAGS, CXXFLAGS, LDFLAGS.

Решение:
Выставить корректные пути для CFLAGS, CXXFLAGS, LDFLAGS, а также перепроверить пути в опциях –with-headers, –with-libs.

Потерянная библиотека: searching for -llibpthread.so

Пример:

/usr/bin/ld: skipping incompatible /usr/lib/libnetsnmp.so when searching for -lnetsnmp
/usr/bin/ld: skipping incompatible /usr/lib/libnetsnmp.a when searching for -lnetsnmp

Причина:
Компоновщик не может найти необходимую бинарную версию библиотеки.

Решение:
Находим нужную библиотеку (обычно она присутствует, но с именем включающим версию, например, libpthread.a.1) и делаем символическую ссылку:

$ find /tmp/cross/sunlh/usr/lib -type f -name 'libpthread*'
$ cd /path/to/libpthread/
$ ln -s libpthread.a.1 libpthread.a

Чудесный файл values-Xa.o

Пример:

 /home/nick/compile/gcc-3.1/gcc/xgcc -B/home/nick/compile/gcc-3.1/gcc/
 -B/usr/local/sparc-sun-solaris2.9/bin/
 -B/usr/local/sparc-sun-solaris2.9/lib/ -isystem
 /usr/local/sparc-sun-solaris2.9/include -O2 -DIN_GCC -W -Wall
 -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -isystem
 ./include -fPIC -g -DHAVE_GTHR_DEFAULT -DIN_LIBGCC2
 -D__GCC_FLOAT_NOT_NEEDED -shared -nodefaultlibs
 -Wl,--soname=libgcc_s.so.1
 -Wl,--version-script=libgcc/sparcv9/libgcc.map -o
 sparcv9/libgcc_s.so.1 -m64 libgcc/sparcv9/_muldi3.o
 ^^^^^^ ^^^ ^^^^^^^

Причины:
Одновременная сборка для разных архитектур (32-битную и 64-битную).

Решение:
Указать опцию при конфигурировании –disable-multilib.

Заключение

Для меня было огромным счастьем собрать “Hello SPARC!”, написанный на Си свежоиспеченным GCC. Выполнить file для бинарника и увидеть это:

$ file hello-sparc.bin
hello-sparc.bin: ELF 32-bit MSB executable, SPARC, version 1 (SYSV), statically linked, not stripped

Безусловно, этот бинарник запустился на реальном SPARC под управлением Sun Solaris 8. Ну, а дальше пошел процесс использования в Enterprise-задачах. Потратив много времени над сборкой кросс-GCC я набрался опыта, нашел ответы на многие вопросы и поделился ими с Вами в данной статье. Надеюсь, данная статья окажется хорошим How-To в копилке вопросов по кросс компиляции.

Успешных Вам кросс компиляций!