3.4.1. 自我修改的程式碼

在電腦時代的早期,記憶體是很珍貴的。人們不遺餘力地減少程式的大小,以為程式資料騰出更多的空間。一個經常使用的技巧是,隨著時間改變程式自身。偶爾仍舊會找到這種自我修改的程式碼(Self Modifying Code,SMC),如今多半是為了效能因素、或者用在安全漏洞上。

一般來說應該避免 SMC。雖然它通常都被正確地執行,但有著並非如此的邊界案例(boundary case),而且沒有正確完成的話,它會產生效能問題。顯然地,被改變的程式碼無法維持在保存被解碼指令的追蹤快取中。但即使程式碼完全(或者有時候)不會被執行,因而不會使用到追蹤快取,處理器也可能會有問題。若是接下來的指令在它已經進入管線的期間被改變了,處理器就得丟掉大量的成果,然後從頭開始。甚至有處理器的大多狀態都必須被丟棄的情況。

最後,由於處理器假定––為了簡化起見,而且因為這在 99.9999999% 的情況下都成立––程式碼分頁是不可修改的(immutable),所以 L1i 的實作不會採用 MESI 協定,而是一種簡化的 SI 協定。這表示,若是偵測到了修改,就必須做出許多的悲觀假設。

強烈地建議盡可能避免 SMC。記憶體不再是如此稀有的資源。最好是撰寫各自的函式,而非根據特定的需求修改一個函式。或許有天 SMC 的支援能夠是可選的,而我們就能夠以這種方式偵測出嘗試修改程式碼的漏洞程式碼(exploit code)。若是真的必須使用 SMC,寫入操作應該要繞過快取,以免因為 L1i 所需的 L1d 的資料造成問題。關於這些指令的更多訊息,見 6.1 節。

在 Linux 上,識別出包含 SMC 的程式通常非常容易。使用正規工具鏈(toolchain)建構的話,所有程式的程式碼都是防寫的(write-protected)。程式設計師必須在連結期(link time)施展強大的魔法,以產生程式分頁能夠被寫入的可執行檔。當這種情況發生時,現代的 Intel x86 與 x86-64 處理器具有專門的、計算自我修改的程式碼使用次數的效能計數器。有了這些計數器的幫助,非常輕易就能夠識別有著 SMC 的程式,即使程式由於寬鬆的許可而成功執行。

results matching ""

    No results matching ""