以前、「xv6-riscvのビルド&QEMUでの実行環境をMacに導入する」という記事を書きました。
MIT純正のxv6にはx86版(Intel 32bit)とRISC-V版があり、以前の記事では、買い替え前のIntel Mac上でRISC-V版をクロスコンパイラを用いてコンパイルし、QEMUで動かしていました。
しかしこの度、とある事情により、M1 MacBook Air上にx86版のxv6のビルド環境を整えなくてはならなくなりました。ところが、xv6は3年ほど前にx86版の開発を終了しRISC-V版の開発に移行しており、わざわざM1 Macでx86版をビルドする手法はどこにも載っていません。
というわけで今回は、M1でx86版のxv6をビルドできるようにした経緯を書き残しておきます。
実行環境
- MacBook Air 2020
- Chip: Apple M1
- OS: macOS Monterey 12.4
- RAM: 8GB
- コンパイラ: i386-jos-elf-gcc (GCC) 4.6.1
- エミュレータ: QEMU emulator 6.2.0
方法
arm64たるM1 Macでx86用のプログラムをビルドするのだから当然、通常通りには行きません。そもそもxv6のコンパイルにはクロスコンパイラが必要です。
リポジトリ[1]のREADMEを見てみると、Macではi386-jos-elf-gcc
というクロスコンパイラが推奨されていることが分かります。しかし、このクロスコンパイラはintel Macにしか対応しておらず、M1 Macでは利用できません。
幸い、M1 MacにはRosetta2があります。Rosetta2によりx86_64のバイナリも実行可能になるため、これを利用すればi386-jos-elf-gcc
も動くはずです。
というわけで、まずはx86_64版のhomebrewを手に入れ、x86_64版のhomebrewでx86版のgccクロスコンパイラをインストールし、それを使ってビルドします。今回導入するコンパイラは次の通り。
- i386-jos-elf-gcc
- i386-jos-elf-gdb (デバッグ用)
また、xv6を動かすにはエミュレータが必要ですが、今回はQEMU (qemu-system-i386
)を使用します。QEMUに関してはarm64版で問題ありません。
※注:以下、zshを利用しているものとして進めます。
1. x86_64版のhomebrewを手に入れる
まずはターミナルを起動し、プロセスがRosetta2を通してx86_64のプロセスとして実行されるようにするために以下のコマンドを実行します。
% arch -x86_64 -e PATH= /bin/zsh --login
次にhomebrewをインストールします。
% /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
こうすることで、/usr/local/bin/brew
にx86_64版homebrewがインストールされます。現状、homebrew版はarm64版とx86_64版が以下のように併存している状態です。
アーキテクチャ | パス |
---|---|
arm64 | /opt/homebrew/bin/brew |
x86_64 | /usr/local/bin/brew |
このままにしておくと、zshをx86_64として実行しているとき、PATHに/usr/local/bin/brew
より先に/opt/homebrew/bin/brew
が来てしまうため、arm64版のhomebrewを起動しようとしてしまいます。
そこで、~/.zshrc
の末尾に以下を追記します。
if [ "$(arch)" = "arm64" ]; then
export PATH="/opt/homebrew/bin:$PATH"
else
export PATH="/usr/local/bin:$PATH"
fi
これにより、x86_64版とarm64版で別々のパスが優先され、homebrewが各々のアーキテクチャで使い分けられます。
(x86_64で実行した場合:)
% uname -m
x86_64
% which brew
/usr/local/bin/brew
(arm64で実行した場合:)
% uname -m
arm64
% which brew
/opt/homebrew/bin/brew
2. x86のgccクロスコンパイラをインストール
先述の通り、以下の2つをインストールします。
- i386-jos-elf-gcc
- i386-jos-elf-gdb (デバッグ用)
先程homebrewをインストールしたターミナルで、以下を実行。
% brew tap liudangyi/i386-jos-elf-gcc
% brew install i386-jos-elf-gcc i386-jos-elf-gdb
インストールが完了したら準備完了です。
3. xv6のビルド
まずはxv6をGitHubのリポジトリ[1]からクローン。
% git clone https://github.com/mit-pdos/xv6-public.git
% cd xv6-public
そしてビルド。同時にQEMUで実行もしてみます。
% make qemu
すると….
無事起動!
手前のターミナルと後ろのQEMUで同じコマンド入力画面が2つ表示されてるけど…
リファレンス[2]曰く、どうもmake qemu
ではGUIウィンドウも出てくる仕様みたい。
以下のように、make qemu-nox
でCUIオンリーにできるっぽいです。
% make qemu-nox
すると、こちらも無事動きました。
とりあえずxv6のlsコマンドを実行。
コマンドの実行も問題なさそうです。
ちなみに
今回はzshを% arch -x86_64 -e PATH= /bin/zsh --login
でRosetta2(x86-64)に切り替えて実行しましたが、この操作が必要なのはインストール時のみです。以後は、いちいち切り替えなくてもxv6のビルドと実行が可能です。
おわりに
今回はx86版のxv6をM1 Mac上で起動しましたが、ほぼ同じ流れでxv6-riscvもビルド環境を整えることが可能です(こちらも導入済み)。気が向いたらそちらも記事に書こうと思います。
なお、今回はクロスコンパイラの実行のためにRosetta2を使いましたが、xv6がビルドできるarm64版のgccクロスコンパイラがあればRosetta2がなくてもできる気はします。ただ、i386-jos-elf-gcc
はarm64に対応しておらず、代わりにarm64に対応したx86_64-elf-gcc
で-m32
オプションを追加してコンパイルしたところ、なぜかxv6の起動時に無限ループが発生してしまいました。
まだまだ調査の余地はありそうですが、とりあえずRosetta2でi386-jos-elf-gcc
が問題なく動いてるしいっか、という結論に。いずれにせよ、公式で推奨されていると思われるi386-jos-elf-gcc
を使うに越したことはないと思います。