アセンブリでQuine

この記事は、[Yahoo! JAPAN 18 新卒 2 Advent Calendar 2018](https://adventar.org/calendars/3419)の11日目の記事です。
前回は @walk8243 さんによる「[CaaSって高いじゃん！IaaSでしょ！](https://qiita.com/walk8243/items/5dfdbc73aa895cc001db)」でした。
次回は @teakun さんによる「[Scratchで始める大人のための子ども向けプログラミング入門](https://qiita.com/teakun/items/243394d5936ae1601e92)」です。

# はじめに
Linuxの復習していた際に書いたx86_64アセンブリのQuineについて紹介します。Quineはコードと同一の文字列を出力するプログラムですが、テストが容易で言語や環境について理解する目的で活用できるかと思います。

※ 本記事で記載しているアセンブリは[Gas](https://ja.wikipedia.org/wiki/GNU%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%A9)です。

## コード
```quine.S
mov $m, %rsi
mov $280, %rdx
mov $1, %rdi
mov $1, %rax
syscall
push $34
mov %rsp, %rsi
mov $1, %rdx
mov $1, %rax
syscall
mov $m, %rsi
mov $280, %rdx
mov $1, %rax
syscall
push $0x00000a22
mov %rsp, %rsi
mov $2, %rdx
mov $1, %rax
syscall
xor %rdi, %rdi
mov $60, %rax
syscall
m:.ascii"mov $m, %rsi
mov $280, %rdx
mov $1, %rdi
mov $1, %rax
syscall
push $34
mov %rsp, %rsi
mov $1, %rdx
mov $1, %rax
syscall
mov $m, %rsi
mov $280, %rdx
mov $1, %rax
syscall
push $0x00000a22
mov %rsp, %rsi
mov $2, %rdx
mov $1, %rax
syscall
xor %rdi, %rdi
mov $60, %rax
syscall
m:.ascii"
```
https://gist.github.com/orleika/7e89ebbce3281db30d5e219feb0832a8

# 解説

## プログラムの概観

疑似コードで書くといたってシンプルな構成です。

```
定数mを出力する
"を出力する
定数mを出力する
"\nを出力する
終了する
mを宣言する（宣言時にmは""で囲われている）
```

## 構成する要素

プログラムの概観が決まれば、あとは構成する要素について理解するだけで自ずと完成します。

### レジスタ

使用するレジスタは`rax`、`rdi`、`rsi`、`rdx`です。

|レジスタ|使い方|
|-----------------|------------------|
|`rax`|システムコール番号を指定した状態で`syscall`システムコールを実行することで該当のシステムコール番号に該当する命令が実行されます。|
|`rdi`|システムコール命令の第1引数を指定します。|
|`rsi`|システムコール命令の第2引数を指定します。|
|`rdx`|システムコール命令の第3引数を指定します。|


### syscall

`syscall`システムコールの書式について確認します。

```
$ man 2 syscall　# 以下抜粋
SYNOPSIS
       #define _GNU_SOURCE        /* or _BSD_SOURCE or _SVID_SOURCE */
       #include <unistd.h>
       #include <sys/syscall.h>   /* For SYS_xxx definitions */

       int syscall(int number, ...);
```
引数にシステムコール番号を渡します。システムコールは次の形式で定義されています。

```sys/syscall.h
#define SYS__xxx __NR__xxx
```

つまり、xxxのシステムコール番号を得るためには`__NR__xxx` という定数宣言を見ればよいということがわかります。しかし1個ずつ宣言を見ていくのも面倒なので、システムコールの名称と番号のマッピングを出力する`ausyscall`コマンドを使用して調べていくことにします。

```
$ man ausyscall # 以下抜粋
DESCRIPTION
ausyscall is a program that prints out the mapping from syscall name to number and reverse for the given arch.
```

### write

`syscall`で実行するシステムコール番号を調べます。

```
$ ausyscall write --exact
1
```
`1`です。また、`write`システムコールの書式についても確認します。

```
$ man 2 write　# 以下抜粋
SYNOPSIS
       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);
```

第1引数にファイルディスクリプタ、第2引数に文字列バッファ、第3引数にサイズを渡します。ファイルディスクリプタは標準出力を選択するので、`1`を入れます。また、文字列はスタック以外にも、`.ascii`ディレクティブを指定することで簡単に記述できます。

ここまでくれば文字出力は簡単です。

```hello.S
mov $m, %rsi
mov $13, %rdx
mov $1, %rdi
mov $1, %rax
syscall
m:.ascii "Hello World!\n"
```

## _exit
`syscall`で実行するシステムコール番号を調べます。

```
$ ausyscall exit --exact
60
```
`60`です。また、`_exit`システムコールの書式についても確認します。

```
$ man 2 exit # 以下抜粋
SYNOPSIS
       #include <unistd.h>

       void _exit(int status);
```
第1引数にステータスコードを渡します。コマンドが正常終了したことをシェルに伝えるために`0`を入れておきます。

_exitシステムコールを呼ぶことで`segmentation fault (core dumped)`が発生しなくなります。

```exit.S
mov $0, %rdi
mov $60, %rax
syscall
```

### ASCII

.asciiディレクティブに書けない`"`や`\n`は即値として書く必要があります。

```
$ man ascii # 以下抜粋
 Oct   Dec   Hex   Char      
 ---------------------------------------
 012   10    0A    LF  '\n' (new line)

 042   34    22    "  
```

## 実行結果

次の実行環境で動作確認を行いました。

```
$ uname -a
Linux instance-1 2.6.32-754.6.3.el6.x86_64 #1 SMP Tue Oct 9 17:27:49 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23)
```
プログラムとその実行結果に差分がないことを確認します。

```
$ gcc -nostdlib quine.S -o quine
$ diff quine.S <(./quine)
$ file quine
quine: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
```

# あとがき

初心に立ち返り、なるべくウェブで調べることなく`man`を駆使して実装することを心がけてみました。普段アセンブリを書いてシステムコールを呼び出す機会なんてそうそうありませんが、高級言語で記述したときに内部でどのように動作するのかに関心を向けることも時には大切なのではないでしょうか。

# 参考文献

- https://ja.wikibooks.org/wiki/X86%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%A9/GAS%E3%81%A7%E3%81%AE%E6%96%87%E6%B3%95
- https://www.mztn.org/lxasm64/amd04.html


<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="クリエイティブ・コモンズ・ライセンス" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a>
この記事は <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">クリエイティブ・コモンズ 表示 4.0 国際 ライセンス</a>の下に提供されています。