微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从没有C代码的Reset_Handler启动程序

如何解决从没有C代码的Reset_Handler启动程序

我希望能够在ARM Cortex-M4微控制器上运行和调试从纯汇编生成的二进制文件,而不必在C程序中使用内联汇编。

我有一个链接描述文件和一些实用程序C启动代码,该代码建立中断向量表,实现Reset_Handler函数,将.data部分从闪存复制到SRAM,然后调用{{1 }}。此工作流程可以正常工作,但是有点笨拙,我宁愿直接编写汇编程序,而不是直接在带有汇编助记符的main()的C程序中内联。我也很想失去兴趣-也许总有一种更好的方法main()函数如下所示:

Reset_Handler

编辑:我的工具链和链接脚本的详细信息包括在下面。

  • 电路板:带有Cortex-M4的STM32F407VG。
  • 用于调试的OpenOCD和GDB
  • 用于代码编辑器的
  • vim(我的目的是在没有任何东西的情况下在裸机上工作 IDE提供的启动或链接代码
  • void Reset_Handler(void) { //copy .data section to SRAM uint32_t size = (uint32_t)&_edata - (uint32_t)&_sdata; uint8_t *pDst = (uint8_t*)&_sdata; //sram uint8_t *pSrc = (uint8_t*)&_la_data; //flash for(uint32_t i =0 ; i < size ; i++) { *pDst++ = *pSrc++; } //Init. the .bss section to zero in SRAM size = (uint32_t)&_ebss - (uint32_t)&_sbss; pDst = (uint8_t*)&_sbss; for(uint32_t i =0 ; i < size ; i++) { *pDst++ = 0; } __libc_init_array(); main(); } 用于编译和链接

我正在尝试与this tutorial一起使用,但是不是在VM中运行代码,而是在主板上直接运行和调试。

我的链接代码

arm-none-eabi-gcc

解决方法

您的链接描述文件

ENTRY(Reset_Handler)
MEMORY
{
  FLASH(rx):ORIGIN =0x08000000,LENGTH =1024K
  SRAM(rwx):ORIGIN =0x20000000,LENGTH =128K
}
SECTIONS
{
  .text :
  {
    *(.isr_vector)
    *(.text)
    *(.rodata)
    . = ALIGN(4);
    _etext = .;
  }> FLASH
  _la_data = LOADADDR(.data);
  .data :
  {
    _sdata = .;
    *(.data)
    *(.data.*)
    . = ALIGN(4);
    _edata = .;
  }> SRAM AT> FLASH
  .bss :
  {
    _sbss = .;
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss.*)
    *(COMMON)
    . = ALIGN(4);
    _ebss = .;
    __bss_end__ = _ebss;
       . = ALIGN(4);
    end = .;
    __end__ = .;
  }> SRAM
}

由于您已经阅读了arm和st文档,所以您知道向量表从堆栈指针加载值开始,然后是复位向量,然后是其他向量,具体取决于芯片。芯片供应商将应用程序闪存映射为0x08000000,并且具有某些启动选项,这些选项可以镜像到0x00000000,在该位置需要供手臂从其启动。 ram的起始地址为0x20000000,其大小取决于芯片。

.cpu cortex-m4

.word 0x20001000
.word Reset_Handler
.word loop
.word loop

.globl Reset_Handler
.thumb_func
Reset_Handler:
    b loop

.thumb_func
loop:
    b .

.align
.word 0x11223344
.word _edata
.word _sdata
.word _la_data
.word _ebss
.word _sbss
.word 0x55667788

这不是一个不好的起点。通过阅读,您知道的链接器可以生成变量(如果愿意),然后可以在您的代码中使用,如C代码所示,并且在此处可用。

构建

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m4 so.s -o so.o
arm-none-eabi-ld -nostdlib -nostartfiles -T so.ld so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin
arm-none-eabi-objcopy -O srec --srec-forceS3 so.elf so.srec

检查转储

Disassembly of section .text:

08000000 <Reset_Handler-0x10>:
 8000000:   20001000    andcs   r1,r0,r0
 8000004:   08000011    stmdaeq r0,{r0,r4}
 8000008:   08000013    stmdaeq r0,r1,r4}
 800000c:   08000013    stmdaeq r0,r4}

08000010 <Reset_Handler>:
 8000010:   e7ff        b.n 8000012 <loop>

