こんにちは、Unityエンジニアのオオバです。
ECS完全に理解した勉強会で、
@tnayukiさんのLTでアセンブリが話題になりました。
良いプログラマーになるための近道は
アセンブリを勉強することだと
プログラマ初心者にアドバイスしたこと。
オオバも可能であれば良いプログラマになりたいので、
アセンブリを始めました。
ILの勉強もはじめました。
興味ある方はどうぞ読んでみてください。
初心者向け事前知識なしの状態からUnityエンジニアがILを読んでみる
- nasmをインストール
- アセンブリ用意
- コンパイル
👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!
1. ググるとnasmが出てくる
ということで、nasmをインストールします。
$ brew install nasm
HomeBrewでnasmはインストール可能です。
2.Helloworld出力のソースコードを用意
Hello Worldを標準出力するアセンブリソースコードをhelloworld.asm
というファイル名で保存。
3.コンパイル(アセンブル)
ソースコードhelloworld.asm
を
機械語(バイナリ)に変換します。
$ nasm -f macho64 -o helloworld.o helloworld.asm
機械語になったhelloworld.o(バイナリファイル)が出力されます。.oファイル
はオブジェクトファイルと呼ぶらしい。
4.実行ファイルの作成
ldコマンド(リンカコマンド)を使って実行ファイル(バイナリ)を生成します。
ld -arch x86_64 -macosx_version_min 10.11 helloworld.o -lSystem
もし以下のような感じでXcode周りのエラーが出てうまくいかない場合は、
xcrun: error: active developer path ("/Applications/Xcode9.3.app/Contents/Developer") does not exist
Usesudo xcode-select --switch path/to/Xcode.app
to specify the Xcode that you wish to use for command line developer tools, or usexcode-select --install
to install the standalone command line developer tools.
こちらのコマンドを実行してXcodeのコマンドラインツールをインストールします。
xcode-select --install
この記事でも起きたコマンドラインに関するエラーと同じ対処法です。
PyenvでPython3.6.3インストール時のトラブル
改めて、先程のldコマンドを実行します。
ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _main from helloworld.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie
するとこのようなwarningが出てしまいますが、a.out(実行バイナリ)が出力されます。
5.Hello World!
以下のようにa.outを実行します。
./a.out
するとコンソールに出ました!Hello Worldです。
ここからアセンブリのソースコードについて調べていきます。
global _main
_
や.
から始まるものをシンボル
と呼びます。
global
がくっついていると、外部参照を可能にするとのことです。
また複数同名のシンボル名は定義できません。
; DATAセクションの始まりを定義
section .data
セクションは、以下のために存在するとのことです。
プログラム本体やプログラム中で使用する定数、文字列、変数 に必要な性質に応じて区別して管理するため
TEXTセクション書き換え不可、DATAセクションは書き換え可能領域とのことですが、今回はあまり深くは踏み込まないことにします。
str_hello: db Hello World", 0x0a
str_hello
というラベルを定義しています。あとからラベル内から参照します。
dbとは、db 文字列
と書くと1文字ずつASCIIコードに変換し、数値の連続として書き込みます。
db 数字A, 数字B, 数字C
とすると数字A〜Cを数値の連続として書き込みます。連続した値はカンマ
で区切ります。
dbは、要は値を書き込みます。
その書き込んだものをstr_hello
という任意の名前のラベルで参照できるようにしているということです。
_main:
.
や_
から始まり、:
で終わるシンボルをラベル
と呼び、この例では_main:
と定義した場所のメモリアドレス
を値として持ちます。
ラベル以下のコードがそのラベルの処理内容です。
mov rax, 0x2000004
mov rdi, 1
mov rsi, str_hello
mov rdx, 13
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
文法的にはこんな感じ。
今回出てくるオペコードは mov
のみです。
またmov, rax, rdiなどをまとめて、ニーモニック
と呼ばれます。
mov
は値をコピーするオペコードで、
mov rax, 0x2000004
rax
に 0x2000004
という値をコピーするという意味になります。
rax部分がレジスタ
というものにあたり、今回4種類のレジスタが出てきたので以下まとめています。
使用用途
--- | --- | ---
rax | アキュムレーターレジスタ | 計算時の変数、関数戻り値格納システムコール番号指定
rdi | ディスティネーションインデックスレジスタ | スタックの先頭アドレスを保持
rsi | ソースインデックスレジスタ | 文字列操作時のソースポインタ
rdx | データレジスタ | 計算時の変数に使用され、I/Oポインタとして使用
20行目 ,[object Object]
rax(アキュムレーターレジスタ)に標準出力システムコールの番号4
をコピーしています。
x64環境では0x2000000
を加算した値を入れます。
24行目 ,[object Object]
rdi(ディスティネーションインデックスレジスタ)に標準出力システムコールの第1引数になる値をコピーします。
0 : 標準入力、1 : 標準出力, 2 : 標準エラー
とのこと
今回は標準出力させるの1
を指定します。
29行目 ,[object Object]
rsi(ソースインデックスレジスタ)に標準出力システムコールの第2引数になる値をコピーします。
これは、事前に定義しているstr_hello
と記述することでその場所のメモリアドレスがコピーされます。
34行目 ,[object Object]
rdx(データレジスタ)に標準出力システムコールの第3引数になる値をコピーします。
出力するデータのバイト数を指定します。
今回1文字1バイト使用するため、例えばmov rdx, 1
とすると、H
しか出力されません。
37行目 ,[object Object]
標準出力システムコール実行
Hello Worldとコンソール上に出力されます。
41行目 ,[object Object]
exitシステムコール 1を指定(現在のプロセスを終了)
44行目 ,[object Object]
exitシステムコールの第1引数になる値をコピーします。
ステータスコード0を指定。
48行目 ,[object Object]
exitシステムコールの実行。
といった処理内容になります。
まとめ
全く事前知識0の状態でアセンブリに触れてみましたが、そんなに難しくはなく、ある程度ニーモニックの意味が分かれば、なんとなく読める気がしてきました(書ける気はしない)。
勉強会でも紹介されていましたが、Unityのバーストコンパイラではアセンブリソースコードが表示することができます。どのようなコードに最適化がされているか、ニヤニヤしながら眺められるようになると、新しい自分を見つけられるかもしれません。
LTで分かる!アセンブラ
@tnayukiさんのスライドがアップされていたので、追加しました。
この記事が気に入ったらフォローしよう
- macOS High Sierra 10.13.6
- NASM version 2.13.03 compiled on Feb 8 2018