Page List

Search on the blog

2011年11月10日木曜日

アセンブラを出力して遊ぶ

簡単なプログラムをアセンブラで出力して、スタックの動きを追ってみます。簡単なプログラムですが、結構面白いです。環境は、Ubuntu、gcc 4.4.3です。

 まず、Cのソースコード(test.c)。

  1. #include <stdio.h>  
  2.   
  3. main()  
  4. {  
  5.   int i, j, k;  
  6.   
  7.   i = 1;  
  8.   j = 2;  
  9.   k = i + j;  
  10.   printf("%d %d %d\n", i, j, k);  
  11. }  


 そして、アセンブラ(test.s)。
$ gcc -S test.c
でアセンブラのコードを出力できます。

  1.     .file    "test.c"  
  2.     .section    .rodata  
  3. .LC0:  
  4.     .string    "%d %d %d\n"  
  5.     .text  
  6. .globl main  
  7.     .type    main, @function  
  8. main:  
  9.     pushl    %ebp  
  10.     movl    %esp, %ebp  
  11.     andl    $-16, %esp  
  12.     subl    $32, %esp  
  13.     movl    $1, 28(%esp)  
  14.     movl    $2, 24(%esp)  
  15.     movl    24(%esp), %eax  
  16.     movl    28(%esp), %edx  
  17.     leal    (%edx,%eax), %eax  
  18.     movl    %eax, 20(%esp)  
  19.     movl    $.LC0, %eax  
  20.     movl    20(%esp), %edx  
  21.     movl    %edx, 12(%esp)  
  22.     movl    24(%esp), %edx  
  23.     movl    %edx, 8(%esp)  
  24.     movl    28(%esp), %edx  
  25.     movl    %edx, 4(%esp)  
  26.     movl    %eax, (%esp)  
  27.     call    printf  
  28.     leave  
  29.     ret  
  30.     .size    main, .-main  
  31.     .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"  
  32.     .section    .note.GNU-stack,"",@progbits  

 いろいろ分かることがあります。
 まず、スタックのアドレスが番地の大きい方から小さいほうへと広がっているところです。これはx86マシンでは共通だと昔聞いたことがあります。
 それから、andl $-16, %esp があるので、16byteのaddress alignmentを使っているようです。FFFFFFF0でマスキングして16の倍数になる次の数をスタックポインタに代入しています。これだとベースポインタとスタックポインタの整合性があわないようにみえるんですけど、結局ベースポインタ使ってないし、リターンする時ベースポインタをスタックポインタに戻す処理も省略されてるので別にいいような気もします。
 意外だったのは、スタック内の変数にスタックポインタからの差分でアクセスしているところです。スタックポインタは実行時に移動するから、ベースポインタからの差分で変数にアクセスするのが普通かと思いましたが、そうでもないみたいです。
 あと、最後にprintfの可変長引数のところの仕組みがおもしろいです。第一引数の%dが3つあるんで、第一引数をpopしたあと、3回popすればいいんですねー。当然といえば当然ですが、実際コード上でみるとちょっとした感動がありました。printfがcallされるときのstackの状態は下のようになっています。



0 件のコメント:

コメントを投稿