OS作成 - ファイルシステム編
よくわからないバグに遭遇。
関数定義だけはしているが他から一切呼ばれていない関数がコンパイル後に悪さをしている。。
具体的に言うと以下が問題の部分。
PUBLIC void __change_parentdir(ext3_dentry* parent, ext3_dentry* child) { char* p = parent->d_dirblock; char* prev = p; u_int16_t len, sumlen; len = sumlen = 0; while (sumlen < BLOCK_SIZE) { prev = p; len = ((u_int16_t*)(p+4))[0]; p += len; sumlen += len; } }
という関数があり、この関数自体はどこからも呼ばれていないのにコンパイルするとkernelがbootm.binあたりの動作で停止している。さらに細かくみていくと
len = ((u_int16_t*)(p+4))[0];
のみをコメントアウトすると正常にいくのでどうやらこれが悪さをしている模様。
どうゆうことだろう。。ということでこれをobjdumpで逆アセンブルした所をみてみる
390f: 55 push %ebp 3910: 89 e5 mov %esp,%ebp 3912: 83 ec 10 sub $0x10,%esp 3915: 8b 45 08 mov 0x8(%ebp),%eax 3918: 8b 40 30 mov 0x30(%eax),%eax 391b: 89 45 f4 mov %eax,0xfffffff4(%ebp) 391e: 8b 45 f4 mov 0xfffffff4(%ebp),%eax 3921: 89 45 f8 mov %eax,0xfffffff8(%ebp) 3924: 66 c7 45 fe 00 00 movw $0x0,0xfffffffe(%ebp) 392a: 0f b7 45 fe movzwl 0xfffffffe(%ebp),%eax 392e: 66 89 45 fc mov %ax,0xfffffffc(%ebp) 3932: eb 22 jmp 0x3956 3934: 8b 45 f4 mov 0xfffffff4(%ebp),%eax 3937: 89 45 f8 mov %eax,0xfffffff8(%ebp) 393a: 8b 45 f4 mov 0xfffffff4(%ebp),%eax 393d: 83 c0 04 add $0x4,%eax 3940: 0f b7 00 movzwl (%eax),%eax 3943: 66 89 45 fc mov %ax,0xfffffffc(%ebp) 3947: 0f b7 45 fc movzwl 0xfffffffc(%ebp),%eax 394b: 01 45 f4 add %eax,0xfffffff4(%ebp) 394e: 0f b7 45 fc movzwl 0xfffffffc(%ebp),%eax 3952: 66 01 45 fe add %ax,0xfffffffe(%ebp) 3956: 66 81 7d fe ff 0f cmpw $0xfff,0xfffffffe(%ebp) 395c: 76 d6 jbe 0x3934 395e: c9 leave 395f: c3 ret
ここでespはスタックポインタでebpにそれをコピーしており、espから0x10を引くことにより16バイト分のローカル変数領域を確保している。0xfffffffe(%ebp)などはgccがローカル変数としてスタック上に用意したものでローカル変数の詳しい対応を見てみると
char* p | char* prev | u_int16_t len | u_int16_t sumlen |
-12(%ebp) | -8(%ebp) | -4(%ebp) | -2(%ebp) |
0xfffffff4(%ebp) | 0xfffffff8(%ebp) | 0xfffffffc(%ebp) | 0xfffffffe(%ebp) |
となってる、これより問題の1行は
393a: 8b 45 f4 mov 0xfffffff4(%ebp),%eax 393d: 83 c0 04 add $0x4,%eax 3940: 0f b7 00 movzwl (%eax),%eax 3943: 66 89 45 fc mov %ax,0xfffffffc(%ebp)
となってる。
pのアドレスを0xfffffff4(%ebp)から%eaxにコピーし、add $0x4, %eaxでp+4とし、それをメモリ上のアドレスとした値をもう一度%eaxにコピーし、最後に0xfffffffc(%ebp)であるlenにコピーする。
うーん。。問題無さそう。カーネルはメモリ上の0x1000から配置しているのでカーネルの容量増大が0x90000に配置したbootm.binを圧迫してるのかとも思ったけど、それならbootm.bin自体のコードが全く動かないはずだけどそれはないし、カーネル自体のサイズもまだ0x3C000しかないから問題ない。
あとはスタックの関係だろうか。。
追記 問題の箇所が違った
もっとコメントアウトしないと正常動作しない
追記
以下のコードを同じファイルに入れただけでおかしくなった
void hoge() { int i = 0; while (i < 4096) { i++; } }
どうやらアセンブラ的に「jmp」するコードが入るとおかしくなる。jmp先のアドレスが正常にリンクされてなさそう。
追記
どうやらkernel.binのサイズが191800Byte以上になるとメモリ上の0x9036bから0x90374までが上書きされてそこから挙動がおかしくなっている模様。なぜそんな事になってるかはまだ不明。
根が深いバグだ。。なんでこんな所のしかも10バイトだけが上書きされるんだ。。