Кросс-компилятор для Raspberry Pi4

op13bk2kfwkvgiiebrajjvdcvr8.jpeg
Хочу рассказать, как я собрал себе кросс-компилятор для Raspberry Pi4 с помощью crosstool-ng. Возможно кому-то тема покажется слишком примитивной и скучной. Я и сам поначалу думал, что быстро смогу собрать кросс-компилятор, но пришлось повозиться и изучать вопрос, некоторые нюансы были для меня неожиданны. Дальше расскажу что и как я делал.

Пожалуй первый вопрос, который нужно себе задать: «Для чего нужен кросс-компилятор?» В самом деле, Raspberry Pi4 довольно быстр и компилировать программы можно прямо на нем. Если проект простой, то вполне можно так и делать. Но когда проект разрастается и усложняется, то тут уже лучше переходить на кросс-компиляцию. Тогда и другие вопросы проще решить, например, можно организовать авто сборку проекта на интеграционном сервере. При кросс-компиляции на быстром ПК можно значительно поднять скорость сборки, да и средства редактирования программ на ПК можно найти лучше.

Второй вопрос: «Может взять уже готовый кросс-компилятор? Зачем свой-то собирать?» Сказать по правде я не нашел нормального кросс-компилятора для Raspberry. Может я плохо искал? На github от распберри есть проект tools и там есть несколько кросс-компиляторов, но они же древние! Ну что там? Версия 4.9.3? Как-то не серьезно. На странице загрузки Linaro я вижу только версию 7.5, уже получше, но, во-первых, на самом Raspberry Buster OS уже версия 8.3, а во-вторых, компилятор Linaro у меня заработал только после некоторых патчей. Как-то странно…

Именно после попыток запустить компилятор Linaro я подумал, что наверное смогу сам собрать свой собственный кросс-компилятор. Некоторый опыт работы с crosstool-ng у меня уже был. Я делал кросс-компилятор для системы на кристалле Amber.

В чем сложность сборки? У инструмента crosstool-ng есть множество различных настроек, которые не всегда понятны. Сборка кросс-компилятора на моем ноутбуке занимает примерно 45 минут. Если с настройками ошибся, то полученный кросс-компилятор как-то не так работает, что-то не собирает, или собирает, но полученный бинарник не запускается на Raspberry. После проверки полученного кросс-компилятора мне приходилось опять что-то изменять в настройках и опять генерировать его сначала. Ну я сделал несколько попыток, пока не получилось так как я хотел.

Пожалуй главное, чего я не очень понимал сначала — компилятор должен соответствовать операционной системе. Это выглядит несколько странно, но это так получается. В самом деле, в ОС уже содержатся стандартные библиотеки c/c++. Они экспортируют набор функций. Код, генерируемый кросс-компилятором, должен содержать вызовы этих библиотечных функций. Поэтому версии библиотек в самом кросс-компиляторе и в ОС для которой будет генерироваться код должны совпадать.

Попробуем создать кросс-компилятор.
Порядок действий будет такой:
1) Проверить, что в нашей хост системе (я пользуюсь Ubuntu 18) установлены следующие пакеты:

gcc g++ gperf bison flex texinfo help2man make libncurses5-dev python3-dev autoconf automake libtool libtool-bin gawk wget bzip2 xz-utils unzip patch libstdc++6 rsync git
Если чего-то нет, то нужно установить через sudo apt install…
2) Скачиваем crosstool-ng версии 1.24.0 с их сайта
wget crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.24.0.tar.bz2

3) Распаковываем полученный архив
tar xjf crosstool-ng-1.24.0.tar.bz2
cd crosstool-ng-1.24.0

4) Собираем crosstool-ng
./configure
make
make install

5) Патчим crosstool-ng
Это довольно странное действие, но без него у меня не получалось хорошего результата. Без этого патча кросс-компилятор отказывался по умолчанию искать библиотеки в папке /usr/lib/arm-linux-gnueabihf относительно sysroot.
Патч нужно получить на самом распберри.
На распберри пи4 с установленной OS Buster (это важно, я делаю кросс-компилятор именно для этой ОС) нужно взять исходники binutils:
sudo apt install binutils-source
и посмотреть файл /usr/src/binutils/patches/129_multiarch_libpath.patch
Этот файл мы кладем в папку crosstool-ng-1.24.0/packages/binutils/2.31.1 на нашем хост компьютере. Это важно положить именно в эту папку, так как на самой Raspberry Pi OS Buster версия binutils 2.31.1. Проверьте это командой в консоли на распбери
ld --version
Кроме этого, нужно проверить, какая GLIB стоит в вашей OS на распбери
ldd --version
У меня в Buster стоит 2.28 и значит и сам crosstool-ng нужно будет сконфигурировать в эту версию GLIB 2.28 там по умолчанию не такая версия. Если этого не сделать, то кросс-компилятор будет генерировать код, который не будет запускаться на Raspberry в Buster OS — бинарник может ссылаться на несуществующие функции во внешних библиотеках.

6) Подготовим начальную конфигурацию crosstool-ng
В самом crosstool-ng уже есть довольно много типовых конфигураций. В том числе, там есть очень похожая на то, что нам надо конфигурация для Raspberry Pi3
Список всех возможных конфигураций можно посмотреть командой

./ct-ng list-samples
Потом выбранную концигурацию нужно «активировать» командой
./ct-ng armv8-rpi3-linux-gnueabihf
Появится новый файл конфигурации .config, который полностью определяет свойства будущего кросс-компилятора. Теперь его нужно немного поправить. Можно редактировать и вручную, но легче исправлять из меню
./ct-ng menuconfig
Выглядит самое первое меню вот так:
xkqaszn2ihv6lpomz5nriqyqvqu.png

