LoginSignup
174
141

More than 1 year has passed since last update.

Mac で始める「ゼロからのOS自作入門」

Last updated at Posted at 2021-03-23

Mac で「ゼロからのOS自作入門」の「8.7 メモリ管理に挑戦」まで進められたので、ひとまずそこまでの手順をメモとして残しておきます。この環境構築方法で本の最後まで進められるかどうかはまだわかりません。

環境は macOS BigSur(11.4), CPU は Intel でも Apple Silicon でも OK です。Homebrew はすでにインストールされているものとします。

PATH にいろいろ追加していきますが、適宜 .zshrc 等の PATH の設定にも追加してください。

EDK II の準備

後でインストールする binutils がすでにインストールされ PATH に追加されていると失敗するので注意してください。

$ cd $HOME
$ git clone https://github.com/tianocore/edk2.git
$ cd edk2
$ git checkout 38c8be123aced4cc8ad5c7e0da9121a181b94251
$ git submodule init
$ git submodule update
$ cd BaseTools/Source/C
$ make

mikanos-build リポジトリの準備

$ cd $HOME
$ git clone https://github.com/uchan-nos/mikanos-build.git osbook
$ cd osbook/devenv
$ curl -L https://github.com/uchan-nos/mikanos-build/releases/download/v2.0/x86_64-elf.tar.gz | tar xz
$ cd ..

次の内容の mac.patch というファイルを作って適用します。

--- a/devenv/make_image.sh
+++ b/devenv/make_image.sh
@@ -23,11 +23,24 @@ qemu-img create -f raw $DISK_IMG 200M
 mkfs.fat -n 'MIKAN OS' -s 2 -f 2 -R 32 -F 32 $DISK_IMG

 $DEVENV_DIR/mount_image.sh $DISK_IMG $MOUNT_POINT
-sudo mkdir -p $MOUNT_POINT/EFI/BOOT
-sudo cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
+if [ `uname` = 'Darwin' ]; then
+    mkdir -p $MOUNT_POINT/EFI/BOOT
+    cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
+else
+    sudo mkdir -p $MOUNT_POINT/EFI/BOOT
+    sudo cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
+fi
 if [ "$ANOTHER_FILE" != "" ]
 then
-    sudo cp $ANOTHER_FILE $MOUNT_POINT/
+    if [ `uname` = 'Darwin' ]; then
+        cp $ANOTHER_FILE $MOUNT_POINT/
+    else
+        sudo cp $ANOTHER_FILE $MOUNT_POINT/
+    fi
 fi
 sleep 0.5
-sudo umount $MOUNT_POINT
+if [ `uname` = 'Darwin' ]; then
+    hdiutil detach $MOUNT_POINT
+else
+    sudo umount $MOUNT_POINT
+fi
diff --git a/devenv/mount_image.sh b/devenv/mount_image.sh
index ba8233e..aea4d7d 100755
--- a/devenv/mount_image.sh
+++ b/devenv/mount_image.sh
@@ -16,5 +16,9 @@ then
     exit 1
 fi

-mkdir -p $MOUNT_POINT
-sudo mount -o loop $DISK_IMG $MOUNT_POINT
+if [ `uname` = 'Darwin' ]; then
+    hdiutil attach -mountpoint $MOUNT_POINT $DISK_IMG
+else
+    mkdir -p $MOUNT_POINT
+    sudo mount -o loop $DISK_IMG $MOUNT_POINT
+fi
$ patch -p1 < mac.patch

QEMU のインストール

Homebrew でインストールします。

$ brew install qemu

もし、 Apple Silicon Mac で qemu をソースコードからビルドする場合は必ず ver 6.0 以降を使用してください。

LLVM のインストール

本では LLVM7 を使っていますが、Mac で EDK II を使うためには LLVM9 以上が必要です。homebrewで最新版(2021 年 5 月時点で 12)をつかうことにします。

$ brew install llvm

# Intel Mac の場合
$ export PATH=/usr/local/opt/llvm/bin:$PATH

# Apple Silicon Mac の場合
$ export PATH=/opt/homebrew/opt/llvm/bin:$PATH

その他のビルドに必要なパッケージのインストール

$ brew install nasm dosfstools binutils

# Intel Mac の場合
$ export PATH=/usr/local/opt/binutils/bin:$PATH

# Apple Silicon Mac の場合
$ export PATH=/opt/homebrew/sbin:/opt/homebrew/opt/binutils/bin:$PATH

