読者です 読者をやめる 読者になる 読者になる

cのコードがどんな感じのアセンブラコードに落ちるのか確認

debug

テスト環境はこんな感じです。

[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>