わたすけです。
昔個人の方でこんなブログを書きました。
そして、この記事にこんなことを書いています。
呼び出すのはsys_exitです。ちなみに、この呼び出しを忘れるとセグフォするので注意しましょう(1敗)。
アセンブリでのsyscallとint 0x80の違い – わたすけのへや
そう、Linuxにおいて、sys_exitシステムコール(以下exit)を呼ばないプログラムを作ると、Segmentation Faultが発生してしまいます。
何故なのでしょうか?調べてみました。
そもそもシステムコールとは
Wikipediaによると、「オペレーティングシステム (OS)(より明確に言えばOSのカーネル)の機能を呼び出すために使用される機構のこと」らしいです。例えば、OSの機能のうちひとつである標準出力に何かを出力しようと思った時、writeというシステムコールを使います。
exitもシステムコールの一種で、プログラムの終了をOSに通知するシステムコールです。
ですが、C言語ではmain関数からreturnするだけでプログラムが終了しますよね。アセンブリでもreturnすればいいんじゃないでしょうか?
そんなことはないです。そもそも特に指定しない場合、プログラムの開始地点はmainではないです。C言語はコンパイラがいい感じに初期化や終了処理を行ってくれるため、mainはあくまでコンパイラが挿入した処理の中で呼ばれる関数にすぎないのです。コンパイラがexitシステムコールを呼んでくれるわけですね。
CPUのきもちになる
パソコンにおける重要なパーツとして、CPUというものがありますね。CPUはメモリから機械語をとってきて、それに応じた処理を行います。
この機械語はメモリのどこから取ってくるのでしょうか?それを記憶するのがCPUのレジスタであるプログラムカウンタとかインストラクションポインタとかいうやつで、例えばこのレジスタの値が0xff00だったら、メモリの0xff00番地から読み取ります。これによって4バイト長の命令をフェッチした場合、プログラムカウンタの値は0xff04になるわけです。
Linuxの保護機能
LinuxやWindowsなどのOSは、複数のソフトを同時に実行することが出来ます(実際には同時に実行してるように見えるだけ)。マルチタスク機能ですね。パソコンに搭載されているメモリをうまく各プロセスに分配してあげるのがOSの役割です。
さて、複数のプロセスを同時に起動する場合は、当然セキュリティにも気を配らないといけません。例えば、APEXが使用しているメモリの領域を別のソフトが書き換えてしまうとチートができるのでまずいわけです。
こういうのを防ぐためにセグフォというものがあります。面倒なので詳細は省きますが、簡単に言うとプロセスが使って良い領域以外にアクセスしようとすると発生するエラーみたいなものです。
exitを呼ばないときの挙動
ということで、例としてwriteシステムコールを呼ぶだけ(returnもexitも何もしない)というプログラムを実行した時の流れは、大まかに以下のような感じです
- プログラムがメモリにコピーされて、プログラムカウンタがエントリーポイントに設定される
- いろいろあってwriteシステムコールが呼ばれる
- ここでプログラムは終わりであるにも関わらず、プログラムカウンタは進み続ける
- 読み込み権限のない領域にアクセスしてしまっておわり
という感じです。
終わりに
いかがでしたか?
皆さんもコンピュータの気持ちになっていきましょう。
コメント