Hello,worldをちょっとだけよく見てみる

# agenda
* self-introduction
* 実行環境
* Hello,world
* コンパイル時にオプションをつけてみる
* objdumpを使ってみる
* 見てみよう
* レジスタ
* 命令
* 読んでみよう
* あとがき

## self-introduction
IPFactory所属の1年生 [konvat](http://twitter.com/konvat)です。低めのレイヤに興味があります
##実行環境
ThinkPad X1 Carbon (7th gen)
CPU: Intel Core i7-10710U
RAM: 16GB
OS: Ubuntu 19.10 64bit
GCC: gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008

**実行環境により実行結果は異なります。**
# Hello,world
まずはなんの変哲もないHello,worldを出力するプログラムを書きます

```c:hello.c
#include <stdio.h>
  
int main(void)
{
  printf("Hello,world\n");
  return 0;
}
```

さて、これをコンパイルして実行すれば普通に動くのですが、ちょっと寄り道をしましょう。
## コンパイル時にオプションをつけてみる
gccにオプションをつけます。

```sh
gcc hello.c -o hello -Wall -Wextra -g -O0 -static
```

オプションについては、[ここ](http://x68000.q-e-d.net/~68user/unix/pickup?gcc)のサイトが参考になると思います。詳しくはそちらを参照してください。
軽く説明すると、`-o`は実行ファイルに名前をつけるオプションです。
今回は`hello`という名前にしていますが、`-o`オプションで名前を指定しないと、
`a.out`という名前で実行ファイルが生成されます。
`-Wall`、`-Wextra`は、コンパイル時に各種警告を出すようにするオプションです。
`-W`オプションで指定できる警告の種類はたくさんあります(数えたところ320個ほど)。
見てみたい方は`gcc --help=warnings`で見ることができます。
今回は、`-Wall`と`-Wextra`を併用することによってほとんどの警告を表示するようにします。
`-g`はgdb等のデバッガを使うときに役に立つ情報をバイナリに残しておくオプションです。
`-O`は最適化をするオプションです。`-Ox`の`x`の部分に数字を入れて最適化の度合いを指定します。
今回は`-O0`というオプションをつけましたが、
これは指定できる最適化の度合いの中で一番低い最適化の度合いにしておくというオプションです。
`-static`は静的にリンクするオプションです。今回はこのあと読むものが読みやすくなるようつけました。
## objdumpを使ってみる
さて、先程のコマンドを実行するとコンパイルができて実行ファイルが生成できたはずです。
今回はこの中身のmain関数を見てみることによって内部的にどんなことをしているのかというのを
少し見てみたいと思います。

```sh
objdump -S hello | less
```

`objdump`コマンドは、オブジェクトファイルの内容を表示することのできるコマンドで、
実行ファイルを調査したりするのに便利なコマンドです。
今回は`-S`オプションと`-M`オプションをつけました。
`-S`オプションは、ソースコードを含めて逆アセンブル情報を表示するオプションです。
オブジェクトファイルとは、軽く説明するとコンパイラがソースコードをバイナリに変換したものです。
今回は、実行ファイルを読むのでコンパイルの後のリンクという処理も終わった状態のものです。
このあたりについては、Cのプログラムがどういう仕組みで実行されるかについて
最低でもふわっとした理解が必要です。[このサイト](http://aoking.hatenablog.jp/entry/20121109/1352457273)とかが参考になると思います。
`-M`オプションでこの後見られるアセンブリをintel記法で記述することを指定します。見やすいですし。
また、`|`(パイプ)で繋いだ`less`コマンドによって、ファイル内の文字列を検索します。
ファイルが開けた後、

```sh
/<main>
```

と検索すると、main関数の部分を見ることができます。
## 見てみよう
さて、先程までの操作で実行ファイルを逆アセンブルした情報が見られてmain関数の部分が検索できました。
すると、main関数の部分の前後は省略しますがこんな感じになりました。

```sh
0000000000401d35 <main>:
#include <stdio.h>

int main(void) 
{
  401d35:       f3 0f 1e fa             endbr64 
  401d39:       55                      push   rbp
  401d3a:       48 89 e5                mov    rbp,rsp
        printf("hello,world\n");
  401d3d:       48 8d 3d c0 22 09 00    lea    rdi,[rip+0x922c0]        # 494004 <_IO_stdin_used+0x4>
  401d44:       e8 b7 fc 00 00          call   411a00 <_IO_puts>
        return 0;
  401d49:       b8 00 00 00 00          mov    eax,0x0
}
  401d4e:       5d                      pop    rbp
  401d4f:       c3                      ret    

```

うーん、16進数とアセンブリだ！今回は、アセンブリの部分を読んでいきたいと思います。
実行環境が64bitプロセッサ、64bitOSなのでこのアセンブラはx64です。
ここで使うレジスタと命令について、これまた軽く説明しておきます。
### レジスタ
#### eax
アキュムレータレジスタ。昔は名前の通り算術演算用のレジスタだったが、
現在は他の汎用レジスタと差はない。
#### rsp
スタックポインタレジスタ。スタックの先頭アドレスを保持する。
#### rbp
ベースポインタレジスタ。現在のスタックフレームのアドレスが格納されている。[参考](http://www.c-tipsref.com/words/stackframe.html )
#### rdi
ディスティネーションインデックスレジスタ。もともとメモリ位置を示すためのレジスタだったが、
今ではstring型の命令においてのコピー先(ディスティネーション)を示すための専用レジスタ。
その他の場合は汎用レジスタとして使用できる。
#### rip
インストラクションポインタ。
プログラムカウンタ(PC)とも呼ばれる。次の命令のアドレスが格納されている。
### 命令
以下、`op`をオペランド、`op1`を第1オペランド、`op2`を第2オペランドとする。
#### push

```sh
push op ;rspをデクリメントし、rspのメモリアドレスにopの値(内容)を書き込む
```

#### pop

```sh
pop op ;rspのメモリアドレスから値を読み出し、opに格納した後、rspをインクリメントする
```

#### mov

```sh
mov op1, op2 ;op2の内容をop1にコピー
```

#### lea

```shell-session
lea op1, [op2] ;op2に格納されている、アドレスの値そのものをop1に格納する
```

#### call

```sh
call op ;rspをデクリメントし、rspのメモリアドレスにripの値を書き込んだ後、ripに呼び出し先のアドレス(op)を設定する
```

#### ret

```sh
ret ;rspのメモリアドレスから値を読み出し、それをripに設定した後、rspをインクリメントする
```

## 読んでみよう
一通り必要な説明が終わりましたので早速読んでいきましょう。
### 関数を呼び出して最初にやる処理

```sh
int main(void) 
{
  401d35:       f3 0f 1e fa             endbr64 
  401d39:       55                      push   rbp
  401d3a:       48 89 e5                mov    rbp,rsp
```

最初の1行、`endbr64`も命令で、end branch 64bitの略。詳しくは[ここ](https://tutorialmore.com/questions-301621.htm)
次の2行は、関数が実行されるとき最初に実行される処理で、`rbp`はスタックの底のアドレス、
`rsp`はスタックの一番上のアドレスを指しています。
`push rbp`でrbpの内容、つまりスタックの底のアドレスですから、現在のアドレスであり、
`main(void)`の呼び出し元のアドレスをバックアップすることによって、
`main(void)`が終了したときに`main(void)`の呼び出し元の処理を再開するときに使えるようにします。
これが終わったら、`mov rbp, rsp`で`rsp`の値を`rbp`に書き込みます。
これによって最新のスタックのアドレスが、
`main(void)`のスタックフレームのベースアドレスとして使われるようになります。
### printf()の実行

```sh
      printf("hello,world\n");
  401d3d:       48 8d 3d c0 22 09 00    lea    rdi,[rip+0x922c0]        # 494004 <_IO_stdin_used+0x4>
  401d44:       e8 b7 fc 00 00          call   411a00 <_IO_puts>
```

さて次。`lea rdi, [rip+0x922c0]`で、
`rip`に格納されているアドレスに`0x922c0`を加算したアドレスを`rdi`に書き込んでいます。
`rip+0x922c0`というアドレスは、色々調べてみると、どうやら`"Hello,world"`、
つまりHello,worldという文字列の先頭アドレスらしい。
`rdi`を使用して、この後使われる関数に渡しているようです。
なるほど、だから`rdi`を使うのでしょうか？
さあいよいよ`printf()`の実行です。
`call 411a00 <_IO_puts>`で`411a00`番地にジャンプし、関数を呼び出します。
`411a00`番地って何だ...と思いこれもまた色々調べたところ、どうやら`glibc/libio/input.c`の中の
`_IO_puts(const char *str)`という関数の先頭アドレスのようです。
ということは、`call 411a00`は`glibc/libio/input.c`の
`_IO_puts(const char *str)`を呼び出す処理ということですね？
なるほど、`call 411a00 <_IO_puts>`の`<_IO_puts>`ってこういう意味があったのか〜！？
なんで余計なものがついているんだろうと思っていましたが、やっぱりちゃんと意味はあったようです。
結局、内部的に実行されていたのは`printf()`ではなく、`_IO_puts(const char *str)`でした。
これはgccの最適化によるもののようです。
もっと詳しく掘り下げてしまうと長くなってしまうので、
また今度別記事で`glibc/libio/input.c`を読んでみたいと思います。(時間かかると思いますが)
### return 0;

```sh
        return 0;
  401d49:       b8 00 00 00 00          mov    eax,0x0
}
```

長くなってしまいましたが、次は`return 0;`の処理です。
`mov eax, 0x0`で、`eax`に0x0、つまり0を書き込みます。
つまり、`return 0;`の`0`ですね。`eax`は戻り値の格納に使われるレジスタなので、
文字通り`return 0;`のための処理ですね。
###関数の終了

```sh
  401d4e:       5d                      pop    rbp
  401d4f:       c3                      ret
```

さて、最後の処理です。
`pop rbp`で`rbp`に`push rbp`の処理でバックアップしておいた
`main(void)`の呼び出し元のアドレスを元のように格納し、呼び出し元に戻れるよう準備します。
そして、`ret`で`main(void)`の呼び出し元に戻り、次の処理を実行します。
## あとがき
ということで、Hello,worldを出力するだけのプログラムの`main(void)`の部分だけですが
アセンブリを読んで、何をしているのか**ちょっとだけ**詳しく読んでみました。
間違い等あればご指摘いただけるとありがたいです。最後まで読んで頂き、ありがとうございました。