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

tviewをLLVM 10でビルドするとページフォルトが発生する #4

Closed
uchan-nos opened this issue Nov 18, 2020 · 3 comments
Closed
Labels
bug Something isn't working

Comments

@uchan-nos
Copy link
Owner

uchan-nos commented Nov 18, 2020

MikanOSのビルドはLLVM 7(llvm-7, lld-7, clang-7)で動作確認しているが,
ふとした思いでtviewアプリをLLVM 10でビルドしたらカーネル側でページフォルトが起きることが分かった。

カーネルをLLVM 7,tviewをLLVM 10でビルドし,QEMUで起動させ,tview jpn.txt を実行するとページフォルトが発生する。例外時の RIP は 0x14efb4 であった。その周辺の逆アセンブル結果は次。

  14efac:       4c 8b 6e 70             mov    r13,QWORD PTR [rsi+0x70]
  14efb0:       4c 8b 76 78             mov    r14,QWORD PTR [rsi+0x78]
  14efb4:       48 0f c3 47 40          movnti QWORD PTR [rdi+0x40],rax
  14efb9:       4c 0f c3 47 48          movnti QWORD PTR [rdi+0x48],r8
  14efbe:       4c 0f c3 4f 50          movnti QWORD PTR [rdi+0x50],r9

例外が起きたのは movnti 命令。例外が起きたときのスクリーンショットは下図。

Screenshot from 2020-11-18 09-49-43

@uchan-nos uchan-nos added the bug Something isn't working label Nov 18, 2020
@uchan-nos
Copy link
Owner Author

ld.lld-10, clang-10, clang++-10 を使ってビルドすると PF が発生する。
clang/clang++ のバージョンは固定で ld.lld のみ ld.lld-7 を使うようにすると正常に動くようになる。

とりあえず lld-7 と lld-10 でリンクした場合のマップファイルの違いを見てみる。まず注目するのは .text 領域の開始アドレスが lld-7 のときは 0xffff80000000b000,lld-10 になると 32 バイトだけ後方にずれ,0xffff80000000b020 になること。

lld-7 でリンクした場合のマップファイル(.text 開始位置付近):

ffff800000009f28 ffff800000009f28       3c     1         /home/uchan/osbook/devenv/x86_64-elf/lib/libc++abi.a(cxa_noexception.cpp.o):(.eh_frame+0x50)
ffff800000009f68 ffff800000009f68       a4     1         /home/uchan/osbook/devenv/x86_64-elf/lib/libc++abi.a(cxa_noexception.cpp.o):(.eh_frame+0x8c)
ffff80000000b000 ffff80000000b000    2f598    16 .text
ffff80000000b000 ffff80000000b000      b57    16         tview.o:(.text)
ffff80000000b000 ffff80000000b000       ef     1                 MapFile(char const*)
ffff80000000b0f0 ffff80000000b0f0      14d     1                 OpenTextWindow(int, int, char const*)

lld-10 でリンクした場合のマップファイル(.text 開始位置付近):

ffff800000009f28 ffff800000009f28       3c     1         /home/uchan/osbook/devenv/x86_64-elf/lib/libc++abi.a(cxa_noexception.cpp.o):(.eh_frame+0x50)
ffff800000009f68 ffff800000009f68       a4     1         /home/uchan/osbook/devenv/x86_64-elf/lib/libc++abi.a(cxa_noexception.cpp.o):(.eh_frame+0x8c)
ffff80000000b020 ffff80000000b020    2f598    16 .text                          
ffff80000000b020 ffff80000000b020      b57    16         tview.o:(.text)        
ffff80000000b020 ffff80000000b020       ef     1                 MapFile(char const*)
ffff80000000b110 ffff80000000b110      14d     1                 OpenTextWindow(int, int, char const*)

@uchan-nos
Copy link
Owner Author

uchan-nos commented Nov 18, 2020

.text の開始アドレスが 32 バイトだけ後ろになるため,ELF の LOAD セグメントの開始アドレスもずれるようだ。
readelf -l アプリバイナリ とするとプログラムヘッダを一覧できる。その差分を見てみる。

