Pull to refresh

Заметки о кросс–компиляции приложений под DD-WRT

Programming *
Недавно у меня перестал работать вайфай-рутер и после некоторых раздумий я заказал Asus RT-N16. Хотелось наконец–то познакомиться с альтернативными прошивками. Характеристики этого рутера уже описывались на Хабре. Итак, DD-WRT установлена (v2.24), самба заведена, в первый юсб порт воткнута системная флешка, а во второй — внешний жесткий диск. А дальше я заинтересовался: а смогу ли я запустить свои программы на этом рутере? Я не нашел в сети руководства по сборке программ под рутер и надеюсь этой статьей восполнить пробел. Приведу пошаговое руководство с описанием небольших проблем, встреченных на пути.

В качестве подопытной программы мне хотелось запустить Easysync, о которой я недавно писал. Повторюсь, что это открытая программа для синхронизации файлов в стиле дропбокса. Программа написана с использованием Qt 4, а в качестве движка синхронизации используется Unison. Так что, эта статья описывает как откомпилировать Qt, Unison, Easysync для архитектуры MIPS и рассказывает о запуске Easysync на вашем домашнем рутере.

Хозяйке на заметку.


В рутерах используются непревычные обычному пользователю процессоры. Домашние пользователи привыкли к архитектуре x86, процессорам Intel и AMD. В Asus RT-N16 используется процессор Broadcom BCM4718 c MIPS архитектурой. Ресурсов на самом рутере может не хватить для сборки программы и поэтому обычно программы компилируют на большом брате (например, привычный десктопный компьютер), но под нужную архитектуру. Этот процесс называется кросс–компиляцией.

Инструменты.


  • Нам понадобится рабочий компьютер с линуксом на борту. Я использовал Ubuntu;
  • Компиляторы (gcc, g++);
  • Стандартные библиотеки Си, Си++;
  • Утилиты в виде GNU make, TAR, GZIP, GIT и так далее ;). В случае с Убунту, большинство уже установлено.

