Cortex M3 .bss 区域初始化的裸机启动代码

电器工程 微控制器 stm32 手臂 皮质-m3 链接器
2022-01-18 18:28:56

我从这里获得了灵感,开发了 arm cortex M3 的裸机启动代码。但是,我遇到了以下问题:假设我声明了一个未初始化的全局变量,比如 main.c 中的 unsigned char 类型

#include ...
unsigned char var; 
...
int main()
{
 ...
}

这使得 STM32 f103 中的 .bss 区域从 _BSS_START=0x20000000 开始,到 _BSS_END = 0x20000001 结束。现在,启动代码

    unsigned int * bss_start_p = &_BSS_START; 
    unsigned int * bss_end_p = &_BSS_END;

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }

尝试将整个 .bss 区域初始化为零。然而,在那个while循环中,指针增加了4个字节,因此经过一步 bss_start_p = 0x20000004 因此它总是与导致无限循环等的 bss_end_p 不同。

有没有标准的解决方案?我是否想以某种方式“强制” .bss 区域的维度为 4 的倍数?还是应该使用指向 unsigned char 的指针来遍历 .bss 区域?也许是这样的:

    unsigned char * bss_start_p = (unsigned char *)(&_BSS_START); 
    unsigned char * bss_end_p = (unsigned char *)(&_BSS_END);

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }
```
4个回答

正如您所怀疑的,这是因为 unsigned int 数据类型的大小为 4 个字节。每个*bss_start_p = 0;语句实际上清除了 bss 区域的四个字节。

bss 内存范围需要正确对齐。您可以简单地定义 _BSS_START 和 _BSS_END 以便总大小是四的倍数,但这通常通过允许链接描述文件定义开始和停止位置来处理。

例如,这是我的一个项目中的链接器部分:

.bss (NOLOAD) : ALIGN(4)
{
    __bss_start__ = .;
    *(.bss)
    . = ALIGN(4);
    __bss_end__ = .;
} >RAM

ALIGN(4)语句处理事情

此外,您可能希望更改

while(bss_start_p != bss_end_p)

while(bss_start_p < bss_end_p).

这不会阻止问题(因为您可能会清除比您希望的多 1-3 个字节),但它可以最大限度地减少影响 :)

标准解决方案是memset()

#include <string.h>
memset(&_BSS_START, 0, &_BSS_END - &_BSS_START)

如果您不能使用标准库,那么您必须决定是否可以将内存区域的大小四舍五入到 4 个字节并继续使用unsigned int *; 或者如果您需要对此严格要求,在这种情况下您需要使用unsigned char *.

如果你确实像在你的第一个循环中那样对大小进行四舍五入,那么bss_start_p最终可能确实大于bss_end_p但很容易处理小于比较<而不是不等式测试。

当然,您也可以使用 32 位传输填充大部分内存区域,而使用 8 位传输仅填充最后几个字节,但这样做更多的工作却收效甚微,尤其是当它只是一段启动代码时。

只需更改!=<. 无论如何,这通常是一种更好的方法,因为它可以处理这样的问题。

还有无数其他站点和示例。如果不是数万的话,数以万计。有众所周知的带有链接器脚本和 boostrap 代码的 c 库,尤其是 newlib、glibc,但您还可以找到其他的库。用 C 引导 C 是没有意义的。

您的问题已得到解答,您正在尝试对可能不准确的事物进行精确比较,它可能不是从已知边界开始或在已知边界结束。所以你可以做小于的事情,但如果代码没有与精确的比较一起工作,那么这意味着你将过去的 .bss 归零到下一部分,这可能会或可能不会导致坏事发生,所以只需用小于 isnt 替换解决方案。

所以这里是 TL;DR 很好。您不会使用该语言引导一种语言,您可以肯定地摆脱它,但是当您这样做时,您是在玩火。如果您只是在学习如何做到这一点,您需要保持谨慎,而不是愚蠢的运气或您尚未发现的事实。

链接器脚本和引导代码有着非常亲密的关系,他们结婚了,在臀部加入,你不能开发一个没有另一个导致大规模失败的。不幸的是,链接器脚本是由链接器定义的,而汇编语言是由汇编器定义的,因此当您更改工具链时,需要重新编写两者。为什么是汇编语言?它不需要引导程序,编译语言通常需要。如果你不想限制你对语言的使用,我会从一些非常简单的东西开始,它具有最小的工具链特定要求,你不要假设 .bss 变量为零(如果变量从未在该语言中初始化,则代码的可读性会降低, 尽量避免这种情况,对于局部变量来说是不正确的,所以在使用它时必须注意。无论如何,人们都避开全局变量,那么我们为什么要谈论 .bss 和 .data 呢???(全局变量适用于这个级别的工作,但这是另一个主题))简单解决方案的另一个规则是不要在声明中初始化变量,而是在代码中进行。是的,会燃烧更多的闪存,您通常有很多,并非所有变量都用常量初始化,最终会消耗指令。

您可以从 cortex-m 设计中看出,他们可能一直认为根本没有引导代码,因此没有 .data 或 .bss 支持。大多数使用全局变量的人都离不开,所以这里是:

对于使用 gnu 工具链的所有 cortex-ms,我可以使它更小,但功能最小,我不记得你可以从 5.xx 开始到当前 9.xx 的哪些版本,我在 3 左右切换了链接器脚本。 xx 或 4.xx,因为我学到了更多,并且 gnu 改变了一些打破我第一个的东西。

引导程序:

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

C代码的入口点:

void bounce ( unsigned int );

unsigned int a;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

链接器脚本。

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

所有这些都可以更小并且仍然有效,在这里添加了一些额外的东西只是为了看到它在工作。

优化构建和链接。

00000000 <_start>:
   0:   20001000
   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

00000014 <reset>:
  14:   f000 f804   bl  20 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
    ...

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

对于某些供应商,您希望使用 0x08000000 或 0x01000000 或其他类似地址,因为闪存映射到那里并在某些引导模式下镜像到 0x00000000。有些只有这么多闪存镜像在 0x00000000,因此您希望向量表指向应用程序闪存空间不为零。因为它是基于矢量表的,所以一切正常。

首先请注意,cortex-ms 是仅限拇指的机器,无论出于何种原因,它们都强制使用拇指功能地址,这意味着 lsbit 是奇怪的。了解你的工具,.thumb_func 指令告诉 gnu 汇编器下一个标签是拇指函数地址。做表中+1的事情会导致失败,不要冲动去做,做对了。还有其他 gnu 汇编器方法来声明函数,这是最小的方法。

   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

如果您没有正确获取向量表,它将无法启动。

可以说只需要堆栈指针向量(如果您希望自己在代码中设置堆栈指针,可以在其中放置任何内容)和重置向量。我在这里放了四个,没有什么特别的原因。通常放 16 但想缩短这个例子。

那么 C 引导程序需要做的最小的事情是什么?1. 设置堆栈指针 2. 零.bss 3. 复制.data 4. 跳转或调用C入口点

C 入口点通常称为 main()。但是一些工具链会看到 main() 并向您的代码添加额外的垃圾。我故意使用不同的名称。YMMV。

如果这都是基于 ram 的,则不需要 .data 的副本。作为 cortex-m 微控制器,它在技术上是可行的,但不太可能,因此需要 .data 副本......如果有 .data。

我的第一个示例和编码风格是不依赖 .data 或 .bss,如本示例所示。Arm 负责堆栈指针,所以剩下的唯一事情就是调用入口点。我喜欢拥有它,以便入口点可以返回,许多人认为你不应该这样做。你可以这样做:

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word centry
.word done
.word done
.word done

并且不从 centry() 返回并且没有重置处理程序代码。

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000

链接器已将内容放在我们要求的地方。总的来说,我们有一个功能齐全的程序。

所以首先处理链接描述文件:

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

强调名称 rom 和 ram 没有任何意义,它们仅连接部分之间链接器的点。

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

.align
.word __data_rom_start__
.word __data_start__
.word __data_end__
.word __data_size__

添加一些项目,以便我们可以看到这些工具做了什么

void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

添加一些项目以放置在这些部分中。并得到

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, r11, lsl r0
   c:   0000001b    andeq   r0, r0, r11, lsl r0
  10:   0000001b    andeq   r0, r0, r11, lsl r0

00000014 <reset>:
  14:   f000 f80c   bl  30 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

00000030 <centry>:
  30:   2207        movs    r2, #7
  32:   b510        push    {r4, lr}
  34:   4b04        ldr r3, [pc, #16]   ; (48 <centry+0x18>)
  36:   2007        movs    r0, #7
  38:   601a        str r2, [r3, #0]
  3a:   f7ff ffef   bl  1c <bounce>
  3e:   2000        movs    r0, #0
  40:   bc10        pop {r4}
  42:   bc02        pop {r1}
  44:   4708        bx  r1
  46:   46c0        nop         ; (mov r8, r8)
  48:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

这是我们在那个实验中寻找的东西(注意没有理由实际加载或运行任何代码......了解你的工具,学习它们)

  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

所以我们在这里了解到的是,变量的位置在 gnu 链接器脚本中非常敏感。注意data_rom_startdata_start的位置,但为什么data_end有效?我会让你弄清楚的。已经理解为什么一个人可能不想弄乱链接器脚本而只是进行简单的编程......

所以我们在这里学到的另一件事是链接器为我们对齐了 data_rom_start ,我们不需要 ALIGN(4)。我们是否应该假设这将永远有效?

另请注意,它在输出时填充,我们有 5 个字节的 .data,但它填充为 8。没有任何 ALIGN(),我们已经可以使用单词进行复制。根据我们今天在我的计算机上看到的这个工具链,过去和未来可能是这样吗?谁知道,即使 ALIGN 需要定期检查以确认某些新版本没有破坏东西,他们也会不时这样做。

为了安全起见,让我们继续进行该实验。

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   . = ALIGN(4);
   __data_end__ = .;
   } > ted AT > bob
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   . = ALIGN(4);
   __bss_end__ = .;
   } > ted
   __bss_size__ = __bss_end__ - __bss_start__;

}

将末端移动到内部以与其他人的操作保持一致。这并没有改变它:

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

另一个快速测试:

.globl bounce
bounce:
    nop
    bx lr

给予

0000001c <bounce>:
  1c:   46c0        nop         ; (mov r8, r8)
  1e:   4770        bx  lr
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

无需在反弹和 .align 之间填充

哦,对了,我现在记得为什么我不把 _end__ 放在里面。因为它不起作用。

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

一些简单但非常可移植的代码与此链接器脚本相结合

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
bss_zero:
    stmia r1!,{r2}
    sub r0,#4
    bne bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3}
    stmia r2!,{r3}
    sub r0,#4
    bne data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__

给予

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000003d    andeq   r0, r0, sp, lsr r0
   c:   0000003d    andeq   r0, r0, sp, lsr r0
  10:   0000003d    andeq   r0, r0, sp, lsr r0

00000014 <reset>:
  14:   480c        ldr r0, [pc, #48]   ; (48 <blen>)
  16:   2800        cmp r0, #0
  18:   d004        beq.n   24 <bss_zero_done>
  1a:   490a        ldr r1, [pc, #40]   ; (44 <bstart>)
  1c:   2200        movs    r2, #0

0000001e <bss_zero>:
  1e:   c104        stmia   r1!, {r2}
  20:   3804        subs    r0, #4
  22:   d1fc        bne.n   1e <bss_zero>

00000024 <bss_zero_done>:
  24:   480b        ldr r0, [pc, #44]   ; (54 <dlen>)
  26:   2800        cmp r0, #0
  28:   d005        beq.n   36 <data_copy_done>
  2a:   4908        ldr r1, [pc, #32]   ; (4c <rstart>)
  2c:   4a08        ldr r2, [pc, #32]   ; (50 <dstart>)

0000002e <data_copy>:
  2e:   c908        ldmia   r1!, {r3}
  30:   c208        stmia   r2!, {r3}
  32:   3804        subs    r0, #4
  34:   d1fb        bne.n   2e <data_copy>

00000036 <data_copy_done>:
  36:   f000 f80f   bl  58 <centry>
  3a:   e7ff        b.n 3c <done>

0000003c <done>:
  3c:   e7fe        b.n 3c <done>

0000003e <bounce>:
  3e:   46c0        nop         ; (mov r8, r8)
  40:   4770        bx  lr
  42:   46c0        nop         ; (mov r8, r8)

00000044 <bstart>:
  44:   20000008    andcs   r0, r0, r8

00000048 <blen>:
  48:   00000004    andeq   r0, r0, r4

0000004c <rstart>:
  4c:   00000074    andeq   r0, r0, r4, ror r0

00000050 <dstart>:
  50:   20000000    andcs   r0, r0, r0

00000054 <dlen>:
  54:   00000008    andeq   r0, r0, r8

00000058 <centry>:
  58:   2207        movs    r2, #7
  5a:   b510        push    {r4, lr}
  5c:   4b04        ldr r3, [pc, #16]   ; (70 <centry+0x18>)
  5e:   2007        movs    r0, #7
  60:   601a        str r2, [r3, #0]
  62:   f7ff ffec   bl  3e <bounce>
  66:   2000        movs    r0, #0
  68:   bc10        pop {r4}
  6a:   bc02        pop {r1}
  6c:   4708        bx  r1
  6e:   46c0        nop         ; (mov r8, r8)
  70:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

我们可以停在那里或继续前进。如果我们以与链接描述文件相同的顺序进行初始化,那么如果我们还没有到达那里,我们可以继续进行下一步。和 stm/ldm 仅需要/希望使用字对齐地址,因此如果您更改为:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

在链接器脚本中首先使用 bss,是的,您想要 ble 而不是 bls。

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3804        subs    r0, #4
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000004    andcs   r0, r0, r4

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

这些循环会更快。现在我不知道 ahb 总线是否可以是 64 位宽,但是对于一个全尺寸的手臂,你会想要在 64 位边界上对齐这些东西。在 32 位边界但不是 64 位边界上的四个寄存器 ldm/stm 成为三个单独的总线事务,其中在 64 位边界上对齐是单个事务,每条指令节省几个时钟。

因为我们正在做baremetal,我们对所有可以先说bss然后是数据的事情负全部责任,那么如果我们有堆这样做,那么堆栈就会从上到下增长,所以如果我们将bss归零并溢出一些,只要我们从正确的地方很好,我们还没有使用那个内存。然后我们复制 .data 并可以溢出到堆中,这很好,堆与否都有足够的空间用于堆栈,所以我们不会踩到任何人/任何东西(只要我们确保在链接器脚本中这样做。如果存在问题,请使 ALIGN() 更大,以便我们始终在我们的空间内进行这些填充。

所以我的简单解决方案,要么接受,要么离开。欢迎修复任何错误,我没有在硬件或模拟器上运行它......

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(8);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

   . = ALIGN(8);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

}



.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3,r4,r5,r6}
    stmia r2!,{r3,r4,r5,r6}
    sub r0,#16
    ble data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__


void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-ld -o hello.elf -T flash.ld flash.o centry.o
arm-none-eabi-objdump -D hello.elf > hello.list
arm-none-eabi-objcopy hello.elf hello.bin -O binary

把它们放在一起,你会得到:

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3810        subs    r0, #16
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000008    andcs   r0, r0, r8

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

请注意,这适用于 arm-none-eabi- 和 arm-linux-gnueabi 以及其他变体,因为没有使用 ghee whiz 的东西。

当您环顾四周时,您会发现人们会为链接器脚本中的酥油高手而疯狂,巨大的厨房水槽东西。最好只知道如何去做(或者更好地掌握工具,这样你就可以控制发生的事情),而不是依赖别人的东西,因为你不理解和/或不想研究而不知道它会在哪里崩溃它。

作为一般规则,不要使用相同的语言引导语言(引导在这个意义上意味着运行代码而不是使用相同的编译器编译编译器)你想使用更简单的语言和更少的引导。这就是为什么 C 是在汇编中完成的,它没有引导要求,您只需从复位后的第一条指令开始。JAVA,当然你可以用 C 语言编写 jvm 并用 asm 引导 C 语言,然后用 C 语言引导 JAVA,但也可以用 C 语言执行 JAVA。

因为我们控制了对这些复制循环的假设,所以它们在定义上比手动调整的 memcpy/memset 更严格和更清晰。

请注意,您的另一个问题是:

unsigned int * bss_start_p = &_BSS_START; 
unsigned int * bss_end_p = &_BSS_END;

如果这些是本地的,没问题,如果这些是全局的,那么您需要首先初始化 .data 以使它们工作,如果您尝试使用该技巧来执行 .data ,那么您将失败。局部变量,很好。如果您出于某种原因决定制作静态局部变量(我喜欢称它们为局部全局变量),那么您将再次陷入困境。每次您在声明中进行分配时,您都应该考虑一下,它是如何实现的,它是否安全/健全。每次你假设一个变量在未声明时为零,同样的处理,如果一个局部变量不被假设为零,如果是全局变量,那么它是。如果您从不认为它们为零,那么您永远不必担心。