$ diff -y <(readelf -l tview.lld7)  <(readelf -l tview.lld10)

Elf file type is EXEC (Executable file)				Elf file type is EXEC (Executable file)
Entry point 0xffff80000000b880				      |	Entry point 0xffff80000000b8a0
There are 5 program headers, starting at offset 64		There are 5 program headers, starting at offset 64

Program Headers:						Program Headers:
  Type           Offset             VirtAddr           PhysAd	  Type           Offset             VirtAddr           PhysAd
                 FileSiz            MemSiz              Flags	                 FileSiz            MemSiz              Flags
  PHDR           0x0000000000000040 0xffff800000000040 0xffff	  PHDR           0x0000000000000040 0xffff800000000040 0xffff
                 0x0000000000000118 0x0000000000000118  R    	                 0x0000000000000118 0x0000000000000118  R    
  LOAD           0x0000000000000000 0xffff800000000000 0xffff	  LOAD           0x0000000000000000 0xffff800000000000 0xffff
                 0x000000000000a014 0x000000000000a014  R    	                 0x000000000000a014 0x000000000000a014  R    
  LOAD           0x000000000000b000 0xffff80000000b000 0xffff |	  LOAD           0x000000000000a020 0xffff80000000b020 0xffff
                 0x000000000002f598 0x000000000002f598  R E  	                 0x000000000002f598 0x000000000002f598  R E  
  LOAD           0x000000000003b000 0xffff80000003b000 0xffff |	  LOAD           0x00000000000395c0 0xffff80000003b5c0 0xffff
                 0x0000000000003e78 0x0000000000003f10  RW    |	                 0x0000000000003e78 0x0000000000003f0c  RW   
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000	  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000
                 0x0000000000000000 0x0000000000000000  RW   	                 0x0000000000000000 0x0000000000000000  RW   

 Section to Segment mapping:					 Section to Segment mapping:
  Segment Sections...						  Segment Sections...
   00     							   00     
   01     .rodata .eh_frame 					   01     .rodata .eh_frame 
   02     .text 						   02     .text 
   03     .data .data.rel.ro .got .bss 				   03     .data .data.rel.ro .got .bss 
   04     							   04     

Read + Exec の LOAD セグメント(.text 領域が含まれるセグメント)の仮想アドレスが lld-10 では 0xffff80000000b020 になっていることが分かった。仮想アドレスが 4KiB 境界にないことは想定しておらず,そのへんでバグが発生している可能性がある。

@uchan-nos
Copy link
Owner Author

lld-10 で 0x20 だけ後ろにずれる理由を推測する。

注目するのは Read Exec の LOAD セグメントのオフセットが lld-7 は 0xb000 で lld-10 は 0xa020 になっていること。
lld-7 は 4KiB 境界にそろっているが,lld-10 は前のセグメントに詰めて配置される。
おそらくファイル内に余計な空白を作らないためだろう。

ファイル内オフセットが,そのままメモリ上のオフセットに影響している。
ページ(4KiB)単位でコピーができるようにとの配慮だろうか。

uchan-nos added a commit that referenced this issue Dec 1, 2020
lld-10 から ELF の LOAD セクションが 4KiB ページ境界にアラインされなくなった。
そのためページ数の計算にページ内オフセットを加算する必要がある。
uchan-nos added a commit that referenced this issue Jan 31, 2021
lld-10 から ELF の LOAD セクションが 4KiB ページ境界にアラインされなくなった。
そのためページ数の計算にページ内オフセットを加算する必要がある。
imshota pushed a commit to imshota/mikanos that referenced this issue Feb 4, 2021
lld-10 から ELF の LOAD セクションが 4KiB ページ境界にアラインされなくなった。
そのためページ数の計算にページ内オフセットを加算する必要がある。
sozysozbot added a commit to sozysozbot/duvuvurkarpogeschel that referenced this issue Sep 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant