golang

Как я веб-приложение на Go для архитектуры MIPS кросс-компилировал

  • пятница, 12 июня 2026 г. в 00:00:22
https://habr.com/ru/articles/1046503/

Введение

Привет, Хабр! Являюсь давним пользователем маршрутизаторов Keenetic/Netcraze. В свое время подкупили надежность устройств, широкие возможности программного обеспечения, оперативная техническая поддержка, большое сообщество пользователей возможность установки дополнительных пакетов через среду Entware. В относительно недавнем прошлом производитель строил свои устройства на базе mips/mipsel чипов, таким образом, несколько таких устройств оказались у меня в руках.

Дополнительные пакеты в репозитории среды Entware позволяют решать массу задач. Я в свою очередь решил использовать ее для решения проблем деградации сети Интернет в текущем месте проживания.

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

В своих поисках остановился на программе Xray. Помимо исходного кода, авторы любезно предоставляют свой продукт собраным в виде консольной программы под различные платформы и архитектуры, включая mips на linux. Ничего не имею против консоли, но необходимость редактировать достаточно большие json-файлы в поике работоспособной конфигурации, при появлении нововых деградаций, немного удрачает. Особенно когда знаешь, что графические приложения, основанные на исходном коде Xray, умеют конфигурироваться с помощью url-ов или qr-кодов. Поэтому решил найти решение с графическим веб-инфтерфейсом в репозиториях на которые ссылаются авторы Xray. Подавляющее их большинство написано на языке Go и в бинарном виде поставляется только для архитектур x86_64, arm, s390. Что вполне достаточно для типового VPS/VDS, но не для нашего встраивоемого устройства. Исправим эту оплошность :-)

Идеальный вариант

Конечно было бы удобнее получить инструмент, который можно было бы интегрировать в веб-интерфейс Keenetic OS (например, в виде плагина, как для OpenWRT или для AsusWRT). Но я так понимаю возможностей интеграции сторонних в веб-интерфейс у нее нет.

Первая попытка

Среди многообразия вышеупомянутых приложений, было выбрано tx-ui из-за минимизации зависимостей от внешних СУБД и скриптов по настройке, которые по-умолчанию рассчитаны на использование systemd.

Компилятор Go предоставляет возможности по кросс-компиляции из коробки для большого количества архитектур через выставление переменных окружения GOARCH и GOOS. На момент написания статьи есть проблемы с компиляцией под MIPS на версии 1.26, поэтому пользоваться будем версией 1.27.8. Устанавливаем, воспользовавшись инструкцией с официального сайта.

Скачиваем архив с исходным кодом со страницы релизов в github, распаковываем, переходим в директорию:

mkdir habr_mips
cd habr_mips/
wget https://github.com/AghayeCoder/tx-ui/archive/refs/tags/v0.6.9.tar.gz
tar -xzf v0.6.9.tar.gz
cd tx-ui-0.6.9/

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

GOOS=linux GOARCH=mipsle go build 'main.go'

Проверяем формат собранного файла:

file main
main: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), statically linked, BuildID[sha1]=e539635e58a2b8b87f24e4590d1109ed8e0eb99c, with debug_info, not stripped

32-bit LSB executable, MIPS, MIPS32 – сообщает о том что ELF-файл собран для little endian 32-разрядной архитектуры MIPS.

Переносим файл любым доступным способом на маршрутизатор, выполняем его и натыкаемся на ошибку:

./main 2026/03/15 12:11:00 Starting x-ui 0.6.9 2026/03/15 12:11:00 Error initializing database: Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub

Поверхностный поиск проблемы сообщает о том, что мы должны выставить переменную окружения CGO_ENABLED=1 и убедиться в том, что компилятор gcc установлен. Наверное самым простым способом будет установить обертку над gcc для сборки mips приложений на x86. Но на текущий момент нет ясности в том, что нам не понадобиться собирать зависимости с динамическим подключением библиотек. Этот момент важен, так как система Entware установлена по пути /opt, что не даст нам найти /opt/bin/ld по пути /bin/ld для поиска библиотек из Entware.

Вторая попытка

Подготовительный этап

