我试图熟悉ARM Cortex-M4微控制器中的链接和启动过程。浏览链接器脚本几乎所有部分都标记为可加载。
起初我认为这意味着它会从闪存复制到RAM,但后来我才知道这样做是以不同的方式处理的。那么flash中的一个部分可以加载是什么意思呢?它已经加载并从闪存中的位置运行了吗?另外,我指的是包含说明的章节。
在这种情况下可加载是否意味着调试器加载到设备中?
答案 0 :(得分:2)
一个功能齐全的皮质m程序
flash.s
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.align
.thumb_func
.globl dummy
dummy:
bx lr
so.c
void dummy ( unsigned int );
int notmain ( void )
{
unsigned int ra;
for(ra=0;ra<10;ra++) dummy(ra);
return(0);
}
flash.ld 记忆 { rom:ORIGIN = 0x00000000,LENGTH = 0x1000 ram:ORIGIN = 0x20000000,LENGTH = 0x1000 }
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
构建
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -c so.c -o so.o
arm-none-eabi-ld -o so.elf -T flash.ld flash.o so.o
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf so.bin -O binary
(可以使用cortex-m4起作用)
so.list
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000011 andeq r0, r0, r1, lsl r0
8: 00000017 andeq r0, r0, r7, lsl r0
c: 00000017 andeq r0, r0, r7, lsl r0
00000010 <reset>:
10: f000 f804 bl 1c <notmain>
14: e7ff b.n 16 <hang>
00000016 <hang>:
16: e7fe b.n 16 <hang>
00000018 <dummy>:
18: 4770 bx lr
1a: 46c0 nop ; (mov r8, r8)
0000001c <notmain>:
1c: b510 push {r4, lr}
1e: 2400 movs r4, #0
20: 0020 movs r0, r4
22: 3401 adds r4, #1
24: f7ff fff8 bl 18 <dummy>
28: 2c0a cmp r4, #10
2a: d1f9 bne.n 20 <notmain+0x4>
2c: 2000 movs r0, #0
2e: bc10 pop {r4}
30: bc02 pop {r1}
32: 4708 bx r1
是一个不太复杂的链接器脚本和程序,精灵有更少的东西
readelf的一部分
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 010000 000034 00 AX 0 0 4
[ 2] .ARM.attributes ARM_ATTRIBUTES 00000000 010034 00002d 00 0 0 1
[ 3] .comment PROGBITS 00000000 010061 000011 01 MS 0 0 1
[ 4] .symtab SYMTAB 00000000 010074 0000f0 10 5 12 4
[ 5] .strtab STRTAB 00000000 010164 00003d 00 0 0 1
[ 6] .shstrtab STRTAB 00000000 0101a1 00003a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
y (purecode), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x00000000 0x00000000 0x00034 0x00034 R E 0x10000
hexdump -C so.bin
00000000 00 10 00 20 11 00 00 00 17 00 00 00 17 00 00 00 |... ............|
00000010 00 f0 04 f8 ff e7 fe e7 70 47 c0 46 10 b5 00 24 |........pG.F...$|
00000020 20 00 01 34 ff f7 f8 ff 0a 2c f9 d1 00 20 10 bc | ..4.....,... ..|
00000030 02 bc 08 47 |...G|
00000034
a&#34;二进制&#34;有许多种类,它是一个不幸的命名不好的术语,因为它是如此令人困惑。您在上面的反汇编中看到的所有内容都是程序运行所必需的,这是它的真正二进制部分,必须在需要加载运行的任何地方加载它。如果在操作系统上,则操作系统读取elf文件,使用其地址/偏移量提取可加载部分,并在入口点启动之前将它们加载到内存中。利用已经具有elf文件格式的工具,我们可以重复使用其中的一些用于微控制器,我们不能/通常不使用入口点,因为这没有意义,我们必须使向量/入口点匹配硬件需要,在这种情况下是一个向量表。
来自objcopy的hexdump还显示了我们必须让处理器可见的部分,以便程序运行,或加载到地址/内存空间(在这种情况下,闪存位于内存或地址空间)。
但&#34;二进制&#34;文件,elf还包含调试符号,以防您想要启动调试器,在工具链命令行中添加更多选项,您可以获得有关这些项在源代码文件中的位置的更多信息,这样您在某些情况下可以看到单步执行代码时的高级语言。那些不是可加载的部分,它们是程序的描述或只是帮助,它们不是机器代码,也不是处理器执行程序所需的数据,所以它们不需要加载到内存空间。
另一个&#34;二进制&#34;文件格式
arm-none-eabi-objcopy so.elf -O ihex so.hex
cat so.hex
:100000000010002011000000170000001700000081
:1000100000F004F8FFE7FEE77047C04610B5002483
:1000200020000134FFF7F8FF0A2CF9D1002010BCA2
:0400300002BC0847BF
:00000001FF
它有一些额外的信息,但几乎所有这些都是我们必须加载到地址空间以供程序运行的部分
另一种二进制文件格式
S00A0000736F2E7372656338
S1130000001000201100000017000000170000007D
S113001000F004F8FFE7FEE77047C04610B500247F
S113002020000134FFF7F8FF0A2CF9D1002010BC9E
S107003002BC0847BB
S9030000FC
也大部分是节目。
elf只是另一种文件格式(非常受gnu工具欢迎,但仍然只是另一种文件格式),它包含所需的机器代码和数据以及一堆其他东西,机器代码和数据是我们必须加载的如果这是一个操作系统,或者我们理想地加载到微控制器的闪存中,但是并非所有微控制器都是相同的,那么只有基于ram的内存并且在枚举期间通过usb下载程序(进入ram)。和其他解决方案,或者如果调试你可能有物品加载到ram取决于mcu和工具,虽然这不是如何启动,所以这不是一个很好的二进制文件。
如果您认为需要将.bss归零并拥有任何.data,那么您需要其他信息,.bss的偏移量和大小以及.data的偏移量和内容,然后引导程序将这些项置零并复制,以及您需要在非易失性闪存/ ROM中获取该信息,这只是运行程序所需的机器代码和数据所需的更多数据。如果你让其他人为你编写代码,那么可能有定制的链接器脚本和引导代码,它们允许你只有.data项目并在gui上按下一个构建按钮,当你的入口点(主要的) ()按惯例和/或标准)开始执行或代表C入口点的高级代码的代码。