简单的 ARM 程序集启动文件似乎没有到达任何代码
Simple ARM assembly startup file doesn't seem to reach any code
我正在尝试使用 ARM Thumb 程序集编写一个非常简单的程序,它将关闭我的 Arduino Due 上的 LED(链接到它的 ATSAM3X8E 引脚似乎默认在内部上拉)。在汇编中做更复杂的事情之前,我这样做是为了测试。
我已经将初始 SP 值以及所有 15 个强制系统异常向量放在一个部分中,并进行了快速 GPIO 硬件寄存器初始化例程(将引脚定义为我可以写入的输出,禁用拉取-up,所以只要我没有实际写入输出数据寄存器,输出引脚就应该保持关闭状态)。
(我没有执行任何 RAM 加载例程,因为目前我的程序中除了只读常量之外没有其他值,所以也没有数据部分)。
我使用了 GNU 汇编程序,ld 和一个简单的链接器脚本,确保向量 table 位于二进制文件的最开头,从 elf 快速复制了一个 objcopy 并得到了一个很好的原始二进制文件我使用 BOSSA 命令行刷新了 MCU 的内部闪存。 (见下面的代码)
但什么也没发生,LED 保持高电平。
.thumb
.syntax unified
.section .vectors
.word 0x20001000 @StackTop, somewhere near the end of the SRAM
.word Reset_Handler
.word No_Handler @NMI_Handler
.word No_Handler @HardFault_Handler
.word No_Handler @MemFault_Handler
.word No_Handler @BusFault_Handler
.word No_Handler @UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word No_Handler @SVC_Handler
.word No_Handler @DebugMon_Handler
.word 0
.word No_Handler @PendSV_Handler
.word No_Handler @SysTick_Handler
.word No_Handler @IRQ0
@... (same with all IRQ's until 28)
.word No_Handler @IRQ29
.section .text
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, =WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
LDR r0, =PIOB_WPMR @Disable GPIO Write Protection
LDR r1, [r0]
LDR r2, =PIOB_KEY
LDR r3, [r2]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_PER @Disable peripeheral function (pure GPIO)
LDR r1, [r0]
LDR r2, =PIOB27
LDR r3, [r2]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_OER @Define pin as output
LDR r1, [r0]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_OWER @Allow writes to ODSR
LDR r1, [r0]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_PUDR @Disable internal pull-up for the pin
LDR r1, [r0]
ORR r1, r1, r3
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
WDT_MR: .word 0x400E1A54
PIOB_WPMR: .word 0x400E10E4
PIOB_KEY: .word 0x50494F00
PIOB_PER: .word 0x400E1000
PIOB_OER: .word 0x400E1010
PIOB_OWER: .word 0x400E10A0
PIOB_PUDR: .word 0x400E1060
PIOB_ODSR: .word 0x400E1038
PIOB27: .word 0x8000000 @ 0x1u<<27
我用完全相同的寄存器在 C 中做了同样的事情(但让 Microchip Studio 为我使用 ARM GCC)并且这次成功了。
如果错误出在我的自定义链接描述文件中,则为:
MEMORY
{
flash (rx) : ORIGIN = 0x00080000, LENGTH = 0x00080000
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00018000
}
SECTIONS
{
.text :
{
*(.vectors)
*(.text)
} > flash
}
我担心的是程序实际上似乎没有到达任何初始化代码。
如果我拿你程序的一个子集
.thumb
.syntax unified
.section .vectors
.word 0x20001000
.word Reset_Handler
.section .text
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, =WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
WDT_MR: .word 0x400E1A54
assemble 和 link 它与你的 linker 脚本,然后 disassemble.
Disassembly of section .text:
00080000 <Reset_Handler-0x8>:
80000: 20001000 andcs r1, r0, r0
80004: 00080009 andeq r0, r8, r9
00080008 <Reset_Handler>:
80008: f000 f804 bl 80014 <init>
8000c: f000 f808 bl 80020 <main>
80010: e7ff b.n 80012 <No_Handler>
00080012 <No_Handler>:
80012: e7fe b.n 80012 <No_Handler>
00080014 <init>:
80014: 4804 ldr r0, [pc, #16] ; (80028 <WDT_MR+0x6>)
80016: 6801 ldr r1, [r0, #0]
80018: f441 4100 orr.w r1, r1, #32768 ; 0x8000
8001c: 6001 str r1, [r0, #0]
8001e: 4770 bx lr
00080020 <main>:
80020: e7fe b.n 80020 <main>
00080022 <WDT_MR>:
80022: 400e1a54 andmi r1, lr, r4, asr sl
80026: 00220000 eoreq r0, r2, r0
8002a: Address 0x000000000008002a is out of bounds.
首先要检查的是向量 table。
00080000 <Reset_Handler-0x8>:
80000: 20001000 andcs r1, r0, r0
80004: 00080009 andeq r0, r8, r9
00080008 <Reset_Handler>:
第一项是sp init值,看起来没问题,第二项是复位向量,必须设置lsbit,是的,那是正确的,所以你不会立即失败,有一些希望。
正如评论中指出的那样
00080014 <init>:
80014: 4804 ldr r0, [pc, #16] ; (80028 <WDT_MR+0x6>)
...
00080022 <WDT_MR>:
80022: 400e1a54 andmi r1, lr, r4, asr sl
不是将值 0x400e1a54 放入 r0,而是将 WDT_MR 的地址放入 r0。其中 disassembler 有点混乱。但基本上它把 0x00080022 放在 r0 中。这不是你想要的。
所以如果我这样做:
.thumb
.syntax unified
.section .vectors
.word 0x20001000
.word Reset_Handler
.section .text
.equ WDT_MR, 0x400E1A54
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, =WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
.align
.word 0x12345678
底部的 .align 和那个额外的词(我认为不是真的需要)是为了让反汇编更容易阅读。
给这个
00080014 <init>:
80014: 4804 ldr r0, [pc, #16] ; (80028 <main+0x8>)
80016: 6801 ldr r1, [r0, #0]
80018: f441 4100 orr.w r1, r1, #32768 ; 0x8000
8001c: 6001 str r1, [r0, #0]
8001e: 4770 bx lr
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: bf00 nop
80024: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
80028: 400e1a54 andmi r1, lr, r4, asr sl
现在 0x400e1a54 正在加载到 r0,这样您就可以在逻辑中控制该寄存器。
目视检查 disassemble 看它是否按照您的想法进行。
WDT_MR 在你的代码中是一个标签,它基本上是一个地址。因此,当您执行 ldr r0,=WDT_MR 时,您要求的是地址。现在这是您可以做的另一件事:
.thumb
.syntax unified
.section .vectors
.word 0x20001000
.word Reset_Handler
.section .text
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
.align
WDT_MR: .word 0x400E1A54
所以你离成功还差一个字符
00080014 <init>:
80014: 4803 ldr r0, [pc, #12] ; (80024 <WDT_MR>)
80016: 6801 ldr r1, [r0, #0]
80018: f441 4100 orr.w r1, r1, #32768 ; 0x8000
8001c: 6001 str r1, [r0, #0]
8001e: 4770 bx lr
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: bf00 nop
00080024 <WDT_MR>:
80024: 400e1a54 andmi r1, lr, r4, asr sl
现在正在加载该标签的值,而不是标签的地址。而且我怀疑这实际上是您想要做的,而不是 .equ 的事情。 YMMV。如果您执行 .EQU 操作,那么 assembler 将创建池,如果您执行此操作,则您可以控制这些内容所在的位置。
现在你可能遇到了上面显示和讨论的第二个问题,我解决了它:
00080022 <WDT_MR>:
80022: 400e1a54 andmi r1, lr, r4, asr sl
80026: 00220000 eoreq r0, r2, r0
8002a: Address 0x000000000008002a is out of bounds.
如果你的 ldr 正确,你就会从 0x80022 加载 32 位,这不是 32 位对齐的地址。在像这样的池之前,您想要在至少 4 字节的边界上对齐并且对于 arm gas,.align 就足够了:
.thumb_func
main:
@nothing much here
B main
.align
WDT_MR: .word 0x400E1A54
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: bf00 nop
00080024 <WDT_MR>:
80024: 400e1a54 andmi r1, lr, r4, asr sl
注意 nop 工具添加为填充,如果我们要:
.thumb_func
main:
@nothing much here
B main
adds r0,#1
.align
WDT_MR: .word 0x400E1A54
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: 3001 adds r0, #1
00080024 <WDT_MR>:
80024: 400e1a54 andmi r1, lr, r4, asr sl
它仍然正确对齐。
我正在尝试使用 ARM Thumb 程序集编写一个非常简单的程序,它将关闭我的 Arduino Due 上的 LED(链接到它的 ATSAM3X8E 引脚似乎默认在内部上拉)。在汇编中做更复杂的事情之前,我这样做是为了测试。
我已经将初始 SP 值以及所有 15 个强制系统异常向量放在一个部分中,并进行了快速 GPIO 硬件寄存器初始化例程(将引脚定义为我可以写入的输出,禁用拉取-up,所以只要我没有实际写入输出数据寄存器,输出引脚就应该保持关闭状态)。 (我没有执行任何 RAM 加载例程,因为目前我的程序中除了只读常量之外没有其他值,所以也没有数据部分)。
我使用了 GNU 汇编程序,ld 和一个简单的链接器脚本,确保向量 table 位于二进制文件的最开头,从 elf 快速复制了一个 objcopy 并得到了一个很好的原始二进制文件我使用 BOSSA 命令行刷新了 MCU 的内部闪存。 (见下面的代码)
但什么也没发生,LED 保持高电平。
.thumb
.syntax unified
.section .vectors
.word 0x20001000 @StackTop, somewhere near the end of the SRAM
.word Reset_Handler
.word No_Handler @NMI_Handler
.word No_Handler @HardFault_Handler
.word No_Handler @MemFault_Handler
.word No_Handler @BusFault_Handler
.word No_Handler @UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word No_Handler @SVC_Handler
.word No_Handler @DebugMon_Handler
.word 0
.word No_Handler @PendSV_Handler
.word No_Handler @SysTick_Handler
.word No_Handler @IRQ0
@... (same with all IRQ's until 28)
.word No_Handler @IRQ29
.section .text
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, =WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
LDR r0, =PIOB_WPMR @Disable GPIO Write Protection
LDR r1, [r0]
LDR r2, =PIOB_KEY
LDR r3, [r2]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_PER @Disable peripeheral function (pure GPIO)
LDR r1, [r0]
LDR r2, =PIOB27
LDR r3, [r2]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_OER @Define pin as output
LDR r1, [r0]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_OWER @Allow writes to ODSR
LDR r1, [r0]
ORR r1, r1, r3
STR r1, [r0]
LDR r0, =PIOB_PUDR @Disable internal pull-up for the pin
LDR r1, [r0]
ORR r1, r1, r3
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
WDT_MR: .word 0x400E1A54
PIOB_WPMR: .word 0x400E10E4
PIOB_KEY: .word 0x50494F00
PIOB_PER: .word 0x400E1000
PIOB_OER: .word 0x400E1010
PIOB_OWER: .word 0x400E10A0
PIOB_PUDR: .word 0x400E1060
PIOB_ODSR: .word 0x400E1038
PIOB27: .word 0x8000000 @ 0x1u<<27
我用完全相同的寄存器在 C 中做了同样的事情(但让 Microchip Studio 为我使用 ARM GCC)并且这次成功了。 如果错误出在我的自定义链接描述文件中,则为:
MEMORY
{
flash (rx) : ORIGIN = 0x00080000, LENGTH = 0x00080000
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00018000
}
SECTIONS
{
.text :
{
*(.vectors)
*(.text)
} > flash
}
我担心的是程序实际上似乎没有到达任何初始化代码。
如果我拿你程序的一个子集
.thumb
.syntax unified
.section .vectors
.word 0x20001000
.word Reset_Handler
.section .text
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, =WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
WDT_MR: .word 0x400E1A54
assemble 和 link 它与你的 linker 脚本,然后 disassemble.
Disassembly of section .text:
00080000 <Reset_Handler-0x8>:
80000: 20001000 andcs r1, r0, r0
80004: 00080009 andeq r0, r8, r9
00080008 <Reset_Handler>:
80008: f000 f804 bl 80014 <init>
8000c: f000 f808 bl 80020 <main>
80010: e7ff b.n 80012 <No_Handler>
00080012 <No_Handler>:
80012: e7fe b.n 80012 <No_Handler>
00080014 <init>:
80014: 4804 ldr r0, [pc, #16] ; (80028 <WDT_MR+0x6>)
80016: 6801 ldr r1, [r0, #0]
80018: f441 4100 orr.w r1, r1, #32768 ; 0x8000
8001c: 6001 str r1, [r0, #0]
8001e: 4770 bx lr
00080020 <main>:
80020: e7fe b.n 80020 <main>
00080022 <WDT_MR>:
80022: 400e1a54 andmi r1, lr, r4, asr sl
80026: 00220000 eoreq r0, r2, r0
8002a: Address 0x000000000008002a is out of bounds.
首先要检查的是向量 table。
00080000 <Reset_Handler-0x8>:
80000: 20001000 andcs r1, r0, r0
80004: 00080009 andeq r0, r8, r9
00080008 <Reset_Handler>:
第一项是sp init值,看起来没问题,第二项是复位向量,必须设置lsbit,是的,那是正确的,所以你不会立即失败,有一些希望。
正如评论中指出的那样
00080014 <init>:
80014: 4804 ldr r0, [pc, #16] ; (80028 <WDT_MR+0x6>)
...
00080022 <WDT_MR>:
80022: 400e1a54 andmi r1, lr, r4, asr sl
不是将值 0x400e1a54 放入 r0,而是将 WDT_MR 的地址放入 r0。其中 disassembler 有点混乱。但基本上它把 0x00080022 放在 r0 中。这不是你想要的。
所以如果我这样做:
.thumb
.syntax unified
.section .vectors
.word 0x20001000
.word Reset_Handler
.section .text
.equ WDT_MR, 0x400E1A54
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, =WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
.align
.word 0x12345678
底部的 .align 和那个额外的词(我认为不是真的需要)是为了让反汇编更容易阅读。
给这个
00080014 <init>:
80014: 4804 ldr r0, [pc, #16] ; (80028 <main+0x8>)
80016: 6801 ldr r1, [r0, #0]
80018: f441 4100 orr.w r1, r1, #32768 ; 0x8000
8001c: 6001 str r1, [r0, #0]
8001e: 4770 bx lr
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: bf00 nop
80024: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
80028: 400e1a54 andmi r1, lr, r4, asr sl
现在 0x400e1a54 正在加载到 r0,这样您就可以在逻辑中控制该寄存器。
目视检查 disassemble 看它是否按照您的想法进行。
WDT_MR 在你的代码中是一个标签,它基本上是一个地址。因此,当您执行 ldr r0,=WDT_MR 时,您要求的是地址。现在这是您可以做的另一件事:
.thumb
.syntax unified
.section .vectors
.word 0x20001000
.word Reset_Handler
.section .text
.thumb_func
Reset_Handler:
BL init
BL main
B No_Handler
.thumb_func
No_Handler:
B No_Handler
.thumb_func
init:
LDR r0, WDT_MR @Disable Watchdog Timer
LDR r1, [r0]
ORR r1, r1, #0x8000
STR r1, [r0]
BX lr
.thumb_func
main:
@nothing much here
B main
.align
WDT_MR: .word 0x400E1A54
所以你离成功还差一个字符
00080014 <init>:
80014: 4803 ldr r0, [pc, #12] ; (80024 <WDT_MR>)
80016: 6801 ldr r1, [r0, #0]
80018: f441 4100 orr.w r1, r1, #32768 ; 0x8000
8001c: 6001 str r1, [r0, #0]
8001e: 4770 bx lr
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: bf00 nop
00080024 <WDT_MR>:
80024: 400e1a54 andmi r1, lr, r4, asr sl
现在正在加载该标签的值,而不是标签的地址。而且我怀疑这实际上是您想要做的,而不是 .equ 的事情。 YMMV。如果您执行 .EQU 操作,那么 assembler 将创建池,如果您执行此操作,则您可以控制这些内容所在的位置。
现在你可能遇到了上面显示和讨论的第二个问题,我解决了它:
00080022 <WDT_MR>:
80022: 400e1a54 andmi r1, lr, r4, asr sl
80026: 00220000 eoreq r0, r2, r0
8002a: Address 0x000000000008002a is out of bounds.
如果你的 ldr 正确,你就会从 0x80022 加载 32 位,这不是 32 位对齐的地址。在像这样的池之前,您想要在至少 4 字节的边界上对齐并且对于 arm gas,.align 就足够了:
.thumb_func
main:
@nothing much here
B main
.align
WDT_MR: .word 0x400E1A54
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: bf00 nop
00080024 <WDT_MR>:
80024: 400e1a54 andmi r1, lr, r4, asr sl
注意 nop 工具添加为填充,如果我们要:
.thumb_func
main:
@nothing much here
B main
adds r0,#1
.align
WDT_MR: .word 0x400E1A54
00080020 <main>:
80020: e7fe b.n 80020 <main>
80022: 3001 adds r0, #1
00080024 <WDT_MR>:
80024: 400e1a54 andmi r1, lr, r4, asr sl
它仍然正确对齐。