のらりくらり

物理化学分野のポスドクです。プログラミング、読書、自転車などが好きです。

Macでシステムコール

Macでアセンブラから直接システムコールを呼ぼうとしたらなかなかうまくいかない件の覚書。

Linuxの場合・・・Linuxシステムコールしようとすると、まずレジスタを全部クリアしたあと、呼びたいシステムコールの番号(とかに#defineされてるはず)をeaxにわたして、ebxに第一引数、ecxに第二引数、edxに第三引数・・・って感じでmovしてやって、その後int 0x80の特殊割り込み命令をかければいいはずです。これは参考になるところがたくさんあるからいいとして・・・
Macの場合・・・
これがよくわかりません。まず、Cでsleep()だけとりあえず呼び出してみて、それをgccの-Sオプションでコンパイルしてみます。その中間ファイル(アセンブラソース)を見ると、call _sleep が見当たるわけで。
nmを確認しても、

> nm a.out
0000000100000f3a s  stub helpers
0000000100001048 D _NXArgc
0000000100001050 D _NXArgv
0000000100001060 D ___progname
0000000100000000 A __mh_execute_header
0000000100001058 D _environ
                 U _exit
0000000100000f14 T _main
0000000100001000 s _pvars
                 U _sleep
                 U dyld_stub_binder
0000000100000ed8 T start

とあるように、sleepっていう関数が普通にあるのと同じようです。
その後、大してアセンブラのファイルもみずに、普通に0から書いてみると、sleep()をコールする前にスタックに引数を一つPushしてやってみてもこれがSegmentation faultとなります。
試行錯誤した結果、以下のコードだとうまくいきました。

.text
.globl _main
_main:
	pushl %ebp
	movl  %esp, %ebp
	subl  $8,  %esp
	pushl $0
	pushl $0
	pushl $0
	pushl $5;
	call  _sleep;
	leave
	ret

どうも、引数をプッシュする順序は普通の関数呼び出しと同じように右側から、つまり、第4引数、第3引数・・・第1引数の順にスタックにプッシュしてやれば良い様ですが、システムコールの場合は、必ず4つpushしてやらないといけないようです。該当する引数が無いときは、$0のプッシュで良いみたいです。execveなどでもやってみましたが、やはりそうでした。

ほかで調べてみると、xnuカーネルのsysenter命令とかを用いればもっと直接的な呼び出しができるようです。またはsyscall関数というのが用意されているみたいで、このsyscallの第1引数、第2引数・・・の順に、Linuxシステムコールするときのeax、ebx・・・に入れるものを渡せばいいようです。
これらについてはまだよくわからないので、今後調べてみたいと思います。