「2.2 EDK II でハローワールド」をビルドする

$ cd $HOME
$ mkdir workspace
$ cd workspace
$ git clone https://github.com/uchan-nos/mikanos.git
$ cd mikanos
$ git checkout osbook_day02a
$ cd $HOME/edk2
$ ln -s $HOME/workspace/mikanos/MikanLoaderPkg .
$ source edksetup.sh

Conf/target.txt を開き次のように変更します。TOOL_CHAIN_TAG が本とは異なるので注意してください。CLANG38 ツールチェインは Linux でしか使えないため代わりに CLANGPDB ツールチェインを使用します。

設定項目 設定値
ACTIVE_PLATFORM MikanLoaderPkg/MikanLoaderPkg.dsc
TARGET DEBUG
TARGET_ARCH X64
TOOL_CHAIN_TAG CLANGPDB

変更を保存したらビルドします。

$ build

$HOME/edk2/Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi というファイルが作られれば成功です。

QEMU で実行

次のようにして QEMU を実行します。

$ $HOME/osbook/devenv/run_qemu.sh $HOME/edk2/Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi

うまくいくと QEMU が起動し、さっきビルドしたプログラムが実行されるはずです。

スクリーンショット 2021-03-23 23.45.51.png

「3.4 ブートローダからピクセルを描く」の補足

Linux では標準のシェルとして bash が使われているのに対し、Mac では zsh が使われています。変数に含まれるスペースの扱いが zsh と bash では異なるため、bash を前提としている本の

$ clang++ $CPPFLAGS -O2 --target=x86_64-elf -fno-exceptions -ffreestanding -c main.cpp

ではコンパイルできません。代わりに

$ clang++ ${=CPPFLAGS} -O2 --target=x86_64-elf -fno-exceptions -ffreestanding -c main.cpp

としてください。

「3.5 カーネルからピクセルを描く」の補足

『「2.2 EDK II でハローワールド」をビルドする』で TOOL_CHAIN_TAG に本で指定している CLANG38 ではなく、CLANGPDB を指定しました。CLANG38 ツールチェインは System V AMD64 ABI を使用しますが、CLANGPDB ツールチェインは Microsoft x64 ABI を使用します。(ABI については本の「コラム 4.1 ABI」を参照してださい。)このため System V AMD64 ABI で引数が渡ってくることを想定しているカーネルの KernelMain に引数を渡せません。そこで本のリスト 3.8 の

typedef void EntryPointType(UINT64, UINT64);

typedef void __attribute__((sysv_abi)) EntryPointType(UINT64, UINT64);

にすると、強制的に System V AMD64 ABI でカーネルを呼び出すことができ、引数を渡せます。

本とは異なり「LLVMのインストール」で LLVM の最新版を使うようにしました。LLVM 10 以降では -z separate-code というオプションをつけてリンクしないと本書のブートローダでロードすることができません。(「ゼロからのOS自作入門」 03日目 を参考にさせていただきました。ありがとうございます。)

本の

$ ld.lld $LDFLAGS --entry KernelMain -z norelro --image-base 0x100000 --static -o kernel.elf main.o

の代わりに

$ ld.lld $LDFLAGS --entry=KernelMain -z norelro --image-base 0x100000 --static -o kernel.elf -z separate-code main.o

とします。

「4.1 make 入門」の補足

Makefile の LDFLAGS の行を変更し、-z separate-code をつけてリンクするようにします。

LDFLAGS += --entry KernelMain -z norelro --image-base 0x100000 --static -z separate-code

「5.2 分割コンパイル」の補足

Makefile で sed を使うようになりますが、Linux の sed と Mac の sed はコマンドラインオプションが異なります。sed を使う行は

    sed -I '' -e 's|$(notdir $(OBJ))|$(OBJ)|' $@

にする必要があります。

また本とは使用するツールチェインが異なるためだと思いますが、 make を実行すると

ld.lld: error: undefined symbol: __cxa_pure_virtual
>>> referenced by graphics.cpp
>>>               graphics.o:(vtable for PixelWriter)
make: *** [kernel.elf] Error 1

というエラーが出るようになります。完全仮想関数を呼び出そうとしてしまった場合に呼ばれるエラーハンドラの実装を提供する必要があるそうです。(OS 自作に便利な C++ の機能 10 選(前編))

main.cpp に次のような実装を書いておくとビルドできるようになります。

extern "C" void __cxa_pure_virtual() {
  while (1) __asm__("hlt");
}
174
141
17

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
174
141