x86でHelloWorld

これは[東京高専プロコンゼミ① Advent Calendar 2022](https://adventar.org/calendars/8370)の2日目の記事です．

## hello world
アセンブラでhello worldして，コンピュータのもっと奥深くへと続く階段を降り始めましょう！

## x86 GNU Assembler (GAS)
`x86 GNU Assembler`(x86 GAS)というのは`GNUコンパイラ`(gccとか)で使われる`x86`というCPUシリーズのアセンブリです
(この記事ではx86-64です）

https://ja.wikipedia.org/wiki/GNU%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%A9

## code
まずはコードを見てください

```ruby:hello.s
.intel_syntax noprefix
.global main

main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address
	mov rdx, 10 # length
	syscall

	mov rax, 60 # exit 
	mov rdi, 0
	syscall


.data
_helloworld:
	.asciz "helloworld"

```

```:コンパイル・リンク
$gcc -c hello.s -o hello.o && ld -e main -o hello.bin hello.o
```


## ディレクティブ
上から見ていきます．
```ruby:1行目
.intel_syntax noprefix
.global main
```
この二つの行ではgccコンパイラに情報を渡しています．
一行目では`intel構文`でアセンブラを描くことをコンパイラに宣言しています．

二行目ではmainから処理が始まるということを教えています．


**このようにコンパイラにいろいろやってくれるように頼む指令のことを`ディレクティブ`(directives)といいます．**
ディレクティブは先頭に`.`が付きます．

[ディレクティブの一覧](https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html)



## ラベル

```ruby:4行目
main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address
```

ここで新しく出てきたのが`ラベル`です．
```ruby:4行目
main:
```
これが`ラベル`です．ここでは`ラベル`を宣言しています．

実そしてここではもう一つ`ラベル`が出てきています．
```ruby:7行目
lea rsi, _helloworld # string address
```
ここです．ここでは`ラベル`を使っています．
この`ラベル`の宣言はもっと後ろでされています．
```ruby:14行目
.data
_helloworld:
	.asciz "helloworld"
```
ここです．

`ラベル`を宣言するときは`:`をラベルの名前の後につけます．
`ラベル`を使うときは`ラベル名`を以下のように使えばいいだけです．
```ruby:7行目
lea rsi, _helloworld # string address
```

`ラベル`というのは場所の名前みたいなもので，`ラベル`が宣言されている場所の`アドレス`が紐図けられています．
人間がコードを見たり書いたりするときに分かりやすくするためのもので，コンパイラがコンパイル時に`ラベル`を`アドレス`に置き換えてくれます．



## レジスタ
```ruby:4行目
main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address
	mov rdx, 10 # length
```

これらの行では`レジスタ`と呼ばれるCPU内部の記憶装置に値を入れています．
[レジスタの一覧](https://www.jamieweb.net/info/x86_64-general-purpose-registers-reference/)

## 命令
このプログラムで出てくる命令は3種類しかないです．
* `mov`
左辺に指定したレジスタに右辺の値を入れる命令です．
```ruby:mov例
mov rax, 1
```
* `lea`
左辺に指定したレジスタに右辺の示すアドレスを入れる命令です．
```ruby:例
lea rsi, _helloworld #_helloworldがさすアドレスがrsiジスタに入る．
```

* `syscall`


## syscall

```ruby:4行目
main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address
	mov rdx, 10 # length
	syscall
```

ここでは`syscall`と呼ばれる．OSが用意してくれているサブルーチンを使うことのできるアセンブリ命令を使っています．

syscallを使うにはraxに使いたいsyscallの番号を入れその他のレジスタに引数を渡します．


syscall番号や引数は以下のリンクを確認してください
[syscall一覧](https://filippo.io/linux-syscall-table/)

```ruby
mov rax, 1 # write
mov rdi, 1 # std 1
lea rsi, _helloworld # string address
mov rdx, 10 # length
```

ここではraxに`write`のsyscall番号である`1`を入れて,`write`を使って標準出力しています．

`write`の引数は以下の通りです．
|rdi|rsi|rdx|
|----|----|----|
|ファイルディスクリプタ（１で標準出力）|文字列の先頭アドレス|文字列の長さ(byte数)|




```ruby:9行目
syscall
```
レジスタにsyscall番号，引数を入れたのち`syscall`命令を実行することで`syscall`を呼び出せます．



```ruby:11行目
mov rax, 60 # exit 
mov rdi, 0
syscall
```
ここでのsyscallはプログラムを終了するため`exit`を呼んでいます．


## 文字列の確保
```ruby:14行目
.data
_helloworld:
	.asciz "helloworld"
```
ここでは`ディレクティブ`を使っています．

まず`.data`でこれより下がデータセクションであることをコンパイラに知らせています．

そしてバイナリ中に文字列を埋め込んでくれる`.asciz`という`ディレクティブ`により，`helloworld`という文字列をバイナリ中に埋め込んでいます．

またここでは`ラベル`を宣言しています．
```15行目
_helloworld:
```
この`ラベル`には`.ascii`で確保した文字列の先頭アドレスが入っています．

このラベルはmainの
```ruby
mov rax, 1 # write
mov rdi, 1 # std 1
lea rsi, _helloworld # string address
mov rdx, 10 # length
```
ここで使われています．

## コンパイル・リンク
```:コンパイル・リンク
$gcc -c hello.s -o hello.o && ld -e main -o hello.bin hello.o
```
`GAS`なので`gcc`でコンパイルしてください．
`gcc`でコンパイルだけしているのは普通にコンパイルするとコンパイラがいろいろやってくれるので`-c`でコンパイルだけして`ld`でリンクしています（今回はどっちでもいいけどバイナリを覗くときに分かりやすい）
`ld`の`-e main`は最初に実行し始めるところ(`エントリーポイント`)に`main`を指定しています．

```:実行
$./hello.bin
```

`実行結果`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730711/f6ce5b4e-6c15-930a-9a5a-79f5556620e1.png)


生成された実行形式のファイルを覗いてみましょう．
```:バイナリを覗く
$objdump -d hello.bin
```

***Hello Computer***

## おわりに



ありがとうございました．誤り等はコメントで教えてください．

これは[東京高専プロコンゼミ① Advent Calendar 2022](https://adventar.org/calendars/8370)の2日目の記事です．
このカレンダー，めちゃくちゃ開いていたので急遽日が変わる30分くらい前に投稿するために記事を書き始めました．よって案の定遅刻しています．ゆるして・・・

# リンク集
- Syscall一覧↓

https://filippo.io/linux-syscall-table/

- brkの使いかた，アセンブラの入門↓

https://www.mztn.org/lxasm/asm08.html

- アセンブラ入門（少し古め，けどわかりやすい）↓

http://doomo.main.jp/x86asm/

- 命令一覧↓

https://c9x.me/x86/

- Directive 一覧↓ 

https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html

- 汎用レジスタ一覧↓

https://www.jamieweb.net/info/x86_64-general-purpose-registers-reference/

- Opcode/Instruction finder for x86_64

https://hikalium.github.io/opv86/