[アセンブラ]
アセンブラの学習
前回まででとりあえずインラインアセンブラで変数宣言とかできた。次はHello,World!!したい。
システムコール
これを呼べばOSの力を借りて標準出力できる。アセンブラ独力でやるのはしんどいので後で… アセンブラ独力では無理だ
システムコールとは、オペレーティングシステム (OS)(より明確に言えばOSのカーネル)の
機能を呼び出すために使用される機構のこと。実際のプログラミングにおいては、OSの機能は関数 (API)
呼び出しによって実現されるので、OSの備える関数 (API) のことを指すこともある。
さてさて
ここまででたぶんお分かりかもしれないが、アセンブラはCPUのアーキとOSに依存する。
CPUのアーキ | OS |
---|---|
i386 や x86_64 | Linux/Windows/BSD .etc... |
レジスタの使い方や種類が違う | システムコールの種類や使い方が違う |
やっぱりC言語って便利だったんすね~、JavaとかC#に至っては神に見える
Linuxのシステムコール
オンラインコンパイラを使いたいのでLinuxを中心に攻めていく、以下は下記のサイトの翻訳となる
32-bit Linuxにおけるシステムコール
- 32bitのLinuxでシステムコールを実行する場合、システムコールの番号をレジスタのeaxに置き、その引数を順番にebx, ecx, edx, esi, ediそしてebpに置き、intの0x80を呼び出します
- 返り値に情報を返してくるシステムコールもあります、返り値はeaxに入ります
- すべてのレジスタはシステムコールを通じて保存されます
64-bit Linuxにおけるシステムコール
- 64bitのLinuxでシステムコールを実行する場合、システムコールの番号をレジスタのraxに置き、その引数を順番にrdi, rsi, rdx, r10, r8そしてr9に置き、syscallを呼び出します(※x86と違ってそのまんまなんですな)
- 返り値に情報を返してくるシステムコールもあります、返り値はraxに入ります。-4095~-1の範囲の値はエラーを示します、つまりC言語で言う-errnoです。
- システムコールはレジスタのrcxとr11を破壊しますが、残りのレジスタはシステムコールを通じて保存されます
- 詳細はこのThe AMD64 ABIから
Linuxのシステムコール一覧
- Linuxには何百ものシステムコールが存在します。オンラインで得られるよさげな情報源はここです http://syscalls.kernelgrok.com/ 。64bit Linuxについてはここを確認してみてください http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64
これらのページを確認し、そして当然ですがLinuxのソースコードも見て下さい。
めんどくさい…
Hello,World
アセンブラソースを修正
先ほどのページに生のアセンブラが書いてあるので、それを修正してみよう
# ---------------------------------------------------------------------------------------- # Writes "Hello, World" to the console using only system calls. Runs on 64-bit Linux only. # To assemble and run: # # gcc -c hello.s && ld hello.o && ./a.out # # or # # gcc -nostdlib hello.s && ./a.out # ---------------------------------------------------------------------------------------- .global _start .text _start: # write(1, message, 13) mov $1, %rax # system call 1 is write mov $1, %rdi # file handle 1 is stdout mov $message, %rsi # address of string to output mov $13, %rdx # number of bytes syscall # invoke operating system to do the write # exit(0) mov $60, %rax # system call 60 is exit xor %rdi, %rdi # we want return code 0 syscall # invoke operating system to exit message: .ascii "Hello, world\n"
インラインアセンブラ版(C言語)
- オペランド制約をきっちり記述しないとHelloWorldが出ないようです
#include <stdio.h> #include <unistd.h> int main() { const char hello[] = "Hello World!\n"; const size_t hello_size = sizeof(hello); unsigned int ret; __asm__ volatile ( "mov $1, %%rax \n\t" "mov $1, %%rdi \n\t" "mov %1, %%rsi \n\t" "mov %2, %%rdx \n\t" "syscall" : "=a"(ret) : "S"(hello), "d"(hello_size) ); return 0; }
システム言語の意義とは
- C言語はC言語でソースコードを書くと、それをアセンブラに変換し、動く機械語に変換する。
- 動く機械語とはバイナリと呼ばれるものである。OSはバイナリをCPUに渡し、実行結果を受け取る。
- プログラミング言語の世界で、このバイナリを吐ける言語をシステム言語と呼んだりする。それはC言語を筆頭として、C++、D言語、Go言語などを指す。
- すでに、CPUのプラットフォームとOSのプラットフォームが固定化された世界では、アセンブラでコーディングすることは労力がかかるために意味をなさないことが多い。
- だから、「アセンブラで最適化した〜」とか、そういう言説や人はあまり信用できない(と、管理人は思う)
- 言いたいことはつまり、ソースコードから機械語への変換を助けて最適化するのがシステム言語の役割であり、それを人手でやるのは非効率であるということだ。
- でも、新たにシステム言語を作ったり、OSを一から書く場合アセンブラは避けては通れない気がします。