はじめに
「C言語? ああ、あれねポインタのやつね。参照的なあれでしょ? うん。知ってる知ってる」
上記は私のC言語に対する印象です。かなりひどい。
これは何とかしなくちゃいけないよね?ってことで今回の内容になります。
環境:
Ubuntu 14.04
gcc version 4.8.4
objdump(binUtils for Ubuntu) 2.24
x64
確認
以下のC言語のメソッドに関して確認していきます。
int plus(int num1, int num2) {
return num1 + num2;
}
上記のファイルからgcc -c plus.c
で実行ファイルplus.o
を生成します。
次にobjdump -d plus.o
で以下の逆アセンブル結果が得られます。
0000000000000000 <plus>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d fc mov %edi,-0x4(%rbp)
7: 89 75 f8 mov %esi,-0x8(%rbp)
a: 8b 45 f8 mov -0x8(%rbp),%eax
d: 8b 55 fc mov -0x4(%rbp),%edx
10: 01 d0 add %edx,%eax
12: 5d pop %rbp
13: c3 retq
解説
push %rbp
まず一行目で呼び出し元のベースポインタ(rbp)をスタックにプッシュします。
最後に呼び出し元に戻れるように退避させておく感じです。
mov %rsp,%rbp
二行目でベースポインタ(rbp)の値をスタックポインタ(rsp)の値に初期化します。
呼び出し元のベースポインタ(rbp)が入っているアドレスですね。このメソッドのスタックはここが基準になります。
mov %edi,-0x4(%rbp)
ベースポインタからint型の32bit(4byte)分離れたアドレスにデスティネーションレジスタ(edi)の値を保存します。
第一引数をローカル変数としての領域に格納しています。
mov %esi,-0x8(%rbp)
デスティネーションレジスタ(edi)の値を格納したアドレスから、さらにint型の32bit(4byte)分離れたアドレスにソースレジスタ(esi)の値を格納します。
第二引数をローカル変数としての領域に格納しています。
mov -0x8(%rbp),%eax
アキュムレータ(eax)に第一引数の値を格納しています。
mov -0x4(%rbp),%edx
データレジスタ(edx)に第二引数の値を格納しています。
add %edx,%eax
ついに足しました。今回は一度しか加算がないのでeaxに対してedxを加算していますが、複数の数を加算する場合はedxに加算していき、最後にeaxに加算されるようです。
最後にeaxに加算されるのはeaxが戻り値となるためです。
pop %rbp
呼び出し元のベースポインタの値をスタックから取り出して、呼び出し元に戻る準備をします。
retq
呼び出し元に戻ります。
最後に
今回の目的は、ほぼほぼWeb系の学習してこなかった自分の知見を広げる為のものなので、かなり低レベルな内容です(言語的にも内容的にも)。
いや、ほんとはポインタの動作なんかも確認したかったけど時間が・・・
何はともあれ、とっつきにくいイメージのあったアセンブリに多少なりとも簡単に親しむことができたので、自分の書いたコードがアセンブリでどう表現されるのか知りたい人はgccとobjdumpの組み合わせがおすすめです。
P.S.
よく考えたら全然C言語の勉強になってないっすね。