テスト環境はこんな感じです。
[masami@moon:~/build/debug_test]% uname -a Linux moon 2.6.30-rc7-x86tip-tip #1 SMP Tue May 26 23:41:24 JST 2009 x86_64 GNU/Linux [masami@moon:~/build/debug_test]% gcc -v Using built-in specs. Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.3-10' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 4.3.3 (Debian 4.3.3-10)
cのソースはこんな感じで。特に大した処理はしてません。。
単にcのソースがどんなアセンブラコードになるのか見たかっただけです。
static void *do_something(void *arg) { int i = *(int *) arg; char *p = NULL; if (i == 7) strcpy(p, "test"); else p = "foobar"; printf("%s\n", p); return NULL; } int main(int argc, char **argv) { pthread_t th[10]; int i; for (i = 0; i < sizeof(th) / sizeof(th[0]); i++) pthread_create(&th[i], NULL, &do_something, &i); for (i = 0; i < sizeof(th) / sizeof(th[0]); i++) pthread_join(th[i], NULL); puts("Done\n"); return 0; }
上のコードを-O2と-Sをつけてアセンブラのソースを吐かせるとdo_something()はこんな感じに。
.LC1: .string "foobar" .text .p2align 4,,15 .type do_something, @function do_something: .LFB26: subq $8, %rsp .LCFI3: cmpl $7, (%rdi) movl $.LC1, %eax je .L12 .L10: movq %rax, %rdi call puts xorl %eax, %eax addq $8, %rsp ret .p2align 4,,10 .p2align 3 .L12: movl $1953719668, 0 movb $0, 4 xorl %eax, %eax jmp .L10 .LFE26: .size do_something, .-do_something .section .eh_frame,"a",@progbits
cソースの
int i = *(int *) arg;
が消されて、直接引数の値を使ってif文を行うようになってます。
cmpl $7, (%rdi)
Debug HasksのP65によるとx86_86環境では1番目の引数はrdiレジスタに置かれるとあるので、
rdiレジスタが指すポインタの値と7を比較してます。
次の行ではLC1のところにあるデータをeaxに格納してます。これはiが7以外の場合の処理。
movl $.LC1, %eax
これは、その後のputs()呼び出し(cのコードではprintfだけど、最適化でputs()に変更されてる)の準備。
そして、比較結果を確認。i==7ならラベル.L12にジャンプ。
je .L12
cのソースと順番が変わりますが、iが7でない場合はラベル.L10に進みます。
この.L10はラベル.L12からもジャンプされてきます。
.L10: movq %rax, %rdi call puts xorl %eax, %eax addq $8, %rsp ret .p2align 4,,10 .p2align 3 .L12: movl $1953719668, 0 movb $0, 4 xorl %eax, %eax jmp .L10
もし、iが7以外だった場合は、すでにeaxレジスタにputs()の引数になる文字列が設定されているので、
それをrdiレジスタにコピーして(puts()の一番目の引数なので)、puts()を呼び出します。
このdo_somethingはNULLを返すので、eaxレジスタをクリアしてretで呼び出し元に返る処理となってます。
i==7の場合は.L12にとんで、eaxレジスタに値を入れて、.L10に飛ぶんですが、
このサンプルだと最適化が入っているようで、eaxをxorl命令でクリアしてます。
サンプルをコンパイルしたバイナリをodコマンドで見てみるとこんな感じでした。
0000000000400660 <do_something>: 400660: 48 83 ec 08 sub $0x8,%rsp 400664: 83 3f 07 cmpl $0x7,(%rdi) 400667: b8 92 07 40 00 mov $0x400792,%eax 40066c: 74 12 je 400680 <do_something+0x20> 40066e: 48 89 c7 mov %rax,%rdi 400671: e8 32 fe ff ff callq 4004a8 <puts@plt> 400676: 31 c0 xor %eax,%eax 400678: 48 83 c4 08 add $0x8,%rsp 40067c: c3 retq 40067d: 0f 1f 00 nopl (%rax) 400680: c7 04 25 00 00 00 00 movl $0x74736574,0x0 400687: 74 65 73 74 40068b: c6 04 25 04 00 00 00 movb $0x0,0x4 400692: 00 400693: 31 c0 xor %eax,%eax 400695: eb d7 jmp 40066e <do_something+0xe>