[英]LDR pseudoinstruction
當我使用 gcc -S 從 C 代碼創建 ARM 匯編代碼時,我得到了一個我不知道的 LDR 指令的變體。 具體來說,我得到了“ldr r3, .L5”指令,其中“.L5”是編譯器定義的標簽。 我不清楚為什么我沒有得到偽指令“ldr r3,=.L5”,這應該是在寄存器中加載任意數字的唯一方法。
更多詳情:
int sum;
int main(){
sum = 0;
for(int i=1; i<=n; i++){
sum = sum + i*i;
}
}
然后在樹莓派上,我使用“gcc -O0 -S sum_squares_C.c”編譯,編譯器版本為 gcc (Raspbian 8.3.0-6+rpi1) 8.3.0。
輸出是這個 ARM 代碼(指令“ldr r3, .L5”在標簽“main”之后的第 7 行):
.arch armv6
.eabi_attribute 28, 1
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 34, 1
.eabi_attribute 18, 4
.file "sum_squares_C.c"
.text
.global n
.data
.align 2
.type n, %object
.size n, 4
n:
.word 1
.comm sum,4,4
.text
.align 2
.global main
.arch armv6
.syntax unified
.arm
.fpu vfp
.type main, %function
main:
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 1, uses_anonymous_args = 0
@ link register save eliminated.
str fp, [sp, #-4]!
add fp, sp, #0
sub sp, sp, #12
ldr r3, .L5
mov r2, #0
str r2, [r3]
mov r3, #1
str r3, [fp, #-8]
b .L2
.L3:
ldr r3, [fp, #-8]
ldr r2, [fp, #-8]
mul r2, r2, r3
ldr r3, .L5
ldr r3, [r3]
add r3, r2, r3
ldr r2, .L5
str r3, [r2]
ldr r3, [fp, #-8]
add r3, r3, #1
str r3, [fp, #-8]
.L2:
ldr r3, .L5+4
ldr r3, [r3]
ldr r2, [fp, #-8]
cmp r2, r3
ble .L3
mov r3, #0
mov r0, r3
add sp, fp, #0
@ sp needed
ldr fp, [sp], #4
bx lr
.L6:
.align 2
.L5:
.word sum
.word n
.size main, .-main
.ident "GCC: (Raspbian 8.3.0-6+rpi1) 8.3.0"
.section .note.GNU-stack,"",%progbits
在我看來,gcc 使用指令“ldr r3, .L5”等同於“ldr r3, =.L5”。 這是正確的嗎? 在哪里可以找到此指令語法的定義? 是否可以強制 gcc 不使用此指令,而是使用“ldr r3, =.L5”(出於教學原因我需要這個)?
謝謝! 弗朗切斯科
ldr r3, .L5
從地址.L5
加載一個字到r3
。 在標簽.L5
有變量sum
的地址。 所以這會將sum
的地址加載到r3
。
ldr r3, =.L5
將ldr r3, =.L5
的地址.L5
到r3
。 然后程序需要再次取消引用它以獲得sum
的地址。 沒有理由這樣做。
當您使用ldr r3, =.L5
匯編存儲的地址.L5
地方,那么從該地址負載和。 所以這:
ldr r3, =.L5
...
.L5:
.word sum
與此相同:
ldr r3, .address_of_L5
...
.L5:
.word sum
...
.address_of_L5:
.word .L5
如您所見,編譯器已經為sum
完成了這項工作。 而不是編寫這個程序集:
ldr r3, =sum
編譯器寫道:
ldr r3, .L5
...
.L5:
.word sum
這正是匯編程序無論如何都會做的事情。 我不知道為什么編譯器要這樣做而不是匯編器。
我不清楚為什么我沒有得到偽指令“ldr r3,=.L5”,這應該是在寄存器中加載任意數字的唯一方法。
請注意,這不是將任意數字加載到寄存器中的唯一方法。 它甚至不是將任意數字加載到寄存器中的真正方法。 這是一個偽指令(如您所知):這不是 CPU 實際可以執行的操作,而是匯編程序可以為您方便“編譯”的操作。
節省打字並承擔一個人可能使用的風險
ldr r3,=sum
ldr r3,[r3]
正如另一個例子中所指出的,匯編程序將在機器代碼中創建與人類在沒有 =address 技巧的情況下可以輸入的內容相同的內容
ldr r3,address_of_sum (without the =)
ldr r3,[r3]
...
address_of_sum: .word sum
並且第一個 ldr (不是偽,因為它直接轉換為已知指令,一對一)是 pc 相對負載(假設它可以達到)。
盡管這兩個都是特定於匯編程序的,因為匯編語言是由匯編程序而不是目標定義的。
並非所有 arm 匯編程序都支持 =addresss 快捷方式,應謹慎使用,對於某些值,它不會變成池中具有 pc 相對負載的字。
對於這樣的問題,首先檢查反匯編,大多數時候會回答你的問題,最好先檢查反匯編,然后再檢查裝配。 編譯器生成的匯編不像反匯編那么容易閱讀和遵循,尤其是鏈接時。 從優化的代碼中學習比未優化的代碼更容易學習,因為很多代碼都是這個堆棧(或在這種情況下是全局的)變量。
ldr r3,=0x1000
ldr r3,=0x1234
b .
00000000 <.text>:
0: e3a03a01 mov r3, #4096 ; 0x1000
4: e51f3000 ldr r3, [pc, #-0] ; c <.text+0xc>
8: eafffffe b 8 <.text+0x8>
c: 00001234 andeq r1, r0, r4, lsr r2
在一種情況下,它可以生成一個 mov,然后它不能從池中分配並將值放在那里,然后進行 pc 相對加載。 現在是的,當以這種方式讀取輸出時,您需要查看/理解/忽略我們正在查看值 0x00001234 並查看生成的指令的 andeq 反匯編那一行。
如果你選擇嘗試各種工具,你不應該總是假設 =address 技巧會起作用,它現在適用於 gnu,如果它可以找到一個池,如果它不能,那么你要么自己打字,要么添加一個 .pool 或其他任何東西另一個做同樣事情的偽代碼是幫助匯編程序根據需要找到這個值的位置。
我希望匯編程序始終將此(=地址)放在池中以供外部參考,但從技術上講,工具鏈可以將占位符放在那里並讓鏈接器用 mov 填充它或添加附近的項目並將值放在那里,就像 binutils 所做的那樣,將 bl 放置到外部引用。
氣體:
ldr r3,=sum
b .
00000000 <.text>:
0: e51f3000 ldr r3, [pc, #-0] ; 8 <.text+0x8>
4: eafffffe b 4 <.text+0x4>
8: 00000000 andeq r0, r0, r0
鏈接器稍后會像編譯器輸出一樣填寫地址。 現在-0反匯編很有趣,幾乎很有趣。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.