環境
- Host
- Mac OSX Yosemite 10.10.3
- Vagrant 1.7.2
- VirtualBox 4.3.26
- Guest
- Ubuntu 14.04 on vagrant (chef/ubuntu-14.04)
- qemu 1.7.91
- gcc 4.8.2
- gdb-multiarch 7.7
ブートの流れと gdb で素直に追えない理由
64bit 版の boot loader は memory space を 16 -> 32 -> 64 と拡張しながら起動していくみたいです。
切り替えの際に命令セットが変わるので、 16bit(i8086) で attach していた gdb で b main して continute しても main で止まらなかったり、アーキテクチャの設定を間違えると gdb が文句を言ったりします。
この問題は swetland さんの xv6 の README.64bit にも書かれていて、
とのこと。
* gdb pukes when qemu switches from 32bit to 64bit mode
* this made debugging the mode change entertaining
* for now attach gdb after the switch
要は切り替えると gdb がダメになるので、切り替えた後に attach しなよ、と。
この切り替えポイントと gdb の attach をどうするか格闘したログ
16bit -> 32bit
boot 部分はアセンブラで直書きされているのでその辺りを読む。
out/bootblock.asm にある
でどうやら 16bit -> 32bit している様子。ff 53 1c call *0x1c(%ebx)
なのでこの命令がある 0x7dc1 に break を置く。
ここは起動直後の gdb での continue で止まる。
stepi すると 0x00100020 に飛ぶ。
ちなみに 0x00100020 は mboot_entry ってやつで、ページテーブルの初期化とかしてるみたいです。
これで 32bit になったので 64bit に飛ぶところを探す。32bit -> 64bit
メモリが32bitになったので 0x1000a4 とかが指定できる。
64bit mode にしてる部分は out/kernel.asm の
# shift to 64bit segment
のところみたいです。ljmp $8,$(entry64low - mboot_header + mboot_load_addr)
私の環境のアドレスだと 0x1000a4 。
ここから stepi すると
と言われて gdb で操作できなくなる。Remote 'g' packet reply is too long: 000200000000000000000.....
直前に set architecture i386:x86-64 とかしてもダメ。
ということで、 16 -> 32 した gdb はそのままで新しい gdb を上げる。
新しい gdb で symbol-file と set arch してから target remote すると gdb を待つ状態になるので、古い gdb で continue してエラーが出てから quit 。
古い gdb は落ちて新しいの 64bit で繋がるので、これで b main とかが効くようになります。
Ubuntu 14.04 に環境を作る
- sudo apt-get install -y gcc git qemu gdb-multiarch vim zsh
- git clone https://github.com/swetland/xv6.git xv6-64bit
とか。
Makefile の QEMU を qemu-system-x86_64 にして、64bit build の時は X64 って環境変数を作る。
gdb で debug する時は tool/gdbinit.tmpl の symbol-file kernel を symbol-file/kernel.elf にする。
あとは
- make
- make qemu-nox-gdb
してから gdb-multiarch でいけます。
最初の gdb-multiarch の .gdbinit は
2つ目の gdb-multiarch の .gdbinit は
最初の gdb-multiarch の .gdbinit は
echo + target remote localhost:25900\ntarget remote localhost:25900とか。32bit mode での最後の ljmp までは2つめの gdb-multiarch でいけます。
echo + symbol-file out/kernel.elf\nsymbol-file out/kernel.elf
break *0x7dc1continuestepibreak *0x1000a4
echo + please attach another gdb.\n
2つ目の gdb-multiarch の .gdbinit は
set architecture i386:x86-64:intel
echo + target remote localhost:25900\ntarget remote localhost:25900
echo + symbol-file out/kernel.elf\nsymbol-file out/kernel.elf
とか。 2つ目を接続してから1つ目を continue すると良い。
xv6 を fork してみる
結局やることは
- .gdbinit の書き換え
- 16 -> 32 bit 版 gdb 用 .gdbinit
- 32 -> 64 bit 版 gdb 用 .gdbinit
- QEMU の設定
- X64 を設定
とか沢山あったので、 fork してみました。
3つ shell を上げて
- (1) make
- (1) make qemu-nox-gdb
- (2) gdb-multiarch -x .gdbinit64
- please attach another gdb と出たら
- (3) gdb-multiarch -x .gdbinit64-2
- (2) continue
- (2) exit
- すると (3) の gdb が繋がるので
- (3) b main
- (3) continue
- とかができます。
(3) の gdb の attach 時に 'Bogus trace status reply from target: PacketSize=1000' と出る場合もありますが、もう一度 $ gdb-multiarch -x .gdbinit64-2 すると繋がります。
メモリがきちんと64bitになっていればok。
とかですね。The target architecture is assumed to be i386:x86-64:intel+ target remote localhost:259000x00000000001000e0 in ?? ()+ symbol-file out/kernel.elf(gdb)
あとは main から読むなりなんなりできます。
コマンドまとめ
vagrant 上の Ubuntu 14.04 にて 3 つ shell を上げます。それぞれが(1), (2), (3) です。
(1) $ sudo apt-get install -y gcc git qemu gdb-multiarch vim zsh
(1) $ git clone https://github.com/atton-/xv6.git xv6-64bit
(1,2,3) $ cd xv6-64bit
(1) $ make
(1) $ make qemu-nox-gdb
(2) $ gdb-multiarch -x .gdbinit64
please attach と出たら
(3) $ gdb-multiarch -x .gdbinit64-2
(2) $ continue
して Remote 'g' packet reply is too long: 000200000000000000000.....
と言われると思うので(2) の gdb を落とす
(3) $ b main
(3) $ continue
main で止まります。
(3) の attach に失敗したら (3) をもう一度 gdb-multiarch -x .gdbinit64-2 で上げると良いはずです。
Thanks for your sharing! I merged your GDB work to my Xv6 source tree: https://github.com/jserv/xv6-x86_64 .
返信削除