llvm/clang って全部入りなんですよ。
UEFIの開発では色々な選択肢がありどれも一長一短でなかなか手が出しずらかったのがllvmを使えば主要な懸念が解決する、というのは先日の記事で書いた通りです。
gccはその環境向けに特化されたオプションでビルドされているのでクロス開発するにはオプションを変えてビルドしなおしが必要なのに対し、llvmは「全部入り」でビルドされているらしいという特徴があります。
この「全部入り」というのがミソでして。
もしかして、ターゲット変えればクロスコンパイルできるんじゃね?
ということで、色々奮闘してみました。
たかだか hello world のためにビルドスクリプトが複雑なのはこれをスケルトンにして他の UEFI アプリも作っているからです。
現在の UEFI の主要なターゲットは x64, i386, arm, aarch64 です。
ビルドだけならすぐにすべて対応できたように見えました。 aarch64 はもともと使っていた lld-4.0 だと対応していないようなので lld-6.0 に書き換えたところうまくビルドが通るようになりました。
ビルドできても動作確認できなければ意味がないのでqemuの対応方法を調べました。
もともと x64 対応だったものを i386 に対応するのは簡単にできました。
aarch64 に関してはOVMFを入手してqemuのビルドオプション調整するまでに少し時間かかりましたが、入れてみたらすんなり動きました。
問題が arm でした。
バイナリを正しく認識できていないようなのでファイルヘッダを調べてみました。
ARM の主要な machine type として 0x1C0(arm), 0x1C2(thumb), 0x1C4(thumbv2) の3種類あるようです。ARM には 32bit 固定の ARM 命令セットと 16bit の Thumb 命令セットがあるのが主な原因です。
動いているバイナリの machine type は 0x1C2 なのに対し、 llvm の出力するバイナリの machine type は 0x1C4 でした。
ためしに bootarm.efi の machine type をバイナリエディタで 0x1C4 から 0x1C2 に変えたところ、なんと動作しました。
つぎに clangに入れるパラメータを色々いじってみましたが、うまく machine type 0x1C2 になることができませんでした。
最後に hello.o の machine type をバイナリエディタで 0x1C4 から 0x1C2 に変えてみたところ、 lld が認識してくれませんでした。
そもそも win32 の標準は 0x1C4 のように見えるのですが、 UEFI が 0x1C2 を要求する理由がよくわかりません。
現状 llvm で arm(aarch32) の UEFI 開発は不可能と判断して打ち切ることにしました。
ARM+UEFI の実機なんて Surface RT くらいしか持ってないしハローワールド以上のものは作ってもあまり意味がないですしね・・・