我在看這段C代碼的反匯編代碼:
#define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC))
int main(void){
// Initialization code
while(1) {
SW1 = GPIO_PORTF_DATA_R&0x10; // Read PF4 into SW1
// Other code
SW2 = GPIO_PORTF_DATA_R&0x01;
}
}
該SW1=行的程式集是(抱歉不能復制代碼):
https://imgur.com/dnPHZrd
以下是我的問題:
- 在第一行,PC = 0x00000A56,PC 92 = 0x00000AB2,它不等于 0x00000AB4,顯示的數字。為什么?
我對 SO 進行了一些研究,發現 PC 實際上指向要執行的 Next Next 指令。
pc 用于讀取時,ARM 模式下有 8 位元組的偏移量,Thumb 模式下有 4 位元組的偏移量。
然而 0x00000AB4 - 0x00000A56 = 0x5E = 94,它既不匹配 92 8 也不匹配 92 4。我哪里錯了?
參考:
ldr [pc, #value] 的奇怪行為
為什么ARM PC暫存器指向下一條要執行的指令之后的指令?
LDR Rd,-Label vs LDR Rd,[PC Offset]
uj5u.com熱心網友回復:
來自 ARM 檔案
Operation
address = (PC[31:2] << 2) (immed_8 * 4)
Rd = Memory[address, 4]
pc 是 0xA56 4 因為前面有兩條指令,這是拇指所以 4 個位元組。
(0xA5A>>2)<<2 (0x17*4)
or
(0x00000A5A&0xFFFFFFFC) (0x17<<2)
0xA58 92=0xA64
這是一個 LDR,因此理想情況下它是基于字的地址。由于拇指指令可以位于非字對齊的地址上,因此您當然首先要添加兩條指令(thumb2 使這變得復雜,但為拇指添加了四個)。然后將偏移量的低兩位 (LDR) 歸零,因此需要將其轉換為位元組,乘以四。如果您考慮編碼的每個部分,這會使編碼更有意義。在 arm 模式下,PC 已經是字對齊的,因此不需要 step(在 arm 模式下,您有更多的位用于立即數,因此它是基于位元組而不是基于字的),這使得 arm 和拇指之間的偏移編碼可能會造成混淆。
各種檔案將以不同的方式顯示數學,但它仍然是相同的數學。PC 是唯一令人困惑的部分,尤其是拇指。對于 ARM,您在前面添加 8,兩個,對于拇指,它基本上是 4,因為執行無法判斷是否有拇指 2 到來,如果他們嘗試這樣做,它會破壞很多事情。因此,請為前面的兩個添加 4,以供拇指使用。由于thumb 被壓縮,因此它們不使用位元組偏移量,而是使用4 倍范圍的字偏移量。同樣,這個和/或其他指令只能向前看,不能向后看,所以無符號偏移量。這就是為什么在拇指中組裝東西時會出現對齊錯誤的原因,而手臂中的東西只是未對齊(并且根據架構和設定,你會得到什么)。Thumb 不能為這樣的指令編碼任何地址。
為了理解指令編碼,特別是基于 pc 的尋址,最好回到早期的 ARM ARM(在 armv5 之前,但如果不是,則獲得 armv5 一個)以及 armv6-m 和 armv7-m 和完整大小的 armv7-ar。并查看每個的偽代碼。較舊的通常具有最好的偽代碼,但有時它們會忽略地址低位的掩碼。沒有檔案是完美的,它們和其他所有檔案一樣都有錯誤。當然,與您使用的內核相關的架構是芯片供應商使用的 IP 的官方檔案(甚至到 TRM 的特定版本,因為這些可能以不兼容的方式從一個版本到另一個版本)。但是,如果該檔案不是很清楚,您有時可以從其他人那里得到一些想法,經過檢查,這些想法具有兼容的說明和架構特征。
uj5u.com熱心網友回復:
您錯過了 Thumb 模式規則的關鍵部分,在您鏈接的一個問題中參考(為什么 ARM PC 暫存器指向下一個要執行的指令之后的指令?):
對于所有其他使用標簽的指令,PC 的值是當前指令的地址加上 4 個位元組,結果的 bit[1] 清零以使其字對齊。
(0xA56 4) & -4=0xA58是 PC 相關事物在執行期間相對于的位置ldr r0, [PC, #92]((0xA56 4) & -4) 92=0xab4,反匯編程式計算的位置。它等同于 do
0xA56 & -4=0xa54then4 92,因為4不修改位 #1; 您可以考慮在添加之前或之后清除它4。但是你不能在加上 PC 相對偏移后清除該位;對于其他指令,如ldrb. (拇指模式ldr以字為單位編碼偏移量以更好地利用有限的位數,因此縮放的偏移量以及最終的加載地址始終清除位 [1:0]。)
(感謝 Raymond Chen 發現這一點;我最初也錯過了!)
另請注意,您的除錯器在斷點處停止時會向您顯示 PC 值,但這是您停止的指令的地址。(因為這就是 ARM 例外的作業方式,我假設,保存要回傳的實際指令,而不是一些偏移量。)在指令執行期間,與 PC 相關的東西遵循不同的規則。并且除錯器不會“烹飪”這個值來顯示在其執行期間PC將是什么。
該規則不是“相對于這條指令的結束/下一條指令的開始”。 說明該規則在這種情況下碰巧得到正確答案的答案和評論,但在其他 Thumb 情況下會得到錯誤答案,例如在LDR Rd,-Label vs LDR Rd,[PC Offset]中發生 PC 相對加載指令從 4 位元組對齊的地址開始,因此 PC 的第 1 位已被清除。
您的 LDR 位于0xA56設定位 #1 的地址,因此向下舍入會產生影響。并且您的ldr指令使用了 2 位元組編碼,而不是像您可能需要更大偏移量的 Thumb2 32 位指令。這兩件事都意味著向下舍入 4 恰好是下一條指令的地址,而不是 2 條指令之后或這條指令的中間。
uj5u.com熱心網友回復:
由于程式計數器指向下一條指令,當它在地址處執行LDR時0x00000A56,程式計數器將保存下一條指令的地址,即0x00000A58。
0x0A58 0x5C (decimal 92) == 0x00000AB4
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/395669.html
