当我使用 gcc -S 从 C 代码创建 ARM 汇编代码时,我得到了我不知道的 LDR 指令的变体。具体来说,我得到"ldr r3,.L5"指令,其中".L5"是由编译器定义的标签。 我不清楚为什么我没有得到伪指令"ldr r3,=。L5",这应该是在寄存器中加载任意数字的唯一方法。
更多详情:
- 我从这个 C 代码(文件名:sum_squares_C.c(开始:
int sum;
int main(){
sum = 0;
for(int i=1; i<=n; i++){
sum = sum + i*i;
}
}
然后在Raspeberry PI上,我使用"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
将.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相对负载(假设它可以达到(。
这两者都是汇编程序特定的,因为汇编语言是由汇编程序而不是目标定义的。
并非所有手臂汇编程序都支持 =address 快捷方式,应谨慎使用,对于某些值,它不会在具有 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 相对负载。 现在是的,当以这种方式读取输出时,您需要查看/理解/忽略 andeq 反汇编该行,我们正在查看0x00001234的值并查看生成的指令。
如果你选择尝试各种工具,你不应该总是假设 =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 的反汇编非常有趣,几乎很有趣。