前回C言語のプログラムをディスアセンブルしたソースを参考に、アセンブラを埋め込みました。関数の引数に割り当たるレジスタもおぼろげながら分かってきました。
ポインタ引数の場合、どのレジスタに割り当たるのでしょうか。
配列コピーのプログラムを作って確認してみます。
takk@deb9:~/tmp$ cat -n t3.c 1 #include <stdio.h> 2 #include <string.h> 3 4 void func2(char* a, char* b, int c) 5 { 6 memcpy(b,a,c); 7 } 8 9 int main() 10 { 11 char a[10]; 12 char b[10]; 13 a[0] = 10; 14 a[1] = 20; 15 a[2] = 30; 16 17 func2(a, b, 3); 18 19 printf("b[0]=%d, b[1]=%d b[2]=%d\n",b[0],b[1],b[2]); 20 } takk@deb9:~/tmp$
func2(a,b,c)は、配列aを配列bにcの数分コピーする処理です。
ビルドして実行します。
takk@deb9:~/tmp$ gcc t3.c takk@deb9:~/tmp$ ./a.out a[0]=10, a[1]=20 a[2]=30 takk@deb9:~/tmp$
想定通りですね。
ではディスアセンブル。
takk@deb9:~/tmp$ objdump -d a.out | cat -n
func2のアセンブラを見てみましょう。
134 0000000000000710 <func2>: 135 710: 55 push %rbp 136 711: 48 89 e5 mov %rsp,%rbp 137 714: 48 83 ec 20 sub $0x20,%rsp 138 718: 48 89 7d f8 mov %rdi,-0x8(%rbp) 139 71c: 48 89 75 f0 mov %rsi,-0x10(%rbp) 140 720: 89 55 ec mov %edx,-0x14(%rbp) 141 723: 8b 45 ec mov -0x14(%rbp),%eax 142 726: 48 63 d0 movslq %eax,%rdx 143 729: 48 8b 4d f8 mov -0x8(%rbp),%rcx 144 72d: 48 8b 45 f0 mov -0x10(%rbp),%rax 145 731: 48 89 ce mov %rcx,%rsi 146 734: 48 89 c7 mov %rax,%rdi 147 737: e8 84 fe ff ff callq 5c0 <memcpy@plt> 148 73c: 90 nop 149 73d: c9 leaveq 150 73e: c3 retq
文字列のコピーなのでアドレスを扱います。よってrdiやrsiといったインデックスレジスタが使用されますが、rから始まるレジスタ名は、64bitのレジスタになっています。
たとえば、RAXレジスタは以下ような単位でアクセスできます。
+-------+-------+-------+-------+-------+-------+-------+-------+ | RAX(64) | +-------+-------+-------+-------+-------+-------+-------+-------+ | | EAX(32) | +-------+-------+-------+-------+-------+-------+-------+-------+ | | AX(16) | +-------+-------+-------+-------+-------+-------+-------+-------+ | | AL(8) | AL(8) | +-------+-------+-------+-------+-------+-------+-------+-------+
さてfunc2のローカル変数を見ていきましょう。
mainからfunc2の呼び出し部分のアセンブラは以下のようになっています。
159 753: 48 8d 4d ec lea -0x14(%rbp),%rcx 160 757: 48 8d 45 f6 lea -0xa(%rbp),%rax 161 75b: ba 03 00 00 00 mov $0x3,%edx 162 760: 48 89 ce mov %rcx,%rsi 163 763: 48 89 c7 mov %rax,%rdi 164 766: e8 a5 ff ff ff callq 710 <func2>
でfunc2のプロトタイプはこのようになっています。
4 void func2(char* a, char* b, int c)
func2のaがrdi、bがrsi、cがrdxに割り当たっていますね。
func2では、aからbへコピーするので、レジスタの役割としてはa、b逆に割り当たっています。関数のインターフェースを決める時は、第1引数をdestinationにしておいた方が、rdi/rsiレジスタが一致して読みやすくなりますね。
つまり以下のようなプログラムにする方が、アセンブラが分かりやすくなります。
1 #include <stdio.h> 2 #include <string.h> 3 4 void func2(char* b, char* a, int c) 5 { 6 memcpy(b,a,c); 7 } 8 9 int main() 10 { 11 char a[10]; 12 char b[10]; 13 a[0] = 10; 14 a[1] = 20; 15 a[2] = 30; 16 17 func2(b, a, 3); // aをbに3Byteコピーする 18 19 printf("b[0]=%d, b[1]=%d b[2]=%d\n",b[0],b[1],b[2]); 20 } takk@deb9:~/tmp$
コメント