Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

day03a以降、カーネルの起動まで進まない #134

Closed
mj-hd opened this issue Oct 1, 2022 · 12 comments
Closed

day03a以降、カーネルの起動まで進まない #134

mj-hd opened this issue Oct 1, 2022 · 12 comments

Comments

@mj-hd
Copy link

mj-hd commented Oct 1, 2022

day03a以降、カーネルへ処理を移していると思いますが、この章から先が全て動いておらず、何かご助言いただけないでしょうか。

ソースコード: uchan-nos/mikanos, branch: osbook_day03a

edk2: https://github.com/tianocore/edk2/tree/4ac02962017c77bf38b462f970c884c2dc7931cf (gcc12で動くようこのパッチを当てています)

clang: 14.0.6

カーネルビルドコマンド:

$ clang++ -O2 -Wall -g --target=x86_64-elf -ffreestanding -mno-red-zone -fno-exceptions -fno-rtti -std=c++17 -c main.cpp`
$ ld.lld --entry KernelMain -z norelro --image-base 0x100000 --static -o kernel.elf main.o

QEMU起動コマンド:
$ ./devenv/run_qemu.sh edk2/Build/MikanLoaderX64/DEBUG_CLANG38/X64/Loader.efi kernel/kernel.elf

QEMU起動後の様子:
image

レジスタの値を確認してみると、以下のようになっています(RIPはkernelまで到達できていなそうでした):

(qemu) info registers
RAX=000000003fb7b3e0 RBX=000000003fb79f3b RCX=000000003fb7b3e0 RDX=000000003fea03f8
RSI=0000000000000000 RDI=0000000000101180 RBP=000000003fea87e0 RSP=000000003fea83c0
R8 =0000000000000001 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000
R12=000000003f308198 R13=0000000000000210 R14=000000003fb68234 R15=0000000000000006
RIP=000000003fb73016 RFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     000000003fbee698 00000047
IDT=     000000003f306018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000003fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000500
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=0000000000000000 0000000000000000 XMM01=0000000000000000 0000000000000000
XMM02=0000000000000000 0000000000000000 XMM03=0000000000000000 0000000000000000
XMM04=0000000000000000 0000000000000000 XMM05=0000000000000000 0000000000000000
XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
XMM08=0000000000000000 0000000000000000 XMM09=0000000000000000 0000000000000000
XMM10=0000000000000000 0000000000000000 XMM11=0000000000000000 0000000000000000
XMM12=0000000000000000 0000000000000000 XMM13=0000000000000000 0000000000000000
XMM14=0000000000000000 0000000000000000 XMM15=0000000000000000 0000000000000000
(qemu) x /2i 0x000000003fb73016
0x3fb73016:  48 83 7c 24 40 00        cmpq     $0, 0x40(%rsp)
0x3fb7301c:  74 f8                    je       0x3fb73016

試しに、以下のようにPrint文を仕込んでみると、先ほどとは変わってエラーメッセージが出力されるようになります:

diff --git a/MikanLoaderPkg/Main.c b/MikanLoaderPkg/Main.c
index f7f50be..226af7c 100644
--- a/MikanLoaderPkg/Main.c
+++ b/MikanLoaderPkg/Main.c
@@ -153,13 +153,16 @@ EFI_STATUS EFIAPI UefiMain(

   // #@@range_begin(exit_bs)
   EFI_STATUS status;
+  Print(L"Run First ExitBootServices\n");
   status = gBS->ExitBootServices(image_handle, memmap.map_key);
   if (EFI_ERROR(status)) {
+    Print(L"EFI_ERROR(%d) Run GetMemoryMap\n", status);
     status = GetMemoryMap(&memmap);
     if (EFI_ERROR(status)) {
       Print(L"failed to get memory map: %r\n", status);
       while (1);
     }
+    Print(L"Run Second ExitBootServices\n");
     status = gBS->ExitBootServices(image_handle, memmap.map_key);
     if (EFI_ERROR(status)) {
       Print(L"Could not exit boot service: %r\n", status);

QEMU起動後の様子:
image

@mj-hd
Copy link
Author

mj-hd commented Oct 1, 2022

@uchan-nos
Copy link
Owner

まず、clang と lld のバージョン 7 か 10 でビルドを試してみてもらえないでしょうか。
clang 14 では動作確認をしていませんので。

試しに、以下のようにPrint文を仕込んでみると、先ほどとは変わってエラーメッセージが出力されるようになります:

についてですが、GetMemoryMap と ExitBootServices の間にメモリマップを変化させるような処理をすると ExitBootServices が失敗します。
Print 文はメモリマップを変化させると思いますので、"Could not exit boot service: Invalid Parameter" の原因はそれかなと思います。

@uchan-nos
Copy link
Owner

もし clang 14 でビルドした Loader.efi と kernel.elf を添付していただけたら、手元の QEMU で起動実験をしてみたいと思います。

@mj-hd
Copy link
Author

mj-hd commented Oct 2, 2022

ありがとうございます。こちらにclang14でビルドしたバイナリを上げます。
clang14_artifacts.zip

clang/lddの7を試してみて、動くか確認したいと思います。

@mj-hd
Copy link
Author

mj-hd commented Oct 2, 2022

手元でインストールできるclang10とlld7の組み合わせで、無事kernelに入ることができました!ご協力ありがとうございます。

image

@uchan-nos
Copy link
Owner

@mj-hd
いただいた Loader.efi と kernel.elf を手元の run_qemu.sh で起動させてみたら、確かにご報告のとおりの現象が起きました。

(qemu) stop
(qemu) info registers
RAX=000000003fb7b3e0 RBX=000000003fb79f3b RCX=000000003fb7b3e0 RDX=000000003fea03f8
RSI=0000000000000000 RDI=000000000080201a RBP=000000003fea87e0 RSP=000000003fea83c0
R8 =0000000000000001 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000
R12=000000003f308198 R13=0000000000000210 R14=000000003fb68234 R15=0000000000000006
RIP=000000003fb73016 RFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0

ぱっと見た感じ、レジスタの状態も同じところで止まって(永久ループして)いるようです。

Clang や LLD のバージョンを変えたら挙動が変わったということで、それを手がかりに深掘れば原因を究明できるような気はします。もし mj-hd さんが興味あればサポートしたいと思います。今のところ調査する気は無いということでしたら、Issue をクローズしていただければ。

@mj-hd
Copy link
Author

mj-hd commented Oct 5, 2022

ありがとうございます、空いたタイミングでみてみたいと思います。

@mj-hd
Copy link
Author

mj-hd commented Oct 8, 2022

色々試してみて、kernel.elfのリンカがlld14の場合は起動に失敗、lld7の場合は起動に成功となっているようでした。
逆に、Loader.elf、clangのバージョンは無関係そうでした。(clang/lld14でビルドしたLaoder.elfでも、kernel.elfがlld7でリンクされていれば起動できる。clang14でビルドしたmain.oをlld7でリンクしても起動できる。)

readelf -l をしてみると、エントリポイントと実行可能なLOAD領域の割り当てが違っているようです。(ファイルサイズもlld14の場合は1.9KB、lld7の場合は9.6KBと大きく違います)
image

恐らくこの辺りの問題ではないかなと思っていて、更に調査してみたいと思いますが、この時点で何かヒントなどあれば教えていただけると嬉しいです。

両バージョンでリンクしたkernel.elf: kernel_artifacts.zip

@mj-hd
Copy link
Author

mj-hd commented Oct 8, 2022

すみません、その後、自分の中で結論がでました。

試しにday04dのELFローダの実装後までブートローダを進めてみると、RIPが進んでいるようでした。
image

lld7では、ファイルサイズが大きいことからも分かるようにファイル内のオフセットとロード後のオフセットが一致しているため、(0x00100000 + 24)に記載されてるアドレスに処理を移すだけで動作していました。
lld14では、ファイル上の余分なパディングが消えて一つ目のLOAD領域の直後にエントリーポイントのLOAD領域が保存されています。なので、(0x00100000 + 24)に記載されているアドレスに処理を移しても、ファイル上のオフセットが一致していないため誤作動し、何かしらの例外を引き当てているように思えます。
(無限ループしているアドレスは、メモリマップ上のEfiBootServicesCodeの一部のようですが、何をしているかまでは分かりませんでした)

新しいlldを使用する場合は、day03a時点でELFローダの実装が必要、というのが私の中での結論でした。

この解決策で合っていそうであれば、Closeしたいと思います。

@mj-hd mj-hd closed this as completed Oct 14, 2022
@uchan-nos
Copy link
Owner

@mj-hd 解決おめでとうございます。

おそらくこれと同根の原因だろうとおもいます。
uchan-nos/mikanos@8bd6c4b

カーネルの読み込みも直さないといけないですね。
ただ、いくら最新のバージョンで修正したとしても、osbook_... のタグで実験する人々は依然としてこの問題を経験するので、どうしたものかという感じです。

@Maststu
Copy link

Maststu commented Aug 13, 2023

同じく問題生じたものです.
上記のclang/lldをダウングレードするという方法で解決したと思うのですが,自分の環境(Ubuntu LTS 22.04)ではclang/lldともにバージョン11までしかインストールできなく解決することができませんでした.
解決する方法があれば教えていただきたいです.
closeされたissueに書き込みして申し訳ないです.

@mj-hd
Copy link
Author

mj-hd commented Aug 16, 2023

@Maststu 章を進めて、ELFローダを実装することで正常に動くかと思います

新しいlldを使用する場合は、day03a時点でELFローダの実装が必要、というのが私の中での結論でした。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants