概要
- 最近アセンブラを始めてHelloWorldを出力したのですが、いろいろ難しかったので備忘録としてまとめました。
- 初学者なので、解説はちょいちょい間違ってるかもしれないです。
使用した環境など
OS | Ubuntu22.04 |
---|---|
CPU | Intel 64-bit |
準備
- 以下のコマンドを実行して、必要なものをインストールします。
- アセンブリ言語を翻訳してくれるアセンブラはnasmにしました。
sudo apt-get install nasm sudo apt install binutils
Hello Worldを表示する
1. ASMファイルの用意
- アセンブリ言語は拡張子asmのファイルに記述します。
- 以下のコードを用意し、「hello.asm」に記述しました(コードの解説は後述)。
section .data message: db 'Hello World!', 10 section .text global _start _start: mov rax, 1 mov rdi, 1 mov rsi, message mov rdx, 13 syscall; mov rax, 60 xor rdi, rdi syscall
2. 実行ファイルの生成
作成した「hello.asm」を実行するために、次の2つのコマンドを実行します。nasm -f elf64 hello.asm ld -o hello hello.o
正しく実行できると、「hello.o」と「hello」と2つのファイルが生成できます。
3. 実行ファイルを動かす
以下のコマンドで生成したファイルを実行させます。./hello
すると、以下の様に「Hello World!」が出力されます。
Hello World!
ソースコードの解説(行ごと)
section .data
ここにはプログラムで使用する変数を格納します。
message: db 'Hello World!', 10
message | 定義したい変数名 |
---|---|
db |
|
'Hello World!', 10 |
|
section .text
とりあえずスルーでOKです。
_start:
プログラムのメインとなる部分を記述します。
mov rax, 1
- movはメモリやレジスタに値を書き込む処理であり、この行ではraxというレジスタに1という値を書き込んでいます。
- raxには命令の種類(システムコール番号)を指定するレジスタだそうです。1を指定すると出力が行えるようになります。
- このシステムコール番号はCPUにより異なるそうですが、本環境における代表的なものを以下に示します。
番号 | 処理 |
---|---|
1 | 出力 |
2 | ファイルを開く |
60 | プログラムを終了させる |
参考:Syscall Number for x86-64 linux (A)
補足(1):出力(システムコール番号1)について
ssize_t write(int fd, const void *buf, size_t count);
の処理を行います。引数の意味を下の表に示します。
名称 | 意味 |
---|---|
fd |
|
buf |
|
count | 表示したい文字数 |
補足(2):引数の指定
- 関数に渡すのではなく、引数に該当するレジスタに書き込む必要があるそうです。
- 引数の番号と対応するレジスタを以下に示します。
引数の番号 | 対応するレジスタ名 |
---|---|
1 | rdi |
2 | rsi |
3 | rdx |
4 | r10 |
5 | r8 |
6 | r9 |
参考:Syscall Number for x86-64 linux (A)
実際に引数を指定
- 補足(1)、(2)を基に出力のための引数の指定を行います。
引数 | レジスタ | 値 | 意味 |
---|---|---|---|
fd | rdi | 1 | 標準出力を行う |
buf | rsi | message | 表示したい文字列が変数「message」に格納された「Hello World!」であることを伝える |
count | rdx | 13 |
|
ソースコードの各行と上の表の対応を確認してみてください。
mov rdi, 1 mov rsi, message mov rdx, 13
syscall;
- ここまでの処理を実行させるので、「Hello World!」が表示できます。
mov rax, 60
- このシステムコール番号60を指定する。
- 先程の表より、プログラムを終了しようとしていることが分かる。
xor rdi, rdi
- rdiとrdiの排他的論理和を取ることで、rdiの中身を0に初期化します。
syscall;
- ここまでの処理を実行し、プログラムを終了させる。