7) Дальше меняем странную опцию, возможно необязательную.
Практически везде где хоть что-то пишется о создании кросс-компилятора для Raspberry пишут убрать галочку с опции «Render the toolchain read-only». Эта настройка находится в меню «Paths and misc options». В документации к crosstool-ng написано, что по умолчанию итоговый компилятор будет доступен только для чтения, чтобы ненароком компилируемая программа не была установлена в sysroot самого компилятора. На мой взгляд это вполне разумное решение. Не знаю, для чего всякие авторы пишут убрать галочку отсюда…

8) Следующее. Заходим в пункт «Target options» и устанавливаем значения согласно скриншоту:
oplnsmwy4mmsth8lfxhbmcdoc3g.png

Нужно сказать, что вообще-то Raspberry Pi4 имеет процессор Cortex-A72 и это можно здесь в меню назначить. Однако, я себе оставил Cortex-A53 потому, что я буду компилировать не только для Pi4, но и для Pi3. Хотя, точную настройку можно будет задавать уже при запуске компилятора gcc с помощью параметра в командной строке -mcpu=cortex-a72

Хочу еще добавить, что я предпочитаю, чтобы кросс-компилятор имел точно такой же tuple, как и компилятор на самой плате Raspberry. То есть итоговый компилятор будет arm-linux-gueabihf и никак иначе. Мне так проще. Многие существующие пакеты имеют в своем составе готовые cmake файлы, где компилятор назначается именно arm-linux-gnueabihf. Мы же не хотим потом эти все cmake файлы исправлять вручную и писать там что-то вроде armv8-mygcc-linux-gnueabihf? Для этого я выключаю опцию «Omit vendor part of target tuple».

9) Заходим в меню «Binary utilities» и устанавливаем версию binutils 2.31.1 (Помните, что у нас еще патч именно для него?)
rvniyvyi9hfwhazo1al-8idq424.png

10) Заходим в меню «Operating system» и устанавливаем версию ядра 4.20
_i3brh5w0ixzlr0qnkt3q1mbglo.png

Вообще-то Raspberry Buster OS уже сейчас имеет ядро 5.10, но максимум, что можно поставить это только 4.20

11) Заходим в меню «C-libraries» и устанавливаем версию GLIB 2.28
pa_j6chk9kairi504c49vnahxyy.png

12) Заходим в меню «C-compiler»
dffba5tprg8-ssqm4gursw2lo7u.png

Устанавливаем желаемую версию будущего кросс-компилятора 8.3.0 и добавляем в настройки компилятора core gcc extra config опцию --enable-multiarch. Это важно, чтобы кросс-компилятор находил библиотеки в sysroot по пути /usr/lib/arm-linux-gnueabihf
Теперь выходим из всех меню, сохраняем сделанные изменения, когда спросит и приступаем к сборке.

13) Сборка выполняется простыми командами

export DEB_TARGET_MULTIARCH=arm-linux-gnueabihf
./ct-ng build
Придется подождать. У меня сборка на ноутбуке шла примерно 45 минут. После сборки ваш новый кросс-компилятор расположен в пути ~/x-tools/arm-linux-gnueabihf

Можно проверить какие пути к библиотекам по умолчанию будет использовать кросс-компилятор. Будет ли он искать в пути /usr/lib/arm-linux-gnueabihf?

Для этого запустим команду:

~/x-tools/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-ld --verbose | grep «SEARCH»
Команда должна выдать что-то такое:
SEARCH_DIR (»=/usr/local/lib/arm-linux-gnueabihf»); SEARCH_DIR (»=/lib/arm-linux-gnueabihf»); SEARCH_DIR (»=/usr/lib/arm-linux-gnueabihf»); SEARCH_DIR (»=/usr/local/lib»); SEARCH_DIR (»=/lib»); SEARCH_DIR (»=/usr/lib»); SEARCH_DIR (»=/home/nick/x-tools/arm-linux-gnueabihf/arm-linux-gnueabihf/lib»);
Обратите внимание, что пути начинаются со знака »=». Это значит, что это пути относительно sysroot, если он будет указан при запуске gcc с опцией командной строки --sysroot=PATH_TO_YOUR_SYSROOT.

Вот пожалуй и все. Остается решить вопрос, где взять sysroot. Проще всего на Raspberry Pi установить все нужные девелоперские пакеты и потом, вытащив из устройства SD карту, переписать ее на хост ПК. Но там тоже есть некоторые нюансы.

Теперь, когда кросс-компилятор у нас есть мы можем собрать какую ни будь специфичную для распберри программу. Например, демонстрационные программы userland от самой Raspberry:

git clone github.com/raspberrypi/userland.git
cd userland
export export PATH=${HOME}/x-tools/arm-linux-gnueabihf/bin:$PATH
./buildme
Результат компиляции будет в папке build/bin.

В завершении статьи я хотел бы привести таблицу дополнительных параметров для компиляторов gcc при сборке проектов под различные версии raspberry

Raspberry Pi 1: -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp (alias for vfpv2)
Raspberry Pi 2: -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4
Raspberry Pi 3: -mcpu=cortex-a53 -mfloat-abi=hard -mfpu=neon-fp-armv8 -mneon-for-64bits
Raspberry Pi 4: -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8 -mneon-for-64bits

Надеюсь, что моя статья кому-то окажется полезной.

© Habrahabr.ru