Для исключения проблем, которые могут гипотетически возникнуть при необходимости подключения зависимостей динамически, перейдем к более консервативному варианту получения обертки над компилятором gcc. Для этого воспользуемся инструкцией по сборке OpenWRT-пакетов из исходных кодов из репозитория Entware, а также ссылками на построение SDK с сайта OpenWRT: 1, 2.

Для сборки я использовал LXC-контейнер с окружением Debian 11, так как вероятно Entware базируется на OpenWRT 19.07 или ниже и ему для сборки требуется Python 2.7.

Сборка toolchain

Устанавливаем следующие зависимости для сборки:

sudo apt install build-essential ccache ecj fastjar file g++ gawk \
gettext git java-propose-classpath libelf-dev libncurses5-dev \
libncursesw5-dev libssl-dev python python2.7-dev python3 unzip wget \
python3-distutils python3-setuptools python3-dev rsync subversion \
swig time xsltproc zlib1g-dev g++-multilib p7zip-full

Скачиваем репозиторий и открываем его директорию:

git clone https://github.com/Entware/Entware.git && cd Entware

Обновляем скрипты сборки:

make package/symlinks

Копируем конфигурационный файл для сборки под архитектуру mipsel:

cp -iv configs/mipsel-3.4.config .config

Собираем оставшиеся инструменты для хост-системы:

make -j$(nproc) tools/install

Тулчейн:

make -j$(nproc) toolchain/install

Системные пакеты для целевой системы:

make -j$(nproc) target/compile

Если на каком-то из предыдущих 3 этапов возникает ошибка, следует перезапустить команду с параметром -j1 вместо -j$(nproc) и дополнительным параметром V=sc, например:

make -j1 target/compile V=sc

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

При успешном выполнении команд, в директории staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin будут располагаться исполняемые файлы с префиксом mipsel-openwrt-linux*.

Список исполняемых файлов

~/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin$ ls | grep mips
mipsel-openwrt-linux-addr2line
mipsel-openwrt-linux-ar
mipsel-openwrt-linux-as
mipsel-openwrt-linux-c++
mipsel-openwrt-linux-c++filt
mipsel-openwrt-linux-cpp
mipsel-openwrt-linux-elfedit
mipsel-openwrt-linux-g++
mipsel-openwrt-linux-gcc
mipsel-openwrt-linux-gcc-8.4.0
mipsel-openwrt-linux-gcc-ar
mipsel-openwrt-linux-gcc-nm
mipsel-openwrt-linux-gcc-ranlib
mipsel-openwrt-linux-gcov
mipsel-openwrt-linux-gcov-dump
mipsel-openwrt-linux-gcov-tool
mipsel-openwrt-linux-gnu-addr2line
mipsel-openwrt-linux-gnu-ar
mipsel-openwrt-linux-gnu-as
mipsel-openwrt-linux-gnu-c++
mipsel-openwrt-linux-gnu-c++filt
mipsel-openwrt-linux-gnu-cpp
mipsel-openwrt-linux-gnu-elfedit
mipsel-openwrt-linux-gnu-g++
mipsel-openwrt-linux-gnu-gcc
mipsel-openwrt-linux-gnu-gcc-8.4.0
mipsel-openwrt-linux-gnu-gcc-ar
mipsel-openwrt-linux-gnu-gcc-nm
mipsel-openwrt-linux-gnu-gcc-ranlib
mipsel-openwrt-linux-gnu-gcov
mipsel-openwrt-linux-gnu-gcov-dump
mipsel-openwrt-linux-gnu-gcov-tool
mipsel-openwrt-linux-gnu-gprof
mipsel-openwrt-linux-gnu-ld
mipsel-openwrt-linux-gnu-ld.bfd
mipsel-openwrt-linux-gnu-nm
mipsel-openwrt-linux-gnu-objcopy
mipsel-openwrt-linux-gnu-objdump
mipsel-openwrt-linux-gnu-ranlib
mipsel-openwrt-linux-gnu-readelf
mipsel-openwrt-linux-gnu-size
mipsel-openwrt-linux-gnu-strings
mipsel-openwrt-linux-gnu-strip
mipsel-openwrt-linux-gprof
mipsel-openwrt-linux-ld
mipsel-openwrt-linux-ld.bfd
mipsel-openwrt-linux-nm
mipsel-openwrt-linux-objcopy
mipsel-openwrt-linux-objdump
mipsel-openwrt-linux-ranlib
mipsel-openwrt-linux-readelf
mipsel-openwrt-linux-size
mipsel-openwrt-linux-strings
mipsel-openwrt-linux-strip

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

