Кросс-компиляция «Hello, World» под ARM
Я всю жизнь разрабатываю под x86 (за исключением экспериментов с MIPS-I/PSX, но это больше из исторического интереса), и мне всегда хотелось написать что-нибудь на D под вторую главную архитектуру современности — AArch64. Недавно я досконально изучил, как это правильно делать, причем самым, на мой вкус, безболезненным способом, который не требует WSL, Docker и монструозных тулчейнов — понадобится только обычный свежий LDC плюс несколько дополнительных бинарей, которые легко скачать в готовом виде.
Итак, программа (src/main.d):
module main;
extern(C) nothrow @nogc
{
int printf(const(char)* fmt, ...);
}
extern(C) int main(int argc, char** argv) nothrow @nogc
{
printf("Hello, World!\n");
return 0;
}
Для начала нужно убедиться, что ваша версия LDC собрана с поддержкой aarch64: ldc2 --version.
Важный вопрос: в каком режиме компилировать — BetterC или нет? По моему мнению, на большинстве популярных платформ BetterC дает ноль преимуществ, отнимая важные фичи языка вроде классов. Он хорош для микроконтроллеров, для WASM, для всякой экзотики, но точно не для привычного GNU userspace, который наличествует на любой железке мощнее калькулятора. Так что я предлагаю сразу идти нормальным путем и заменить те функции druntime, которые вам нужны, самописными реализациями. Для простейшего примера, впрочем, это не требуется.
Моя команда сборки выглядит следующим образом:
ldc2 -Isrc -i --vgc
-defaultlib= ^
-debuglib= ^
-mtriple=aarch64-linux-gnu ^
-linker=lld ^
-Xcc=--sysroot=sysroot/aarch64-linux-gnu/libc ^
-Xcc=--gcc-toolchain=sysroot ^
-Xcc=-unwindlib=none ^
src/main.d ^
-of=test
Она предполагает, что в папке sysroot находится минимальное системное окружение, необходимое для компиляции linux-приложений. Я его взял отсюда: https://github.com/ClickHouse/sysroot. Заберите папку linux-aarch64 из репозитория со всем содержимым и переименуйте в sysroot. Это же окружение понадобится для тестирования.
При желании можно оформить то же самое для DUB:
{
"name": "test",
"targetType": "executable",
"targetName": "test",
"sourcePaths": ["src"],
"importPaths": ["src"],
"dflags-ldc": [
"--vgc",
"-defaultlib=",
"-debuglib=",
"-mtriple=aarch64-linux-gnu",
"-linker=lld",
"-Xcc=--sysroot=sysroot/aarch64-linux-gnu/libc",
"-Xcc=--gcc-toolchain=sysroot",
"-Xcc=-unwindlib=none"
],
"postBuildCommands-windows": [
"copy /Y test.exe C:\\Users\\gecko\\Documents\\vbshared\\arm\\test",
"del test.exe"
]
}
Последнее нужно под Windows, чтобы на выходе получился бинарник без расширения «.exe».
Если все хорошо, то вы получите файл test, который можно сразу проверить:
file ./test
Команда выведет что-то вроде
./test: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for
GNU/Linux 3.7.0, with debug_info, not stripped
Если есть ARM-компьютер, то можно запустить приложение на нем (я тестировал на Khadas VIM2), но для тестов гораздо практичнее настроить виртуальное окружение. Если у вас Linux, то рекомендую https://github.com/multiarch/qemu-user-static — нужно скачать qemu-aarch64-static на странице релизов. Это эмулятор пользовательского режима QEMU, который позволяет запускать бинарники AArch64 непосредственно на хост-системе.
Использую я его так:
./qemu-aarch64-static -L ./sysroot/aarch64-linux-gnu/libc/ ./test
Параметр -L передает эмулятору путь к корневой папке окружения, там должны быть динамический линкер /lib/ld-linux-aarch64.so.1 и папка lib64 с библиотеками C/POSIX — проверьте. Важно убедиться, что вместо симлинков не лежат пустые файлы, под Windows часто так получается.
Для Windows я, к сожалению, не нашел эмулятор юзерспейса linux-aarch64 (может, его и нет вовсе). Ручной запуск ARM-машины QEMU — это жуткий геморрой. Самый беспроблемный способ, который не заставляет постоянно запускать и останавливать тяжелые сессии эмуляции (равно как и вводить много команд вручную) — тупо установить обычный десктопный Linux в VirtualBox и там гонять qemu-aarch64-static, не выключая систему. Это немного костыльно, но жить можно, особенно если держать весь проект сразу в shared-папке хоста. Мне лично это удобно тем, что я и так использую Linux на виртуалке для других тестов.