昨日のoopsはこれです.
まず,oopsにある原因のコードの場所を見ます.
[12643.727359] RIP: 0010:[<ffffffffa02a00ae>] [<ffffffffa02a00ae>] count_free+0xae/0x131 [minix]
そうすると,count_free()という関数で落ちていることがわかります.
後ろの0xaeはバグの場所で,0x131は関数のサイズです.
count_free()の0xaeバイト目のコードが落ちた場所ということになります.
count_free()はfs/minix/bitmap.cとmm/slub.cの2ヶ所にあるのですが,[minix]から,bitmap.cだろうと言う感じです.
そしたら,bitmap.oを調べます.使うのはobjdumpです.
[masami@moonlight:~/linux/.../fs/minix]% objdump -D bitmap.o | less bitmap.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <count_free>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 41 56 push %r14 〜中略〜 a4: 44 89 c1 mov %r8d,%ecx a7: 48 8b 5f 28 mov 0x28(%rdi),%rbx ab: 41 ff c0 inc %r8d ae: 8a 0c 0b mov (%rbx,%rcx,1),%cl b1: 88 cb mov %cl,%bl b3: 83 e1 0f and $0xf,%ecx b6: c0 eb 04 shr $0x4,%bl 〜略〜
これで,オフセット0xaeのコードは「 mov (%rbx,%rcx,1),%cl」ということがわかりました.
mov (%rbx,%rcx,1),%clは,1rbxで示されるベース・アドレスの%rcx*1バイト目のデータを%clにmovすると言う意味です.
ちなみに,oopsのCodeで表示されているバイト列は,0xae前後のコードです.
この情報を元に,cで書かれたコードを探します.
bitmap.oで見ると,0xbdバイト目の00 00 00ですが,oopsで表示されている内容の最後3バイトは60 3f 2aです.
これは,リンク時に設定された値になっていると思います.
bd: 03 04 9d 00 00 00 00 add 0x0(,%rbx,4),%eax
まずは,目印になる関数などを探すわけですが,その前にどんな関数か見ておきます.
static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, __u32 numbits) { unsigned i, j, sum = 0; struct buffer_head *bh; for (i=0; i<numblocks-1; i++) { if (!(bh=map[i])) return(0); for (j=0; j<bh->b_size; j++) sum += nibblemap[bh->b_data[j] & 0xf] + nibblemap[(bh->b_data[j]>>4) & 0xf]; } if (numblocks==0 || !(bh=map[numblocks-1])) return(0); i = ((numbits - (numblocks-1) * bh->b_size * 8) / 16) * 2; for (j=0; j<i; j++) { sum += nibblemap[bh->b_data[j] & 0xf] + nibblemap[(bh->b_data[j]>>4) & 0xf]; } i = numbits%16; if (i!=0) { i = *(__u16 *)(&bh->b_data[j]) | ~((1<<i) - 1); sum += nibblemap[i & 0xf] + nibblemap[(i>>4) & 0xf]; sum += nibblemap[(i>>8) & 0xf] + nibblemap[(i>>12) & 0xf]; } return(sum); }
関数の呼び出しとかが無いので,あまり分かりやすい目印がないのですが・・・
この関数だと,return 0;してる箇所が2個あるので,2個目のreturn文を探しました.
この他にも,0xfとandを取っている部分も目印になります.
0を返すなら,xorでeaxを0にするところがあるだろうというのは想像がつくので,
これを探すと,アドレス0x126が,その部分ってわかります.
124: eb 02 jmp 128 <count_free+0x128> 126: 31 c0 xor %eax,%eax 128: 5b pop %rbx 129: 41 5c pop %r12 12b: 41 5d pop %r13 12d: 41 5e pop %r14 12f: c9 leaveq 130: c3 retq
そして,0x126に飛ぶ処理を探すと,0x82にあるje命令がまさにアドレス0x126を指定してます.
72: 0f 84 ae 00 00 00 je 126 <count_free+0x126> 78: 45 89 d8 mov %r11d,%r8d 7b: 4a 8b 3c c7 mov (%rdi,%r8,8),%rdi 7f: 48 85 ff test %rdi,%rdi 82: 0f 84 9e 00 00 00 je 126 <count_free+0x126> <--- return 0を実行する部分 88: 89 d6 mov %edx,%esi 8a: 48 8b 4f 20 mov 0x20(%rdi),%rcx
落ちた場所は0xaeなので,このreturn文より後であることがわかります.
この後の処理で分かりやすいのは,j=0の部分なので,こいつを探します.
8e: 48 c1 e1 03 shl $0x3,%rcx 92: 49 0f af c8 imul %r8,%rcx 96: 45 31 c0 xor %r8d,%r8d <--- j=0
そうすると,0x96にxor命令がいるので,こいつがj=0の実行箇所ですね.
inc命令もいるので,間違いなさそうです.
99: 48 29 ce sub %rcx,%rsi 9c: 48 c1 ee 04 shr $0x4,%rsi a0: 01 f6 add %esi,%esi a2: eb 27 jmp cb <count_free+0xcb> a4: 44 89 c1 mov %r8d,%ecx a7: 48 8b 5f 28 mov 0x28(%rdi),%rbx ab: 41 ff c0 inc %r8d <--- j++
inc命令の次が,例の0xaeです.
cのコードだとこの中のどれかです.
sum += nibblemap[bh->b_data[j] & 0xf] + nibblemap[(bh->b_data[j]>>4) & 0xf];
アセンブラの命令は「mov (%rbx,%rcx,1),%cl」です.
また, nibblemapはint型の配列です.nibblemapのオフセットを求めて,clレジスタに設定したいというのが,命令の内容のようです.
static const int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 };
命令に使ってるrcxレジスタはjの値です.これは0xa4でr8dレジスタの値を,ecxレジスタにコピーしているので簡単にわかります.
99: 48 29 ce sub %rcx,%rsi 9c: 48 c1 ee 04 shr $0x4,%rsi a0: 01 f6 add %esi,%esi a2: eb 27 jmp cb <count_free+0xcb> a4: 44 89 c1 mov %r8d,%ecx a7: 48 8b 5f 28 mov 0x28(%rdi),%rbx ab: 41 ff c0 inc %r8d <--- j++
では,rdxレジスタは?ということですが,これはbh->b_dataです.
rdxレジスタにbh->b_dataが設定されているのは以下の場所です.
ソースコードとアセンブラで設定する位置が違いますが,これは最適化のためと思います.
a7: 48 8b 5f 28 mov 0x28(%rdi),%rbx
struct buffer_headはinclude/linux/buffer_head.hで定義されている構造体です.
struct buffer_head { unsigned long b_state; /* buffer state bitmap (see above) */ struct buffer_head *b_this_page;/* circular list of page's buffers */ struct page *b_page; /* the page this bh is mapped to */ sector_t b_blocknr; /* start block number */ size_t b_size; /* size of mapping */ char *b_data; /* pointer to data within the page */ struct block_device *b_bdev; bh_end_io_t *b_end_io; /* I/O completion */ void *b_private; /* reserved for b_end_io */ struct list_head b_assoc_buffers; /* associated with another mapping */ struct address_space *b_assoc_map; /* mapping this buffer is associated with */ atomic_t b_count; /* users using this buffer_head */ };
ウチのPCは64bitなので,8バイトアラインで数えると構造体の先頭からb_dataまでは0x28(40)バイトです.
他の場所でb_sizeにアクセスしてる場所では,0x20がオフセットになっています.
8a: 48 8b 4f 20 mov 0x20(%rdi),%rcx
そんな訳で,「mov (%rbx,%rcx,1),%cl」はbh->b_data[j]であることが分かりました(∩´∀`)∩ワーイ
ae: 8a 0c 0b mov (%rbx,%rcx,1),%cl <--- bh->b_data[j] b1: 88 cb mov %cl,%bl b3: 83 e1 0f and $0xf,%ecx b6: c0 eb 04 shr $0x4,%bl b9: 48 0f be db movsbq %bl,%rbx bd: 03 04 9d 00 00 00 00 add 0x0(,%rbx,4),%eax
これでバグの場所自体はわかったんですが,どういう状況だったのかは,テストケースとテストデータ調べないとわかりませんorz
なので,まだまだ直すところまではいけません・・・