还有无数其他站点和示例。如果不是数万的话,数以万计。有众所周知的带有链接器脚本和 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_start与data_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 ,那么您将失败。局部变量,很好。如果您出于某种原因决定制作静态局部变量(我喜欢称它们为局部全局变量),那么您将再次陷入困境。每次您在声明中进行分配时,您都应该考虑一下,它是如何实现的,它是否安全/健全。每次你假设一个变量在未声明时为零,同样的处理,如果一个局部变量不被假设为零,如果是全局变量,那么它是。如果您从不认为它们为零,那么您永远不必担心。