(嵌入式 实时操作系统 rtos nuttx 7.1 stm32 源代码分析)
为什么 __start 是处理器执行的第一条指令?
转载请注明出处: http://blog.csdn.net/zhumaill/article/details/23426605
在《 NuttX 启动流程》一文中提到, __start 是处理器执行的第一条指令。那么,为什么在 NuttX 中 __start 是处理器执行的第一条指令?为什么我要把 __start 称为“处理器执行的第一条指令”,而不是称为“程序入口点”?
nuttx/configs/shenzhou/scripts/ld.script:
[cpp] view plain
copy
print ?
- ......
- OUTPUT_ARCH(arm)
- ENTRY(_stext)
- SECTIONS
- ......
......
OUTPUT_ARCH(arm)
ENTRY(_stext)
SECTIONS
......
这里的 ENTRY(_stext) 定义的程序入口点是 _stext。
nuttx/arch/arm/src/Makefile:
[cpp] view plain
copy
print ?
- ......
- nuttx$(EXEEXT): $(HEAD_OBJ) board/libboard$(LIBEXT)
- $(Q) echo "LD: nuttx"
- $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \
- -o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \
- --start-group $(LDLIBS) $(EXTRA_LIBS) $(LIBGCC) --end-group
- ......
......
nuttx$(EXEEXT): $(HEAD_OBJ) board/libboard$(LIBEXT)
$(Q) echo "LD: nuttx"
$(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \
-o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \
--start-group $(LDLIBS) $(EXTRA_LIBS) $(LIBGCC) --end-group
......
这里的 --entry=__start 在链接时重新指定了程序入口点为 __start。
[cpp] view plain
copy
print ?
- xxx@xxx-desktop ~ $ readelf -h nuttx
- ELF Header:
- Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
- Class: ELF32
- Data: 2's complement, little endian
- Version: 1 (current)
- OS/ABI: UNIX - System V
- ABI Version: 0
- Type: EXEC (Executable file)
- Machine: ARM
- Version: 0x1
- Entry point address: 0x80003d1
- Start of program headers: 52 (bytes into file)
- Start of section headers: 145184 (bytes into file)
- Flags: 0x5000202, has entry point, Version5 EABI, soft-float ABI
- Size of this header: 52 (bytes)
- Size of program headers: 32 (bytes)
- Number of program headers: 3
- Size of section headers: 40 (bytes)
- Number of section headers: 18
- Section header string table index: 15
xxx@xxx-desktop ~ $ readelf -h nuttx
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0x80003d1
Start of program headers: 52 (bytes into file)
Start of section headers: 145184 (bytes into file)
Flags: 0x5000202, has entry point, Version5 EABI, soft-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 18
Section header string table index: 15
在编译生成的 elf 文件 nuttx 的文件头中可以找到这个程序入口点的地址0x80003d1。
但是,这个程序入口点只对 elf 格式的文件有用,在非 elf 格式的祼机程序中,这个程序入口点不起作用,我们烧写到芯片 flash 中的格式为 .bin 或 .hex 的文件中没有这个程序入口点的定义。把这里的 __start 改成其它函数一样可以正常运行。
根据 cortex-m3 的定义,系统从 flash 启动时,0x08000004中的值就是 PC(程序计数器)的初始值,这才是真正起作用的,它决定了处理器执行的第一条指令。
nuttx.S:
[cpp] view plain
copy
print ?
- ......
- 08000000 <_vectors>:
- 8000000: 20005db4 .word 0x20005db4
- 8000004: 080003d1 .word 0x080003d1 <---PC 初始值
- 8000008: 08000159 .word 0x08000159
- ......
- 080003d0 <__start>:
- 80003d0: b510 push {r4, lr} <---处理器执行的第一条指令
- 80003d2: f000 f827 bl 8000424 <stm32_clockconfig>
- 80003d6: f000 f967 bl 80006a8 <stm32_lowsetup>
- 80003de: 4b0c ldr r3, [pc, #48] ; (8000410 <__start+0x40>)
- ......
......
08000000 <_vectors>:
8000000: 20005db4 .word 0x20005db4
8000004: 080003d1 .word 0x080003d1 <---PC 初始值
8000008: 08000159 .word 0x08000159
......
080003d0 <__start>:
80003d0: b510 push {r4, lr} <---处理器执行的第一条指令
80003d2: f000 f827 bl 8000424 <stm32_clockconfig>
80003d6: f000 f967 bl 80006a8 <stm32_lowsetup>
80003de: 4b0c ldr r3, [pc, #48] ; (8000410 <__start+0x40>)
......
使用《 NuttX 安装脚本》一文中的方法安装 NuttX 后,反汇编文件 nuttx.S 位于 Linux 当前用户的主目录。这里的 PC 初始值是0x080003d1,指向 __start 函数的起始地址。
顺便说一下,向量表中的跳转地址都是奇数,这是因为 cortex-m3 是在Thumb状态下执行的。在向量表以外的地方,32位指令的跳转地址是奇数,16位指令的跳转地址是偶数。但指令的实际地址都是偶数,如0x080003d0。
nuttx/arch/arm/src/stm32/stm32_vectors.S:
[cpp] view plain
copy
print ?
- ......
- _vectors:
-
-
-
- .word IDLE_STACK
- .word __start
- .word stm32_nmi
- .word stm32_hardfault
- ......
......
_vectors:
/* Processor Exceptions */
.word IDLE_STACK /* Vector 0: Reset stack pointer */
.word __start /* Vector 1: Reset vector */
.word stm32_nmi /* Vector 2: Non-Maskable Interrupt (NMI) */
.word stm32_hardfault /* Vector 3: Hard fault */
......
这里的 .word __start 定义了 PC 的初始值。
__start 函数的声明与定义如下:
nuttx/arch/arm/src/stm32/stm32_vectors.S:
[cpp] view plain
copy
print ?
- ......
- .globl __start
- ......
......
.globl __start
......
在汇编代码中声明了全局符号 __start,相当于声明了函数。
nuttx/arch/arm/src/stm32/stm32_start.c:
[cpp] view plain
copy
print ?
- ......
- void __start(void)
- {
- const uint32_t *src;
- uint32_t *dest;
-
-
-
- stm32_clockconfig();
- stm32_fpuconfig();
- stm32_lowsetup();
- ......
- }
- ......
......
void __start(void)
{
const uint32_t *src;
uint32_t *dest;
/* Configure the uart so that we can get debug output as soon as possible */
stm32_clockconfig();
stm32_fpuconfig();
stm32_lowsetup();
......
}
......
在 C 语言代码中定义了 __start 函数。