启动文件的几个问题的分析
以下分析如无特别指出启动文件,均是指官方hal库启动文件startup_stm32f103xe.s
为什么编译器编译的时候能够把启动文件的代码指令放在bin文件的最前面?
因为.sct链接脚本,声明了编译的规则,也就是如下的代码段中,.o (RESET, +First),section是一种将代码、数据和其他相关信息组织在一起的方式。在链接时,它们被放置在特定的内存地址中。而.o (RESET, +First) 是指定义了一个名为“RESET”的section,并将其放置在链接脚本的第一个位置,而这个段就在启动文件中,实际上指的就是中断向量表,第一个指令是 DCD __initial_sp,DCD是伪指令,会分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。也就是将__initial_sp写入内存, 而此时所在的内存位置正是编译生成的第一个位置。
如果使用了该.sct文件而不存在AREA RESET, DATA, READONLY,会编译失败
如果在汇编的启动文件中不引入_main函数那么程序能够正常执行吗?
可以如果主函数命名为main,那么编译器会自动生成一个名为 _main 的函数,并将其作为 main 函数的入口点,也就是说先执行_main再执行main,实际上,_main 函数是由 C 库提供的,它会在程序启动时执行一些初始化操作,然后调用用户定义的 main 函数,并在 main 函数返回后执行一些清理操作。 总之,在使用标准的 C 库时,main 函数会默认调用 _main 函数,用户无需关心 _main 函数的具体实现,只需要在 main 函数中编写自己的代码即可。
main.c如下
启动代码如下
最后的MAP文件如下,可以发现多了一个从未使用过的!!!main 数据段,来自__main.o文件
如果将main.c的main函数与汇编文件的IMPORT main的main都修改为mymain,
main.c如下
启动文件如下
最后的map如下
可以发现此时就不会调用____main了
总结:存在main函数,会自动调用__main,在执行完__main后在执行main
为什么已经使用了SPACE后面还使_user_initial_stackheap 来初始化堆栈
在代码中,__user_initial_stackheap 是一个符号,它指向堆栈和堆空间的初始化函数。在初始化堆栈和堆空间时,程序会调用该函数来完成初始化。 在启动文件中,使用 SPACE 指令开辟了一段连续的内存空间,但这些空间并没有被初始化。因此,在程序运行时,需要调用 _user_initial_stackheap 函数来初始化堆栈和堆空间。该函数会将堆空间和堆栈的起始地址和大小传递给运行时库,然后运行时库会在该地址范围内分配堆空间和堆栈,并进行必要的初始化操作。 因此,使用 SPACE 指令在启动文件中开辟内存空间只是为了定义堆栈和堆空间的位置和大小,而初始化堆栈和堆空间的工作需要通过调用 _user_initial_stackheap 函数来完成。
该函数会被_main调用,这里的初始化起始应该就是只把数据搬移到指定的RAM空间把,虽然这里的数据都是0
不是已经有SPACE指令开辟堆栈空间了吗,__user_initial_stackheap是做什么的?
SPACE只是在bin文件中生成了对应的数据段,在烧录时会被写入到对应的FLASH空间上,但是实际上并没有对RAM进行操作,操作RAM加载与初始化堆栈空间是由__user_initial_stackheap函数实现的,设置完RAM后,我们就可以用,可以使用malloc去使用堆空间,函数使用的栈空间也得已使用,
据网上的资料显示mdk 启动代码 __user_initial_stackheap()解析,使用分散加载文件,启动代码里一定要使用__user_initial_stackheap()来重新设置堆栈和堆,(但好像其实不用也没事)实际上也就是将以下代码的RW 与ZI去加载到对应空间,查看MAP文件的Execution Region RW_IRAM1信息就可以发现需要加载的数据到哪些地址,具体查看散列文件章节
__user_initial_stackheap 会在哪里调用
会被c库函数_main调用,代替C库函数默认的堆栈和堆初始化函数,具体流程如下
是否使用__MICROLIB,带来的两种堆栈初始化的差异是什么
不定义__MICROLIB也就是不勾选Use MicroLIB的话,会使用__user_initial_stackheap去初始化堆栈空间,该函数会被_main调用,生成的map如下,可以发现使用了__main.o的!!main函数(实际就是__main函数)
勾选上Use MicroLIB的话,会发现生成的map里调用的是entry.o,段名也不是!!!main了,说明初始化的流程不一样了,而且往下寻找也没有发现!!main了,但实际上在汇编中还是有执行__main,而且主函数叫做main,理论上也会调用 __main函数,推测如下
在使用MicroLIB库的情况下,编译器和链接器会使用另外的启动文件和链接脚本,与标准C库的启动文件和链接脚本不同。在MicroLIB库的启动文件中,会定义一个名为__main的函数,它的作用类似于标准C库中的main函数,用于C语言程序的初始化和入口,
并且在尝试过将__initial_sp,__heap_base,__heap_limit三个值删掉后,并且主函数为main后,会报错,但若是修改main为其他名字,会发现就算将这些值删掉后,也不会报错,这样看来也像是因为用了main,所以调用了__main,所以报错。
但是该推断有一问题是为什么段名也没有出现__main
Last updated