GNU as:如何将 .bss/.data 符号加载到寄存器?
GNU as: how to load a .bss/.data symbol to a register?
我的问题很简单。我正在用汇编程序制作我的第一个裸机程序。该体系结构是 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]
当我想 link 将对象 link 转换为生成的图像时,问题 就开始了。 ld 将 a_variable 符号重新定位到新地址的最终 .bss 部分:
20001074 b a_variable
但最终代码保持不变并且程序确实尝试从地址 0x0 读取 a_variable 而不是从0x20001074.
我希望 ld 以某种方式替换新地址,因为当您 link 对象由 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]
ld 似乎用真实地址代替了左边的占位符。
我的问题是为什么这在手写汇编代码的情况下不起作用?我想念什么?
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
我的问题很简单。我正在用汇编程序制作我的第一个裸机程序。该体系结构是 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]
当我想 link 将对象 link 转换为生成的图像时,问题 就开始了。 ld 将 a_variable 符号重新定位到新地址的最终 .bss 部分:
20001074 b a_variable
但最终代码保持不变并且程序确实尝试从地址 0x0 读取 a_variable 而不是从0x20001074.
我希望 ld 以某种方式替换新地址,因为当您 link 对象由 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]
ld 似乎用真实地址代替了左边的占位符。
我的问题是为什么这在手写汇编代码的情况下不起作用?我想念什么?
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