我正在從最新版的《64位匯編語言的藝術》一書中學習Windows下的X64匯編語言和MASM64。
我對書中的那句話有疑問:
在一種情況下,您確實必須擔心記憶體中的 MMU 頁面組織。有時訪問(讀取)超出記憶體中資料結構末尾的資料是很方便的。但是,如果該資料結構與 MMU 頁的末尾對齊,則訪問記憶體中的下一頁可能會出現問題。記憶體中的某些頁面不可訪問;MMU 不允許在該頁面上進行讀取、寫入或執行。嘗試這樣做會產生 x86-64 通用保護(分段)錯誤并中止程式的正常執行。如果您的資料訪問跨越頁面邊界,并且記憶體中的下一頁不可訪問,這將使您的程式崩潰。例如,考慮一個字訪問 MMU 頁面末尾的位元組物件,如圖 3-2 所示。
作為一般規則,您永遠不應該讀取超出資料結構末尾的資料。如果由于某種原因您需要這樣做,您應該確保訪問記憶體中的下一頁是合法的(唉,現代 x86-64 CPU 上沒有指令允許這樣做;確保訪問的唯一方法是合法是確保在您訪問的資料結構之后有有效的資料)。
所以我的問題是:假設我有那個確切的情況。資料段末尾的字變數。如何防止例外?通過手動填充 00h 單元格?正確地將每個變數與其大小對齊?如果我確實對齊所有內容,如果最后一個變數是一個跨越 4k 邊界的 qword,會發生什么?如何防止這種情況?
MASM 會自動分配另一個順序資料段來容納它嗎?
uj5u.com熱心網友回復:
在已知包含任何有效位元組的頁面中讀取任何位置都是安全的,例如在具有未對齊的foo: dq 1. 如果你有它,它總是安全的mov rax, [foo]。
您的匯編器 聯結器將確保 , 和 中的所有存盤.data實際上.rdata都由.bss作業系統允許您觸摸的有效頁面支持。
你的書的重點是你可能有一個 3 位元組結構的陣列,例如 RGB 像素。x86 沒有 3 位元組加載,因此加載整個像素結構mov eax, [rcx]實際上會加載 4 個位元組,包括您不關心的 1 個位元組。
通常這很好,除非[rcx 3]在未映射的頁面中。(例如,緩沖區的最后一個像素,在頁面末尾結束,并且下一頁未映射)。進入另一個你不需要資料的快取行對性能來說并不是很好,所以這是一個權衡與 2 或 3 個單獨的負載,如movzx eax, word ptr [rcx]/movzx edx, byte ptr [rcx 2]
這在 SIMD 中更為常見,您可以在加載它們后在暫存器中一次更多地使用多個元素。喜歡movdqu xmm0, [rcx]加載 16 個位元組,包括 5 個完整像素和另一個像素的 1 個位元組,我們不會在這個向量中處理。
(平面 RGB 沒有這個問題,其中所有 R 分量都是連續的。或者一般來說,AoS 與 SoA = 陣列結構對 SIMD 有好處。如果你展開回圈,你也不會遇到這個問題3 或其他東西,所以 3x 16 位元組向量 = 48 位元組,覆寫 16x 3 位元組像素,如果需要,可能會進行一些洗牌,或者如果您需要不同的常量來與結構或像素的不同組件或其他任何內容對齊,則可以使用 3 個不同的向量常量.)
如果回圈遍歷陣列,則在最終迭代中會遇到同樣的問題。如果陣列大于 1 個 SIMD 向量(XMM 或 YMM),而不是最后一個n % 4元素的標量,您有時可以安排執行在陣列末尾結束的 SIMD 加載,因此它與之前的完整向量部分重疊. (為了減少分支,留下 1..4 個清理元素而不是 0..3,所以如果n是向量寬度的倍數,那么“清理”是另一個完整的向量。)這對于制作 ASCII 字串的小寫副本等非常有用:可以在任何給定位元組上重做作業,而且你是沒有就地存盤,因此您甚至沒有存盤轉發停頓,因為您不會有與先前存盤重疊的負載。對陣列求和(需要避免重復計算)或就地作業不太容易。
另請參閱x86 和 x64 上同一頁面內的緩沖區末尾是否安全?
strlen對于您不知道您被允許讀取的資料是否延伸到下一頁的情況,這是一個挑戰。(除非您一次只讀取 1 個位元組,這比使用 SSE2 慢 16 倍。)
AVX-512 具有帶故障抑制的屏蔽加載/存盤,因此vmovdqu8 xmm0{k1}{z}, [rcx]k1=0x7F 的 a 將有效地成為 15 位元組加載,即使第 16 位元組(屏蔽為零)延伸到未映射的頁面也不會出錯。AVXvmaskmovps等也是如此。但是在 AMD 上,商店版本的速度很慢。
另請參閱使用未對齊緩沖區進行矢量化:使用 VMASKMOVPS:從未對齊計數中生成掩碼?或者根本不使用那個insn
嘗試這樣做會產生 x86-64 通用保護(分段)錯誤
實際上是#PF訪問未映射或權限被拒絕的頁面的頁面錯誤。但是,是的,同樣的區別。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/486326.html

