27 мая 2010 г.

Собираем свой кросскомпилятор для ARM.

Для задачи портирования маленькой RT оси на SDK2.0, внутри которого какой-то ARM7, нужно собрать компилятор, который будет собирать исполняемые файлы под архитектуру ARM, работая при этом на машине с x86_64 процессором. К сожалению, в интернете практически отсутствуют руководства по сборке своего кросскомпилятора - в основном, рекомендуется использовать какие-то монстры наподобие Buildroot или crosstool-ng, которые помимо кросскомпилятора собирают еще кучу софта, мне ненужного.
Методом "научного тыка" я похоже набрел на последовательность действий, которая позволила мне наконец-то собрать для себя кросскомпилятор.

Для сборки нужны:
  • binutils - утилиты типа ar, nm и т.п.
  • gcc - он будет нашим кросскомпилятором
  • newlib - библиотека языка C, оптимизированная для использования во встраиваемых системах
Для сборки рекомендую выделить отдельный каталог, например ~/toolchain-arm, и проводить все последующие действия внутри него.
    Для начала скачиваем последнюю версию binutils отсюда. Распаковываем архив и конфигурируем исходники командой:
    ./configure --prefix=/usr/local/arm-linux/ --target=arm-elf --enable-interwork --enable-multilib --with-float=soft --disable-werror
    Затем, как обычно собираем и устанавливаем binutils. Установка будет произведена в каталог /usr/local/arm-linux. К сожалению, у меня возникли проблемы с правами доступа к подкаталогам каталога arm-linux; но они решаются запуском следующей команды в каталоге arm-linux.
    sudo find . -type d -exec chmod go+rx '{}' \;
    Исполняемые файлы будут расположены в /usr/local/arm-linux/bin. Поместим этот каталог в $PATH, чтобы иметь доступ к утилитам при сборке других программ (желательно поместить нижеследующую строку в ~/.profile и выполнить команду
    . ~/.profile):
    PATH=$PATH":/usr/local/arm-linux/bin" && export PATH
    Теперь мы можем обращаться к новым утилитам, используя префикс arm-elf: arm-elf-ar, arm-elf-nm и т.п.
    Затем, приступим к сборке кросскомпилятора и библиотеки языка C. Скачаем gcc отсюда и newlib отсюда и распакуем их. Также скачаем обновленный командой GNUARM файл t-arm-elf отсюда и заменим им файл [gcc-source-dir]/gcc/config/arm/t-arm-elf.
    Перед сборкой newlib нужно собрать урезанную версию gcc для arm, при помощи которой мы скомпилируем newlib. Затем, уже имея собранную newlib, мы скомпилируем полную версию gcc с newlib.
    Сборку gcc следует проводить в отдельном каталоге [gcc-build-dir], который не должен быть расположен внутри каталога [gcc-source-dir], иначе мы получим ошибку:
    ./gcc/libgcc.mvars: Нет такого файла или каталога
    Перейдем в [gcc-build-dir] и выполним команду:
    sudo ../[gcc-source-dir]/configure --target=arm-elf --prefix=/usr/local/arm-linux/ --enable-interwork --enable-multilib --with-float=soft --enable-languages="c,c++" --with-newlib  --with-headers=../[newlib-source-dir]/newlib/libc/include/ && sudo make && sudo make install
    Все, мы получили урезанную версию кросскомпилятора gcc для arm. Нужно перейти в каталог /usr/local/arm-linux/ и вновь (как на стадии с binutils) исправить права доступа на подкаталоги иначе на следующем шаге мы получим ошибку:
    arm-elf-gcc: error trying to exec 'cc1': execvp: Нет такого файла или каталога
    Теперь приступим к сборке newlib. Перейдем в [newlib-source-dir] и выполним команду:
    ./configure --target=arm-elf --prefix=/usr/local/arm-linux/ --enable-interwork --enable-multilib --with-float=soft
    Далее, как обычно, make и make install.
    Имея собранную newlib приступим к сборке полной версии кросскомпилятора. Перейдем в [gcc-build-dir] и выполним:
    sudo make && sudo make install
    Осталось только вновь исправить права доступа на  подкаталоги в каталоге /usr/local/arm-linux.
    Теперь все - у нас есть свой кросскомпилятор для архитектуры ARM!

    UPD (03.08.2010):
    При сборке простого хелловорлда, у меня внезапно выскочила ошибка:
    arm-elf-gcc -c -Wall -Wstrict-prototypes -Wno-trigraphs -O0 -pipe -g -mcpu=arm7tdmi main.c
    /usr/local/arm-linux/libexec/gcc/arm-elf/4.5.0/cc1: error while loading shared libraries: libmpfr.so.1: cannot open shared object file: No such file or directory
    make: *** [main.o] Ошибка 1
    Оказывается, libmpfr нужно обязательно ставить вместе с gcc. Лично я проделал это так:
    • Качаем MPFR отсюда. Распаковываем и собираем командой:
      ./configure --target=arm-elf --prefix=/usr/local/arm-linux/ && make && sudo make install
      Теперь libmpfr установлена в /usr/local/arm-linux/lib/
    • Переходим в [gcc-build-dir], удаляем оттуда все файлы и каталоги и заново конфигурируем и собираем gcc заново командой:
      sudo ../[gcc-source-dir]/configure --target=arm-elf --prefix=/usr/local/arm-linux/ --enable-interwork --enable-multilib --with-float=soft --enable-languages="c,c++" --with-newlib  --with-headers=../[newlib-source-dir]/newlib/libc/include/ --with-mpfr=/usr/local/arm-linux/lib/ --without-ppl && sudo make && sudo make install

    UPD (16.11.2011):
    Все что описано выше является ужаснейшими костылями. Есть способ проще - http://www.gnuarm.com/support.html, готовые бинарники и исходники в секции "Files".
    Если не собирается binutis и версия gcc >= 4.3, то добавить опцию --disable-werror для скрипта configure.
    Если при сборке ругается на отсутствие makeinfo, а makeinfo в системе есть - подправить переменную MAKEINFO в Makefile на /usr/bin/makeinfo.
    Для сборки insight-gdb нужен установленный bison/yacc.