最初の UEFI プログラムをコンパイルするのは、思ったほど簡単ではありません。環境構築には時間がかかり、リンカーエラーも多く、.EFI プログラムは通常のデスクトップアプリのように直感的に編集して実行できるものではありません。
この記事では、入門者向けに整理します。まず自分の最初の UEFI プログラムをコンパイルしたいだけなら、どこから始めるべきか、どの概念を先に理解すべきか、どこで詰まりやすいかを見ていきます。
UEFI プログラムとは何か
UEFI プログラムは通常、.EFI ファイルです。
Windows でダブルクリックして実行する普通の .exe ではありません。UEFI ファームウェア環境で動作する PE/COFF 実行ファイルです。よくある用途には次のようなものがあります。
- ブートマネージャー
- ハードウェア初期化ツール
- ファームウェア更新ツール
- 起動前診断ツール
- カスタムブートフロー
システム起動の早い段階で見える多くの機能は、UEFI アプリケーション、ドライバー、またはファームウェアサービスと関係している場合があります。
初心者は、最初から完全なファームウェア開発を理解する必要はありません。まずは UEFI Shell またはエミュレーターから読み込める .EFI ファイルをコンパイルすることを目標にします。
最初から EDK II に入らないほうがよい理由
本格的な UEFI 開発では EDK II に出会うことがよくあります。
EDK II は機能が充実しており、実際のファームウェア開発にも近いものです。ただし初心者にはあまり優しくありません。
- プロジェクト構造が複雑
- ビルドシステムに学習コストがある
- 環境変数やツールチェーン設定が多い
- コンパイルエラーを読み解きにくい
- コードを書く前に環境構築で詰まりやすい
目的が「最小の UEFI プログラムをまず動かす」ことであれば、軽量なサンプルから始めるほうが向いています。
pbatard/uefi-simple はそのようなプロジェクトです。目的は明確で、シンプルな UEFI Hello World サンプルを提供し、まず .EFI をコンパイルできるようにすることです。
uefi-simple は何に向いているか
uefi-simple は UEFI 入門の最初の足場として向いています。
主に 3 つの問題を解決します。
- 最小限のコンパイル可能な UEFI アプリケーション構造を提供する
- 最初から大規模なファームウェア工程に触れる複雑さを避けられる
- コンパイル、リンク、実行の流れが通るかを先に確認できる
このプロジェクトは Visual Studio 2022 や MinGW/gcc など複数のビルド方法に対応しており、QEMU と OVMF を使ったテストもできます。
つまり、初期段階から実機を何度も再起動して試す必要はありません。まずエミュレーターで動かすほうが安全です。
入門前に用意するもの
最低限、いくつかの種類のツールが必要です。
1 つ目はコンパイラーツールチェーンです。
Windows では、まず次のどちらかを検討できます。
- Visual Studio 2022
- または MinGW/gcc
2 つ目は UEFI 実行環境です。
主な選択肢は 2 つあります。
- 実機の UEFI Shell で
.EFIを実行する - QEMU + OVMF を使って仮想環境でテストする
3 つ目はサンプルプロジェクトです。
初心者は空ディレクトリからビルドスクリプトを手書きするより、uefi-simple のような最小サンプルを使うほうが、ビルドシステムで詰まる可能性を減らせます。
大まかな流れ
最小 UEFI プログラムの入門手順は、次のように考えると分かりやすいです。
まず、サンプルプロジェクトを取得します。
|
|
次に、ビルドツールチェーンを選びます。
Visual Studio を使う場合は、プロジェクト内の Visual Studio ソリューションに従ってビルドします。
MinGW/gcc を使う場合は、プロジェクトが提供する Makefile や説明に従います。
次に、.EFI ファイルを生成します。
ここで重要なのは対象アーキテクチャを確認することです。一般的な PC は通常 x86_64、つまり 64 ビット UEFI 環境です。
次に、UEFI Shell からアクセスできる場所へ .EFI を置きます。
実機を使う場合は、通常 FAT32 パーティションまたは USB メモリを用意します。
QEMU を使う場合は、ディレクトリやディスクイメージをマウントできます。
最後に、UEFI Shell で実行します。
実行結果は通常、Hello World のような最小限の出力です。
詰まりやすいところ
UEFI プログラムのコンパイルで詰まりやすいのは、C 言語そのものではなく、環境とリンクです。
よくある問題は次の通りです。
- コンパイラーのアーキテクチャが違う
- ターゲット形式が違う
- リンカー引数が不足している
- UEFI エントリーポイントがない
- UEFI が読み込める
.EFIではなく通常の実行ファイルを生成している - QEMU または OVMF の設定ができていない
- 実機の Secure Boot が未署名プログラムの実行を止める
特にリンカーエラーは、初心者にコードの間違いだと思わせがちです。
実際には、エントリー関数、サブシステム、対象アーキテクチャ、リンクスクリプト設定が原因であることも多いです。
そのため最初の段階では、複雑なロジックに急いで進まないほうがよいです。まず元のサンプルがコンパイルでき、実行できることを確認してから、少しずつ出力内容を変えていきます。
テストに QEMU + OVMF をすすめる理由
実機で UEFI プログラムをテストすることはできますが、初心者の段階ではあまり便利ではありません。
何度も次の流れを繰り返すことになります。
- コンパイル
- USB メモリへコピー
- 再起動
- UEFI Shell に入る
- 実行
- エラーを記録
- OS に戻って修正
このループはかなり遅いです。
QEMU + OVMF を使うと、OS の中で直接 UEFI 環境をシミュレートできます。.EFI が読み込めるかをより速く確認でき、実機のブート項目へ影響を与える可能性も低くなります。
プログラムが基本的に動くようになってから実機で試すほうが安定します。
初心者はまずどこを変えるべきか
サンプルプロジェクトで最初の .EFI をコンパイルできたら、すぐに複雑な機能を書き始めないほうがよいです。
おすすめの順番は次の通りです。
- まず出力テキストを変え、再コンパイル後のプログラムが本当に反映されているか確認する。
- 次に UEFI が提供する簡単な情報を読んでみる。
- エントリー関数、出力プロトコル、基本サービスを理解する。
- 最後にファイルシステム、グラフィック出力、ブート項目管理などの複雑な機能を考える。
この順番なら、各ステップを確認できます。
最初から多くを変えると、問題がコードなのか、ビルドなのか、実行環境なのか判断しにくくなります。
普通の C プログラムとの違い
UEFI プログラムは C で書けますが、通常の C プログラムとは実行環境がまったく違います。
通常の C プログラムは OS 上で動き、標準ライブラリ、ファイルシステム、プロセスモデル、システムコールに依存できます。
UEFI プログラムは OS が起動する前に動作します。利用するのは UEFI ファームウェアが提供するサービスです。通常のプログラムで当たり前に使っているものの多くは、ここでは自然に使えるわけではありません。
そのため UEFI プログラムを書くときは、いくつかの違いに慣れる必要があります。
- エントリー関数が違う
- 出力方法が違う
- 利用できるライブラリが違う
- メモリーやファイルアクセスの方法が違う
- デバッグ方法が違う
だからこそ、普通の C プログラムの感覚で直接書くのではなく、最小サンプルから始めるのがおすすめです。
現実的な学習ルート
入門だけなら、次の流れが現実的です。
- 第 1 步:
uefi-simpleをコンパイルする - 第 2 步:QEMU + OVMF で実行する
- 第 3 步:Hello World の出力を変更する
- 第 4 步:UEFI Shell が
.EFIをどう読み込むか理解する - 第 5 步:UEFI のエントリー関数と基本的な出力プロトコルを学ぶ
- 第 6 步:その後で EDK II やより完整な UEFI 開発資料を見る
このルートの重要点は、まずフィードバックループを作ることです。
ソースから .EFI を生成し、UEFI 環境で出力を確認できれば、最初の大きな壁は越えています。
参考
最後に
最初の UEFI プログラムをコンパイルするときの難しさは、多くの場合「C コードを書くこと」ではなく、ツールチェーン、リンク形式、実行環境をつなげることにあります。
複雑な機能へ急ぐ必要はありません。
uefi-simple のような最小サンプルから始め、まず実行できる .EFI を得てから、UEFI のエントリーポイント、プロトコル、ビルド方法を少しずつ理解していくほうが楽です。