Macでアセンブラから直接システムコールを呼ぼうとしたらなかなかうまくいかない件の覚書。
Linuxの場合・・・Linuxでシステムコールしようとすると、まずレジスタを全部クリアしたあと、呼びたいシステムコールの番号(
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・・・に入れるものを渡せばいいようです。
これらについてはまだよくわからないので、今後調べてみたいと思います。