Шаги


  1. Первое, что нам нужно, это понять как вообще собираются программы для DD-WRT. Страница в их вики вводит в курс дела. Особенно интересен раздел Instructions. Там приводится ссылка на toolchain (далее – тулчейн), который используется для сборки DD-WRT из исходников. Тулчейн в данном случае это уже откомпилированная под нужную архитектуру библиотека Uclibc, а так же компилятор. Итак, тулчейн качается с сайта DD-WRT. Там не написанно, но собрано все под архитектурой x86_64. Поэтому если вы, набрав в терминале команду uname -m, не увидите что–нибудь со значением 64 (x86_64 или amd64), то вам придется установить новую версию линукса. Альтернативный метод — собрать тулчейн самому, но я предпочел поставить нужную мне систему. Дождавшись завершения скачивания (а это на сегодняшний день 716 Мб), переходим к следующему пункту.
  2. Открываем архив, в нем папки с тулчейнами под разные архитектуры. Нам нужен toolchain-mipsel_gcc4.1.2. Я распаковал эту папку в /home/fralik/. Почему именно toolchain-mipsel_gcc4.1.2? Mipsel, потому что я видел, что на рутере optware ставится именно из mipsel пакетов, а значит используется порядок байт от младшего к старшему (little-endian). Остальные папки, содержащие mipsel в названии, по–моему используются для сборки компонентов ядра DD-WRT.
  3. Добавляем тулчейн к пути:
    1. PATH=$PATH:~/toolchain-mipsel_gcc4.1.2/bin/
    2. cd ~
    3. mipsel-linux-gcc --version

    Вы должны увидеть информацию о версии GCC.
  4. Для проверки того, что все работает, можно использовать две helloworld программы. helloworld-c.c:
    1. #include <stdlib.h>
    2. #include <stdio.h>
    3.  
    4. int main() {
    5.     printf("Hello world!\n");
    6.     return 0;
    7. }

    Собираем и проверяем, используя команду file:
    mipsel-linux-gcc helloworld-c.c -o helloworld-c
    file helloworld-c

    Должны увидеть:
    helloworld-c: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked (uses shared libs), not stripped


    Можно запустить helloworld-c на рутере и проверить работоспособность еще и в реальности. Теперь helloworld-cpp.cpp:
    #define NEED_PRINTF 1
    #include <iostream>
     
    int main() {
        std::cout << "Hellow world!" << std::endl;
        return 0;
    }

    В терминале:
    mipsel-linux-g++ helloworld-cpp.cpp -o helloworld-cpp

    Обратите внимание на первую строчку! Без нее программа не соберется и будет выдана ошибка:
    /home/fralik/toolchain-mipsel_gcc4.1.2/bin/../lib/gcc/mipsel-linux-uclibc/4.1.2/../../../../include/c++/4.1.2/cstdio:126: error: '::printf' has not been declared

    Собираем, проверяем, что все работает на рутере.
  5. Настало время собрать Qt. Качаем Qt libraries for embedded Linux. Я распаковал архив так же в домашнюю директорию.
  6. Идем в терминале в папку с Qt. Нужно обратить внимание на папку mkspecs/qws. В ней хранятся Make файлы под разные архитектуры. Например, mkspecs/qws/linux-mips-g++/qmake.conf.
    Копируем папку linux-mips-g++ в linux-mipsel-g++:
    cp -R mkspecs/qws/linux-mips-g++/ mkspecs/qws/linux-mipsel-g++
  7. Редактируем файл mkspecs/qws/linux-mipsesl-g++/qmake.conf, чтобы он использовал команды mipsel-linux-* вместо mips-linux-*. В качестве флагов я использую
    -mel -march=mips32r2
  8. Далее приступаем к конфигурации Qt. Но для начала устанавливаем libxext-dev, иначе получим ошибку:
    Basic XLib functionality test failed!

    sudo apt-get install libxext-dev
    ./configure -no-cups -release -shared -no-qt3support -no-phonon -no-audio-backend -no-javascript-jit -no-webkit -qt-sql-lite -no-script -no-scripttools -opensource -no-gui -no-nis -no-opengl -nomake examples -nomake demos


    Должны увидеть:
    Qt is now configured for building. Just run 'make'.
    Once everything is built, you must run 'make install'.
    Qt will be installed into /usr/local/Trolltech/Qt-4.7.2


    Запускаем конфигуратор еще раз:
    ./configure -no-cups -release -shared -no-qt3support -no-phonon -no-audio-backend -no-javascript-jit -no-webkit -qt-sql-sqlite -no-script -no-scripttools -platform linux-g++-64 -xplatform qws/linux-mipsel-g++ -opensource -no-gui -no-nis -embedded mips -little-endian -no-opengl -nomake examples -nomake demos

    В первый раз мы сконфигурировали Qt для текущей архитектуры, во второй — для MIPS. Во второй команде аргумент xplatform как раз указывает на ту папку, которую мы создали в шаге 6. Я убрал поддержку ненужных мне вещей: no-qt3support, no-phonon, no-audio-backend, -nomake example и т.д.
    В результате видим такое сообщение:
    Qt is now configured for building. Just run 'make'.
    Once everything is built, you must run 'make install'.
    Qt will be installed into /usr/local/Trolltech/QtEmbedded-4.7.2-mips
    
  9. Запускаем make. Можно пойти перекурить или заварить чаю. =)
    На этом шаге у меня возникает очередная проблема:
    .obj/release-shared-emb-mips/qrect.o: In function `QRectF::toAlignedRect() const':
    qrect.cpp:(.text+0x1468): undefined reference to `ceilf'
    qrect.cpp:(.text+0x1488): undefined reference to `ceilf'

    Дело в том, что в библиотеке uclibc не включена по умолчанию функция ceilf. Можно, конечно, пересобрать библиотеку и добавить туда эту функцию, но мы все–таки используем тулчейн от DD-WRT и значит на рутере тоже стоит библиотека без функции ceilf. На этом шаге я долго танцевал с бубном, пытаясь превратить вызов ceilf в ceil. Но Qt была непоколебима. Пришлось браться за скальпель, особенно учитывая то, что класс QRect мне не понадобится. Открываем файл ~/qt-everywhere-opensource-src-4.7.2/src/corelib/tools/qrect.cpp, находим строчку 2379. Меняем на:
    int xmax = int( (float)((int)xp + w));

    то есть заменяем вызов
    qCeil(xp+y)
    на
    (float)((int)xp + y)
    Аналогичную операцию проводим над строчкой 2381, а также в строчке 640 файла ~/qt-everywhere-opensource-src-4.7.2/src/corelib/tools/qtimeline.cpp
    Снова вызываем make, на этот раз процесс должен завершиться успешно.
  10. Выполняем
    sudo make install
    Проверяем, что теперь у нас есть библиотека Qt под MIPS (несмотря на заверения Qt, что установка произойдет в папку /usr/local/Trolltech/QtEmbedded-4.7.2-mips, у меня все установилось в /usr/local/Trolltech/Qt-4.7.2):
    file /usr/local/Trolltech/Qt-4.7.2/lib/libQtCore.so.4.7.2

    /usr/local/Trolltech/Qt-4.7.2/lib/libQtCore.so.4.7.2: ELF 32-bit LSB shared object, MIPS, MIPS32 version 1 (SYSV), dynamically linked (uses shared libs), not stripped
  11. Переходим к сборке Easysync.
    # Заходим в какую–нибудь директорию, например, 
    cd ~
    git clone git://github.com/fralik/Easysync.git
    cd Easysync/server
    /usr/local/Trolltech/Qt-4.7.2/bin/qmake easysync-server.pro
    make
    # проверяем:
    file build/easysync-server
  12. Теперь нам нужен Unison. Не смотря на его полезность, его нет в optware. Будем собирать его прямо на рутере. Заходим на рутер по ssh или telnet.
    ipkg-opt install buildroot ocaml
    mkdir /mnt/unison
    cd /mnt/unison
    wget http://www.seas.upenn.edu/~bcpierce/unison//download/releases/stable/unison-2.40.61.tar.gz
    tar xvfz unison-2.40.61.tar.gz
    ocaml mkProjectInfo.ml > Makefile.ProjectInfo
    # Прежде чем собирать унисон, нужно сделать символьную ссылку с libncurses на libcurses
    ln -s /opt/lib/libncurses.so.5.7 /opt/lib/libcurses.so.5.7
    ln -s /opt/lib/libcurses.so.5.7 /opt/lib/libcurses.so.5
    ln -s /opt/lib/libcurses.so.5 /opt/lib/libcurses.so
    make NATIVE=false UISTYLE=text
    ./unison -version
    cp ./unison /opt/bin/unison

    Здесь ключевым моментом является ocaml mkProjectInfo.ml > Makefile.ProjectInfo. Без этого не соберется.
  13. Теперь нам нужно запустить все то, что мы собрали. Допустим адрес рутера 192.168.2.1 и нам доступна директория /mnt для записи
    scp /usr/local/Trolltech/Qt-4.7.2/lib/libQtCore.so.4.7.2 root@192.168.2.1:/opt/lib
    scp /usr/local/Trolltech/Qt-4.7.2/lib/libQtNetwork.so.4.7.2 root@192.168.2.1:/opt/lib
    scp /usr/local/Trolltech/Qt-4.7.2/lib/libQtSql.so.4.7.2 root@192.168.2.1:/opt/lib/
    cd <Easysync/server>
    # Запаковываем Easysync-server в один файл, чтобы было проще передать на рутер:
    ./build_package_dd-wrt.sh
    scp dd-wrt_bundle.tar.gz root@192.168.2.1:/mnt
  14. Заходим на рутер, в папку /mnt, распаковываем easysync-server-mipsel32.tar.gz
    tar xvfz easysync-server_mipsel.tar.gz
    ln -s /opt/lib/libQtCore.so.4.7.2 /opt/lib/libQtCore.so.4.7
    ln -s /opt/lib/libQtCore.so.4.7 /opt/lib/libQtCore.so.4
    ln -s /opt/lib/libQtCore.so.4 /opt/lib/libQtCore.so
    ln -s /opt/lib/libQtSql.so.4.7.2 /opt/lib/libQtSql.so.4.7
    ln -s /opt/lib/libQtSql.so.4.7 /opt/lib/libQtSql.so.4
    ln -s /opt/lib/libQtSql.so.4 /opt/lib/libQtSql.so
    ln -s /opt/lib/libQtNetwork.so.4.7.2 /opt/lib/libQtNetwork.so.4.7
    ln -s /opt/lib/libQtNetwork.so.4.7 /opt/lib/libQtNetwork.so.4
    ln -s /opt/lib/libQtNetwork.so.4 /opt/lib/libQtNetwork.so
     
  15. Далее есть смысл проверить работу унисона без Easysync (можно обратиться как к документации самого унисона, так и к install-файлам Easysync'а). После настройки унисона, возвращаемся на рутер, редактируем файл config.ini.sample и запускаем:
    ./setup_dd-wrt.sh


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

Краткая выжимка


  1. Собирать программы для DD-WRT можно, используя тулчейн с их сайта. Для C++ программ необходимо использовать #define NEED_PRINTF.
  2. Несмотря на отсутствие унисона в сборке optware, его можно собрать на рутере. Можно, в принципе, сделать и кросс–компиляцию, но я не разбирался с этим.
  3. Qt почти что без проблем собирается под MIPS: +1 к кроссплатформенности ваших программ.
  4. Easysync теперь и на домашних рутерах! Стоит отметить, что после перезагрузки сервис не будет запущен автоматически. Скорее всего, он даже не будет помечен как установленный (потому что флаг установки хранится в /tmp). Его можно снова установить командой /opt/bin/easysync-server -i или же запустить без установки командой /opt/etc/init.d/easysync-server start
    Для тех, кто заинтересуется Easysync'ом, но не захочет собирать его из исходников, доступна уже скомпилированная версия.

    Обновление, 30.05.2016


    Пользователь Nabytovych подсказывает, что некоторые ссылки устарели со временем. Обновил ссылки на тулчейн и Qt.
Tags:
Hubs:
Total votes 58: ↑55 and ↓3 +52
Views 18K
Comments 42
Comments Comments 42

Posts