プロセッサは、プログラム カウンターが指すメモリをフェッチしてデコードします。
デコーダーは、命令を理解できない場合、「無効な命令」例外をスローできます。その後、無効な命令を理解しようとする例外ハンドラ (つまり、OS) にジャンプします。多くの場合、ソフトウェアで処理できるサポートされていない命令 (浮動小数点除算など) である可能性があり、中断したところから実行を再開できます。
ただし、それが真に無効な命令である場合、プログラムは何らかの例外/クラッシュでエラーになります( x86では「不正な命令」と思います)。
コードをデータから分離するのに役立つテクニックの 1 つは、コードを仮想メモリ内の異なる「ページ」に配置することです。OS は、コードが置かれているページを「読み取り専用」としてマークできます。このようにして、コードを上書きしようとするプログラムは例外をスローします。一部のシステムでは、「Fault on Execute」など、さらに保護が強化されています。このページのコードを実行しようとすると例外がスローされます (詳細はこちら: http://www.tldp.org/LDP/tlk/mm/memory. html )。
ただし、一部のコードは実際にはプログラムによって「変更可能」である必要があるという点で、少し奇妙になる可能性があります (別名、「自己変更コード」)。通常、命令キャッシュとデータ キャッシュは別々にあるため、これは困難です。通常、命令キャッシュはその周りの世界を認識していません。キャッシュに含まれるアドレスに誰かが新しいデータを書き込んだことを認識しません。一部のプラットフォームでは、i-キャッシュが更新された命令を再フェッチできるように、命令データを変更した後に i-キャッシュをフラッシュするのはプログラマの仕事です。
ただし、最終的には、PC がコード以外のアドレスに決してジャンプしないようにするのは、プログラマー/コンパイラーの仕事です。
(もちろん、悪意のあるユーザーは、PC を自分の悪いコードにジャンプさせようとしますが、スタック バッファー オーバーフロー攻撃は別の投稿に譲ります。)