Сборка

Модифицируем команду которую использовали выше для сборки Go приложения, добавив в нее следующие параметры:

GOMIPS=softfloat – добавляем программную эмуляцию вычислений с плавающей точкой

CGO_ENABLED=1 – переменная окружения, которая включает поддержку cgo в Go, позволяя коду Go взаимодействовать с библиотеками C.

CC=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-gcc – переменная окружения, которая задает путь до компилятора gcc (нужен для работы CGO_ENABLED)

CXX=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-g++ – переменная окружения, которая задает путь до компилятора g++ (нужен для работы CGO_ENABLED)

-ldflags=‘-s -w’ – флаги компиляторам: отключение таблицы символов и генерации отладочной информации в исполняемом файле.

GOOS=linux GOARCH=mipsle GOMIPS=softfloat CGO_ENABLED=1 CC=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-gcc CXX=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-g++ go build -ldflags='-s -w' 'main.go'

На выходе получаем файл main:

file main
main: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /opt/lib/ld.so.1, BuildID[sha1]=2e0163cb60d026a4edd6d5a8dfe227001a2675cb, for GNU/Linux 3.2.0, stripped

Конфигурация приложения на устройстве

Переносим собранный файл любым удобным способом на конечное устройство (или можно использовать эмулятор – про него возможно то же стоит статью написать) и переименовываем его в оригинальное название:

mv main x-ui

Первоначальный запуск выполняем для уточнения доступа к веб-панели

./x-ui setting -show
current panel settings as follows:
Warning: Panel is not secure with SSL
hasDefaultCredential: true
port: 2053
webBasePath: /

По информации [из github] (https://github.com/AghayeCoder/tx-ui/blob/main/README.ru_RU.md), defaultCredential следующие:

Имя пользователя: admin Пароль: admin

Запуск приложения производим вместе с заданием переменной окружения XUI_DB_FOLDER=/opt/etc/x-ui, так как путь к директории с файлом БД по умолчанию /etc/x-ui ведет в read-only для Entware раздел.

XUI_DB_FOLDER=/opt/etc/x-ui ./x-ui

Далее в открываем браузер, вводим туда адрес устройства, порт и webBasePath (если задан) и видим следующую страницу

Окно входа
Окно входа
Окно статуса системы
Окно статуса системы

Считаем, что приложение работоспособно. Конфигурирование приложения не буду затрагивать, так как оно происходит похожим образом как для 3x-ui или аналогичных (примеры можно найти на различных сетевых ресурсах, включая Хабр).

Добавляем скрипт автозапуска в /opt/etc/init.d. Файл должен называться SXXимя, где XX -- приоритет запуска службы (чем больше номер -- тем позже запуск).
#!/bin/sh

ENABLED=yes
PROCS=x-ui
ARGS="run"
PREARGS="XUI_DB_FOLDER=/opt/etc/x-ui"
DESC=$PROCS
PATH=/opt/sbin:/opt/bin:/opt/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/opt/tx-ui

#. /opt/etc/init.d/rc.func

#OPID=$(pgrep x-ui)
#echo $OPID

daemon_status ()
{
        [ -n "`pidof x-ui`" ]
}

start() {
	echo "starting tx-ui..."
	cd /opt/opt/tx-ui
	export $PREARGS
	$PROCS $ARGS &
	}

stop() {
	echo "stopping tx-ui..."
	PID=$(pidof x-ui)
	kill $PID
	}

status() {
	if daemon_status; then
		echo "PID of tx-ui is $(pidof x-ui)"
	else
		echo "tx-ui is not running"
	fi
	}

case "$1" in
        start)
                start
                ;;
        stop)
                stop
                ;;
        restart)
                stop
                sleep 3
                start
                ;;
        status) status
                ;;
        *)
                echo "Usage: $0 (start|stop|restart|status)"
                exit 1
                ;;

esac

exit 0

Таким образом мы собрали и развернули Go приложение для архитектуры mips.