我正在做一個二進制分析專案,我正在建立一個將匯編翻譯成llvm的提升器。我建立了一個記憶體模型,但對str和ldr arm匯編指令如何在記憶體上作業感到有點困惑。所以我的問題是:例如,給定一個記憶體地址0000b8f0,我想在其中存盤一個64位的十進制數值20000000。str指令是將整個20000000存盤在地址0000b8f0中,還是將其分成幾個位元組并將第一個位元組存盤在0000b8f0中,第二個位元組存盤在0000b8f1中,第三個位元組存盤在0000b8f2中,依此類推。 ...同樣,從一個地址(0000b8f0)加載時,ldr指令是只取存盤在0000b8f0的位元組還是取0000b8f0-0000b8f4的全部位元組。
對不起,如果我的問題非常基本,但我需要確保我正確地實作str和ldr對我的記憶體模型的影響。
uj5u.com熱心網友回復:
從邏輯上講1,記憶體是一個8位位元組的陣列。
字加載/存盤一次訪問一個以上的位元組,就像C語言中的SIMD本征,或者像((char*)my_int)[2]加載一個int的第三個位元組的反義詞。
C的記憶體模型是圍繞著支持更廣泛訪問的位元組尋址機器而設計的(比如PDP-11或ARM),所以如果你了解char*在C中是如何訪問其他物件的物件表示的,例如,為什么memcpy可以作業,這就是你所習慣的。
(我沒有使用C語言的例子,將一個int*指向一個char陣列,因為C語言中的嚴格鋸齒規則使得這種行為無法定義。 在ISO C中,只有char*被允許別名為其他型別。Asm對于訪問任何寬度的記憶體位元組有明確的行為,與早期存盤的任何部分或全部重疊,就像GNU C在編譯時使用-fno-strict-aliasing來禁用基于型別的別名分析/優化。
str是一個32位的字存盤;它一次寫入所有4個位元組。如果你從0000b8f1、...2或...3加載,你會得到第2、3或4個位元組,所以str相當于4條獨立的strb指令(通過移位來提取正確的位元組),除了明顯缺乏原子性和性能。
str總是從一個32位暫存器中存盤4個位元組。如果一個暫存器持有一個像2這樣的值,那就意味著上面的位元組都是0。
ARM可以存盤32位暫存器中的4個位元組。
ARM可以是big-endian或little-endian。 我認為現代 ARM 系統最常采用小-endian 方式,就像 x86 一樣,因此數值中最不重要的位元組被存盤在最低地址。
注意,0000b8f4 是下一個字的低位元組;它是一個 4 位元組對齊的地址。
另外,用
位于0000b8f0的位元組不能單獨容納20000000;一個位元組沒有那么大,如果這是你所問的。
int64_t存盤20000000將需要兩個32位存盤。例如,兩個str指令,或ARMv8的stp來做一對暫存器的64位存盤,或一個stm存盤-多重指令的兩個暫存器。 或者八個strb位元組存盤指令。
str 或 stp 的 1/4 或 1/8 的資料量之外,也是如此。
uj5u.com熱心網友回復:
如果你問的是軟體是如何作業的,從高層次的角度來看,如果你瞇起眼睛是的。 地址0x0000b8f0是基礎地址,值0x20000000被存盤為
0xb8f0 0x00
0xb8f1 0x00
0xb8f2 0x00
0xb8f3 0x20
但是,硬體是一個完全不同的故事。 首先,你有許多總線,在像ARM這樣的內核中,你可能有一個內部L1快取(你可能啟用也可能不啟用)。 以及芯片供應商連接的AHB/axi/等總線。 這些通常都是32或64位寬(核心總線的外部,芯片的內部)。 因此,假設沒有mmu,這將是一個地址為0xb8f0的單一總線事務,資料為0x20000000或0xXXXXXX20000000,其中的XXes可能是垃圾,往往是陳舊的,而不是假設為零,為什么要浪費門? 內部或外部的快取在物理上不會由位元組寬的部件創建,它們可能是32位寬或64位加奇偶校驗或cc,所以是33或65或40或72或其他。 我們并不假定內部的srams是8位寬的倍數,單元庫中有數百種尺寸和形狀(寬度和深度)的srams。
假設在總線上的讀取被假定為總線寬度,這是很常見的,所以如果你讀取一個單一的位元組,或者認為你來自軟體,它可能會導致一個完整的32或64或更寬的,讀取,因為它穿越總線,所有這些位元組/位將回來,處理器(核心)本身將隔離它感興趣的位元組,并對它做任何指令想要的東西(例如ldrb)。 另一方面,如果你想存盤一個位元組,那么硬體需要這樣做,所以使用了一些方案,對于axi/ahb等,使用了一個位元組屏蔽,所以如果是32位總線,那么有4位位元組屏蔽/啟用,每個位元組通道一個。 因此,將一個位元組存盤到地址0x0123基本上是寫到0x0120,位元組掩碼為0b1000,以表明該位元組通道正在獲得資料,該位元組在適當的位元組通道上(其他位元組被認為是垃圾/陳舊,沒有預期)。
假設你有一個快取,哪個層并不重要。 如前所述,它們最好是供給它們的總線的倍數,所以如果是32位的總線,那么就是32位加奇偶校驗或ecc寬(33、40,等等)。 一個單位元組的存盤會導致一個讀-修改-寫的程序,因為快取的sram本身只能在32或64位寬的事務中讀/寫(而且地址是這樣的,你不使用/需要較低的地址位,它們被剝離以使基于字或雙字的尋址),所以邏輯會讀取整個字或雙字,修改你想寫的一個位元組,然后再寫回sram中。 這是一種性能上的沖擊,這取決于整體架構是否能夠輕松檢測到它,以及你所損失的整體性能(你可能有很多其他開銷,就像 x86 一樣,無法看到它)。
硬體中一個字大小的存盤絕不等同于在四個不同地址的四個位元組大小的存盤。 根據另一位回答這個問題的人的要求,我已經在這個網站上證明了這一點。 從軟體的角度來看,是的,如上圖所示(假設是小結尾或be-8大恩典),這是一個功能上的等價物。 如果你做了一個字的寫入,你可以做位元組的讀取來訪問那些基于位元組地址的位元組,如果你做了位元組的寫入,你可以做一個字的讀取,并在該讀取中看到那些單獨交易的位元組。
同時也要理解,一個存盤復數,stm,不被認為是單獨的32位事務。 無論是32位還是64位總線,每一個事務都有一個開銷,如果不是更多的話,總線的處理器端會對總線的芯片/總線控制器端說,我想做一個寫,好的,我已經準備好讓你寫。 你要宣告一個長度,即你要發送的總線寬度專案的數量。 因此,在32位總線上的一個stmia sp!,{r0,r1,r2,r3}將是一個有4個資料周期的單一事務,握手后在資料總線上有4個時鐘。 對于64位寬的總線,這不是假設,這就是為什么arm現在要求堆疊指標有64位對齊。 如果地址是64位對齊的,那么它就是一個長度為2的事務,所以資料總線上有兩個時鐘。 但是,如果它不是64位對齊,而是32位對齊,那就是三個事務,一個是32位的值(位元組屏蔽,把總線切成兩半),一個是64位事務,然后是32位事務。 如果處理器支持無對齊(甚至不是32位也不是16位),那么我還沒有見過這樣的內核,所以不知道,但我認為這也是一個以上的交易。
所以純粹從軟體編譯器的角度來看。 一個字大小的存盤就是一個字大小的存盤,四個位元組,一個基礎地址的32位數字。 在功能上等同于在基址 0、 1、 2、 3的四個位元組大小的寫入。 但在性能上不等同,也不等同于指令。 加載也是如此。
許多編譯器作者將在他們的設計中更進一步,盡可能地避免位元組大小的加載/存盤指令。 對于基于位元組/字符的
,你有兩個選擇a = a 1;
r0 = r0 1
r0 = r0 & 0xff;
或
左移算術24
右移算術24
或者
r0 = r0 1
后者知道進入該加法時,r0的上位已經按照變數型別(有符號或無符號)進行了填充,盡管是一個32位的暫存器和一個8位的變數型別,但仍然是32位的表示。 編譯器決定什么時候必須修改它以表示實際寬度。 即使有
a = a 1;
*bptr = a。
使用8位變數和8位指標,你仍然可以在ARM中使用32位暫存器來完成這個任務。
添加r0,r0,#1
strb r0,[r1]
由于存盤將忽略/屏蔽暫存器的前24位,因此不需要預處理。
甚至更短。用str指令寫0x20000000到0xb8f0是一條指令,并且是一個離開處理器的單一事務(或內部到L1快取)。 它不是四個單獨的位元組寫入。 如果你選擇使用strb四次,四個位元組的寫入是一個功能上的等價物(同時假設在isr試圖讀取這個字大小的值時沒有發生中斷)。 但是你必須自己明確地進行四個位元組的寫入。
。轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/320395.html
標籤:
下一篇:MARIE:如何列印一個名字
