【问题标题】:GNU as: how to load a .bss/.data symbol to a register?GNU as:如何将 .bss/.data 符号加载到寄存器中?
【发布时间】:2015-07-29 12:53:02
【问题描述】:

我的问题是非常基本的。我正在用汇编程序制作我的第一个裸机程序。架构是 ARMv7-M,我使用的是 GNU,我正在用 UAL 编写代码。

我在.bss(或.data,没关系)中有一个变量声明如下:

.lcomm a_variable, 4

然后我想在程序的某个地方读取它的值。为此,我首先将其地址加载到寄存器中,然后将变量本身的值加载到另一个寄存器中:

adr     r0, a_variable
ldr     r1, [r0, #0]

到目前为止一切顺利。编译后的对象包含我的 a_variable 符号:

00000000 b a_variable

生成的指令如下所示:

0:  f2af 0004   subw    r0, pc, #4
4:  6801        ldr     r1, [r0, #0]

问题始于我想将对象链接到生成的图像中。 ld 将 a_variable 符号重新定位到新地址的最终 .bss 部分:

20001074 b a_variable

但最终代码保持不变,程序确实尝试从地址 0x0 而非 0x20001074 读取 a_variable

我希望 ld 以某种方式替换新地址,因为当您链接由 GCC 编译的对象时,它似乎会这样做。我的意思是,如果我写一段 C 代码做类似的事情:

static int a_variable;
void foo(void)
{
    a_variable = 5;
}

...然后我在目标文件中得到以下说明:

0:  f240 0300   movw    r3, #0
4:  f2c0 0300   movt    r3, #0
8:  2005        movs    r0, #5
a:  6018        str r0, [r3, #0]

...但最终的图像看起来像这样:

800c:       f242 338c       movw    r3, #9100       ; 0x238c
8010:       f2c0 0301       movt    r3, #1
8014:       2005            movs    r0, #5
8016:       6018            str     r0, [r3, #0]

So ld 似乎用真实地址代替了左边的占位符。

我的问题是为什么这在手写汇编代码的情况下不起作用?我错过了什么?

【问题讨论】:

  • ADR 指令仅适用于同一节中的标签。汇编器或链接器应该产生了错误。
  • 嗯,它没有。可能是因为a_variable和adr指令在联动之前距离不是很远(其实都是同一个0地址)?
  • 我认为这只是一个错误。汇编器在 ARM 模式下给出错误:Error: symbol .bss is in a different section。您的代码是否需要与位置无关?
  • 我猜没有。这只是我只想在我的 stm32f4 MCU 上运行的应用程序。

标签: assembly gnu armv7 binutils


【解决方案1】:

ADR 指令仅在与在同一节和源文件中定义的附近符号(Thumb2 模式下为 +/- 4095)一起使用时才有效。 GNU 汇编器在引用不同部分的符号时应该给出错误。在 ARM 模式下,您的代码会生成 Error: symbol .bss is in a different section 错误,但显然 GAS 在 Thumb 模式下处理 ADR 指令的方式存在一个错误,导致它静默接受它。

相反,您可以使用 LDR 或 MOVW/MOVT 指令将任意 32 位常量(包括地址)加载到寄存器中。 LDR 指令会将地址放入常量池并从那里加载,而 MOVW/MOVT 指令分两步形成常量,就像编译器一样。前一条指令只占用 6 个字节(指令 2 个,常量 4 个),后两条指令占用 8 个字节。例如:

    .syntax unified
    .arch armv7-m
    .code 16

    .bss
    .lcomm a_variable, 4

    .text

    ldr     r1, =a_variable
    movw    r2, #:lower16:a_variable
    movt    r2, #:upper16:a_variable

在组装、链接和反汇编时给出:

$ arm-linux-gnueabi-as -o test.o test.s
$ arm-linux-gnueabi-ld -Tbss=f0000000 test.o
arm-linux-gnueabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000010074
$ arm-linux-gnueabi-objdump -d a.out
...    
00010074 <.text>:
   10074:       4902            ldr     r1, [pc, #8]    ; (10080 <__bss_start-0x10f80>)
   10076:       f240 0200       movw    r2, #0
   1007a:       f2cf 0200       movt    r2, #61440      ; 0xf000
   1007e:       0000            movs    r0, r0
   10080:       f0000000        .word   0xf0000000

【讨论】:

  • :lower16: 和 :upper16: - 这就是我需要的东西!事实上,我知道 movw 和 movt 指令,因为我不知道如何将 GAS 标签连接到它们。至于ldr ...实际上我也尝试过,但我没有注意到汇编程序在包含地址的.text 部分(在您的示例中为地址10080)中存储了另一个.word。所以现在两种方式对我来说都很好。谢谢!唯一奇怪的是adr。有了它,我仍然得到错误的代码。
  • 嗯。与 movw/movt 相比,ldr 为您节省了一个字节。所以乍一看,它似乎更可取。
猜你喜欢
  • 2015-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 2012-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多