覆蓋正在執行的執行檔或 .so
我有一個關於覆蓋正在執行的執行檔或覆蓋一個或多個正在執行的程序正在使用的共享庫 (.so) 文件的問題。
回到過去,出於顯而易見的原因,覆蓋正在執行的執行檔是行不通的。甚至還有一個特定的 errno 值 ETXTBSY 涵蓋了這種情況。
但是很長一段時間以來,我注意到當我不小心嘗試覆蓋正在執行的執行檔時(例如,通過觸發最後一步
cc -o exefile
在exefile
恰好正在執行的建構上的建構),它可以工作!所以我的問題是,它是如何工作的,它是否記錄在任何地方,依賴它是否安全?
看起來有人可能已經調整
ld
以取消其輸出文件的連結並創建一個新文件,只是為了消除這種情況下的錯誤。我不能完全確定它是一直這樣做,還是僅在需要時這樣做(也就是說,可能在它嘗試覆蓋現有文件並遇到 ETXTBSY 之後)。ld
而且我在’s 的手冊頁上沒有看到任何提及這一點。(我想知道為什麼人們不抱怨ld
現在可能會破壞他們的硬連結,或者改變文件所有權,等等。)附錄:這個問題並不是專門關於
cc
/的ld
(儘管這確實是答案的重要組成部分);問題實際上只是“我怎麼再也看不到 ETXTBSY 了?它仍然是一個錯誤嗎?” 答案是,是的,它仍然是一個錯誤,只是在實踐中很少見。(另請參閱我剛剛發佈到我自己的問題的澄清答案。)
它取決於核心,並且在某些核心上,它可能取決於執行檔的類型,但我認為如果您嘗試打開正在執行的執行檔進行寫入或執行文件,所有現代系統都會返回 ETXTBSY(“文本文件忙”)開放寫作。文件表明在 BSD 上一直都是這種情況,但在早期的 Solaris 上並非如此(後來的版本確實實現了這種保護),這與我的記憶相符。在 Linux 上一直如此,或者至少是 1.0。
適用於執行檔的內容可能適用於動態庫,也可能不適用於動態庫。覆蓋動態庫會導致與覆蓋執行檔完全相同的問題:指令會突然從新文件中的相同舊地址載入,這可能有完全不同的東西。但事實上並非所有地方都是如此。特別是在 Linux 上,程序呼叫
open
系統呼叫以在後台打開一個動態庫,其標誌與任何數據文件相同,Linux 很高興允許您重寫庫文件,即使正在執行的程序可能會從其中載入程式碼隨時。大多數核心允許在執行文件時刪除和重命名文件,就像它們允許在打開文件進行讀取或寫入時刪除和重命名文件一樣。就像打開的文件一樣,在執行過程中被刪除的文件只要還在使用中,就不會真正從儲存介質中刪除,即直到執行檔的最後一個實例退出。Linux 和 *BSD 允許,但 Solaris 和 HP-UX 不允許。
刪除一個文件並用相同的名稱寫入一個新文件是非常安全的:要載入的程式碼和包含該程式碼的打開(或正在執行的)文件之間的關聯是由文件描述符而不是文件名決定的。它還有一個額外的好處是它可以原子地完成,通過寫入一個臨時文件然後將該文件移動到適當的位置(
rename
系統呼叫原子地用源文件替換現有的目標文件)。它比 remove-then-open-write 要好得多,因為它不會臨時放置無效的、部分寫入的執行檔是否
cc
覆蓋ld
他們的輸出文件,或者刪除它並創建一個新文件,取決於實現。GCC(至少是現代版本)和 Clang 這樣做,在這兩種情況下,unlink
如果它存在,則呼叫目標然後open
創建一個新文件。(我想知道為什麼他們不做 write-to-temp-then-rename。)我不建議依賴這種行為,除非作為一種保護措施,因為它不適用於每個系統(它可能適用於每個現代系統的執行檔,但不適用於共享庫),並且通用工具鏈不會在最好的辦法。在您的建構腳本中,始終在臨時文件下生成文件,然後將它們移動到位,除非您知道底層工具會這樣做。