假設我的 x86-64 代碼看起來像這樣(盡管這個問題更普遍地適用于所有代碼):
mov rbx,7F0140E5247Dh
jmp rbx
如果目標值未對齊,而代碼可能正在執行,覆寫目標常量是否安全?換句話說,我是否可以觀察到部分更新的跳轉目標,從而導致跳轉到不存在的地址?此外,如果目標常量跨越頁面或快取線邊界,這是否安全?
編輯:
我只對更改單個指令感興趣,而不是更改指令邊界位置。
uj5u.com熱心網友回復:
僅當寫入是原子的,只要它不跨越高速快取行邊界,英特爾上的未對齊 qword 寫入就可以保證這一點,但在 AMD 上則不能保證。最低公分母原子性保證是 8 位元組對齊的存盤是原子的,僅此而已。
使用 anxchg來保證原子 RMW。如果常量本身越過快取線邊界,那將非常慢,但我相信這是正確的。(總線鎖,不僅僅是快取鎖;速度太慢了,甚至只針對 split- 都有一個性能計數器lock,甚至還有一個 CPU 功能,至少在內核代碼中會導致該錯誤,因此您可以在 VM 中找到它的實體。)如果對于任何 CPU,該常量都不會跨越有問題的邊界,它應該與對齊的原子操作一樣快。
或者,如果您的 CPU 支持 AVX,則 16 位元組對齊的 SSE/AVX 存盤在具有 AVX 的 CPU 上保證是原子的。(直到最近幾年才被記錄在實踐中基本上是安全的,但幸運的是它可以追溯所有 AVX CPU,沒有新的功能位。)所以如果你能讓你的常量排列不跨越 16 位元組邊界,您可以通過這種方式對其進行更新。(用它們自己覆寫周圍的位元組不會引起問題,除非另一個執行緒也在更新附近的另一個常量。)
如果性能對此很重要(例如每分鐘左右執行一次以上),可能值得使用一些填充或 NOP 來獲得恒定的 8 位元組對齊,特別是如果您可以延長早期指令以不需要實際的 NOP ,甚至它mov r64,imm64本身。(雖然它是 10 個位元組,一條指令的最大長度是 15。)
這并不能完全概括為替換多條指令
在其他情況下,您可能正在重寫一系列指令,其中指令邊界在不同的地方,那將是另一回事。您說這個問題“更普遍”適用,但僅適用于替換立即數或用相同長度的一個替換整個 4 位元組或 8 位元組指令。如果另一個執行緒可能在您正在撰寫的區域內使用 RIP 休眠或運行,那么您必須考慮在更新后從舊序列中的任何可能的 RIP 中提取代碼的情況。正如我所說,改變指令邊界是有問題的。
但是如果你尊重這個限制,交叉修改代碼是 AFAIK 安全的。我認為 Windows 熱補丁會停止其他可能正在運行代碼的執行緒,但我不知道為什么,因為它已經確保有一個足夠大的指令可以覆寫。要么他們過于謹慎,要么我不知道代碼獲取不尊重存盤原子性存在一些風險。也許只是他們不想在未對齊函式的情況下依賴 2 位元組存盤原子性,甚至認為這是默認值,原因與正常編譯器設定不同。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/459709.html
