【问题标题】:LDR pseudoinstructionLDR 伪指令
【发布时间】:2020-01-29 11:35:02
【问题描述】:

当我使用 gcc -S 从 C 代码创建 ARM 汇编代码时,我得到了一个我不知道的 LDR 指令变体。具体来说,我得到“ldr r3,.L5”指令,其中“.L5”是编译器定义的标签。我不清楚为什么我没有得到伪指令“ldr r3, =.L5”,这应该是在寄存器中加载任意数字的唯一方法。

更多细节:

  1. 我从这个C代码开始(文件名:sum_squares_C.c):
int sum;

int main(){
    sum = 0;
    for(int i=1; i<=n; i++){
            sum = sum + i*i;
    }
}
  1. 然后在 Raspeberry PI 上,我使用“gcc -O0 -S sum_squares_C.c”进行编译,编译器版本为 gcc (Raspbian 8.3.0-6+rpi1) 8.3.0。

    李>
  2. 输出是这个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”(出于教学原因,我需要这个)?

谢谢! 弗朗切斯科

【问题讨论】:

  • 而哪个值存储在.L5
  • 好像是变量sum的地址。
  • 不等价的一个 ldr r3,.L5 将地址 .L5 的值(标签即地址)放入 r3,另一个 ldr r3,=.L5 将 .L5 的地址放入r3。完全不同。对于前者,汇编器将用 pc 相对负载替换它。对于后者,汇编器将尝试在附近的池中创建一个值并创建一个 pc 相对负载,然后链接器将在知道后将地址放入 .L5 中
  • 最好/最好先检查拆卸,然后如果需要返回到组装。或者至少将组装和拆卸相互比较,大多数这类问题都会自己回答。
  • 你没有定义n是吗?如果你优化它,那么它就是死代码,更难阅读未优化的代码。如果您要返回总和但在函数内部声明 n 并优化 gcc 应该简单地计算结果并返回而不是生成循环,如果您要将 n 作为参数传递给函数然后返回它应该优化的总和变成更简单的非循环形式,但会产生一些代码。

标签: c gcc assembly arm


【解决方案1】:

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,.L5”将由汇编程序在指令“ldr r3,[pc,numeric_offset]”中进行转换。所以“ldr r3,.L5”是另一个伪指令?ARM中有没有参考手册?谢谢!
  • 程序集是由汇编器而不是目标定义的,因此如果您想查看 gnu 汇编器手册,而不是 arm 中的任何内容,您可以。不知道为什么试图将其视为伪指令,它直接转换为您要求它执行的 pc 相对负载。所以它不像 case 的地址,因为至少使用气体并不总是相同的指令,而不是一对一的。
  • @Francesco 我不会将其称为伪指令,但也许有些人会。将内存地址更改为与 PC 相关的地址是汇编程序常做的事情。它对每条分支指令都做同样的事情,我们不称它们为伪指令。
  • @ElliotAlderson 我在哪里说它得到了sum 的值?请注意,汇编语言没有“变量值”;当你提到一个变量时,你的意思是它的地址。 ldr r3, =sumsum 的地址加载到r3,而不是sum 的值。
  • 对不起,我被你的措辞弄糊涂了。
【解决方案2】:

为了节省打字并承担人们可能使用的风险:

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 汇编器都支持 =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 或 add 填充它一个附近的项目并将值放在那里,就像 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 反汇编非常有趣,几乎很有趣。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-10
    • 1970-01-01
    • 2017-04-06
    • 2014-07-29
    • 2019-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多