08000012 <loop>:
 8000012:   e7fe        b.n 8000012 <loop>
 8000014:   11223344            ; <UNDEFINED> instruction: 0x11223344
 8000018:   20000000    andcs   r0,r0
 800001c:   20000000    andcs   r0,r0
 8000020:   08000030    stmdaeq r0,{r4,r5}
 8000024:   20000000    andcs   r0,r0
 8000028:   20000000    andcs   r0,r0
 800002c:   55667788    strbpl  r7,[r6,#-1928]!   ; 0xfffff878

那是反汇编的,所以它试图反汇编所有东西,看看这个

08000000 <Reset_Handler-0x10>:
 8000000:   20001000   sp initialization value
 8000004:   08000011   reset handler address orred with one (see the docs)
 8000008:   08000013   some other handler
 800000c:   08000013   some other handler


 8000014:   11223344   .word 0x11223344
 8000018:   20000000   .word _edata
 800001c:   20000000   .word _sdata
 8000020:   08000030   .word _la_data
 8000024:   20000000   .word _ebss
 8000028:   20000000   .word _sbss
 800002c:   55667788   .word 0x55667788

没有.data,因此edata和sdata位于同一位置。 la_data是一种奇怪的事情,因此没有.bss,因此在同一位置开始和结束。所以加一些

.cpu cortex-m4

.word 0x20001000
.word Reset_Handler
.word loop
.word loop

.globl Reset_Handler
.thumb_func
Reset_Handler:
    b loop

.thumb_func
loop:
    b .

.align
.word 0x11223344
.word _edata
.word _sdata
.word _la_data
.word _ebss
.word _sbss
.word 0x55667788

.section .bss
.byte 0

.section .data
.byte 0x66


Disassembly of section .text:

08000000 <Reset_Handler-0x10>:
 8000000:   20001000    andcs   r1,r4}

08000010 <Reset_Handler>:
 8000010:   e7ff        b.n 8000012 <loop>

08000012 <loop>:
 8000012:   e7fe        b.n 8000012 <loop>
 8000014:   11223344            ; <UNDEFINED> instruction: 0x11223344
 8000018:   20000004    andcs   r0,r4
 800001c:   20000000    andcs   r0,r5}
 8000024:   20000008    andcs   r0,r8
 8000028:   20000004    andcs   r0,r4
 800002c:   55667788    strbpl  r7,#-1928]!   ; 0xfffff878

Disassembly of section .data:

20000000 <_sdata>:
20000000:   00000066    andeq   r0,r6,rrx

Disassembly of section .bss:

20000004 <__bss_start__>:
20000004:   00000000    andeq   r0,r0

 8000018:   20000004    andcs   r0,r4

因此.data从0x20000000变为0x20000004(-1),而bss从0x20000004变为0x20000008(-1)

S00A0000736F2E7372656338
S315080000000010002011000008130000081300000863
S31508000010FFE7FEE744332211040000200000002019
S315080000203000000808000020040000208877665584
S309080000306600000058
S70508000011E1

在地址0x0800030处,我们可以看到.data值

因此,您可以简单地用汇编语言重新编写C代码(不需要执行此分析,但很擅长)。如果不将对齐方式放入链接描述文件中,则必须像C代码一样逐字节进行复制,或者如果很幸运,并且想要将其放入其中,则可以尝试更快地检测某些内容,但两端都需要不对齐以同样的方式。

对于这样的MCU,您需要在引导程序中执行的操作,最低限度,

1) stack pointer
2) .data
3) .bss
4) call/branch to C entry point
5) infinite loop

许多人会说您永远不要从main()返回,但是

1) you can protect them anyway,and they will thank you later
2) they perhaps have not created a purely event driven solution

不伤人。因此,当您从arm处阅读文档时,它们具有一种加载堆栈指针的机制,如果使用该机制,则会选中第一个框。

本来就不是苗条和刻薄,完全未经测试,也许是越野车:

.cpu cortex-m4
.syntax unified

.word 0x20001000
.word Reset_Handler
.word loop
.word loop

.globl Reset_Handler
.thumb_func
Reset_Handler:
    /*copy .data section to SRAM */
    /*uint32_t size = (uint32_t)&_edata - (uint32_t)&_sdata;*/
    ldr r0,=_edata
    ldr r1,=_sdata
    subs r0,r1
    bne data_loop_done

    /*uint8_t *pDst = (uint8_t*)&_sdata; //sram*/
    /*uint8_t *pSrc = (uint8_t*)&_la_data; //flash*/

    ldr r2,=_la_data

    /*
    for(uint32_t i =0 ; i < size ; i++)
    {
            *pDst++ = *pSrc++;
    }
    */

data_loop:
    ldrb r3,[r2]
    adds r2,#1
    strb r3,[r1]
    adds r1,#1
    subs r0,#1
    bne data_loop
data_loop_done:

    /*
    Init. the .bss section to zero in SRAM
    size = (uint32_t)&_ebss - (uint32_t)&_sbss;
    pDst = (uint8_t*)&_sbss;
    for(uint32_t i =0 ; i < size ; i++)
    {
            *pDst++ = 0;
    }
    */

    ldr r0,=_ebss
    ldr r1,=_sbss
    mov r2,#0
    subs r0,r1
    bne bss_loop_done
bss_loop:
    strb r2,#1
    bne bss_loop
bss_loop_done:

    /*__libc_init_array();*/
    bl __libc_init_array

    /*main();*/
    bl main

    b loop

.thumb_func
loop:
    b .

__libc_init_array:
    bx lr

main:
    bx lr

.align
.word 0x11223344
.word _edata
.word _sdata
.word _la_data
.word _ebss
.word _sbss
.word 0x55667788

.section .bss
.byte 0

.section .data
.byte 0x66

但是功能正常

08000010 <Reset_Handler>:
 8000010:   4814        ldr r0,[pc,#80]   ; (8000064 <main+0x1e>)
 8000012:   4915        ldr r1,#84]   ; (8000068 <main+0x22>)
 8000014:   1a40        subs    r0,r1
 8000016:   d106        bne.n   8000026 <data_loop_done>
 8000018:   4a14        ldr r2,#80]   ; (800006c <main+0x26>)

0800001a <data_loop>:
 800001a:   7813        ldrb    r3,[r2,#0]
 800001c:   3201        adds    r2,#1
 800001e:   700b        strb    r3,[r1,#0]
 8000020:   3101        adds    r1,#1
 8000022:   3801        subs    r0,#1
 8000024:   d1f9        bne.n   800001a <data_loop>

08000026 <data_loop_done>:
...
 8000064:   20000004    andcs   r0,r4
 8000068:   20000000    andcs   r0,r0
 800006c:   08000078    stmdaeq r0,{r3,r4,r5,r6}

如果您小心一点,可以执行此操作而无需在不必要的地方不使用thumb2指令。您也许可以用thumb2指令来改善此情况,但是如果链接描述文件完成了任务,则可以使用ldr / str并一次与可能的最终值而不是大小进行比较。不管...

嗯,是的,我确实在上面的代码中留下了一条指令...

    ldr r0,#0
    cmp r0,r1
    beq bss_loop_done
bss_loop:
    str r2,#4
    cmp r0,r1
    bne bss_loop
bss_loop_done:

应加快四倍或更多倍,具体取决于系统(芯片)。但是您必须确保起始地址和结束地址对齐。通过增加对双字边界的对齐方式,您可以走得更远

    ldr r0,#0
    mov r3,r1
    beq bss_loop_done
bss_loop:
    stm r1!,{r2,r3}
    cmp r0,r1
    bne bss_loop
bss_loop_done:

本可以在时间循环中在单词中使用stm并保存一条指令。您可能会一次获得4个单词,但可能不会在cortex-m上获得收益,最多获得2个单词是一个不错的平衡。您也可以使用.data副本进行相同的优化。

我希望这不是一项家庭作业,如果仍然可以找到并调试它。但这是读取和移植代码的简单问题。看看那里无休止的例子。

现在在屏幕上查看链接器脚本的设计目的:

.cpu cortex-m4
.syntax unified

.section .isr_vector

.word 0x20001000
.word Reset_Handler
.word loop
.word loop

.section .text

.globl Reset_Handler
.thumb_func
Reset_Handler:
    b loop

.thumb_func
loop:
    b .

Disassembly of section .text:

08000000 <Reset_Handler-0x10>:
 8000000:   20001000    andcs   r1,r4}

08000010 <Reset_Handler>:
 8000010:   e7ff        b.n 8000012 <loop>

08000012 <loop>:
 8000012:   e7fe        b.n 8000012 <loop>

这样您就不必按特定顺序在命令行上获取对象。

链接脚本和引导程序代码之间存在密切的关系,您真的不能一无所有,它们是一对。您不能或不应该尝试混合并匹配Willy nilly项目中的各种链接器脚本和引导程序代码,需要将它们按设计保持在一起。

链接描述文件不是可移植的,并且汇编语言也不是可移植的,因此IMO您应使每个脚本尽可能简单,精简和有意义,更少,更多,更少移植,更少维护,更少工具链特定内容。这不是他们喜欢对复杂的链接器脚本进行严格开发的开发人员的普遍看法。 C库在这里也可以发挥作用,对于gnu模型,C库实际上是一个单独的部分,您可以插入所需的任何一个(它随其相关的引导程序和链接程序脚本一起提供),但这取决于该库的方式没有RTOS的微控制器不是真的对C库友好,因此您必须问自己,我真的需要一个C库吗?我能使它变得更简单,更小(更便宜),可读性和更可维护性吗?项目?

矿井看起来像这样

.thumb_func
reset:
    bl main
    b .

MEMORY
{
    rom : ORIGIN = 0x08000000,LENGTH = 0x1000
    ram : ORIGIN = 0x20000000,LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > rom
    .rodata : { *(.rodata*) } > rom
    .bss    : { *(.bss*)    } > ram
}

对于每个阅读这种经验的人,您都会看到不同的风格,不同的见解等。这是裸机的另一个功能,即以自己的方式做事的自由,只有真正地受到约束硬件规则,仅此而已。没有人的解决方案确实是错误的,只是反映了他们的风格。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。