IAR IDE学习之---关于工程设置中“program entry”作用的猜想
扫描二维码
随时随地手机看文章
1、问题
一个工程包含众多源文件,需要指定一个入口地址,如IAR IDE中默认的入口地址(符号或者标号)"__iar_program_start"(在$TOOLS_DIR$armlib目录下的cstart.s文件中定义)。对于一般程序而言,入口地址就是程序首先被执行的指令(函数)。然后,在嵌入式系统中,必须考虑芯片复位时的异常向量表,在异常向量表中的复位异常跳转的地址,也是程序首先执行的地址。那么此时的program entry和复位异常跳转地址的孰是孰非?哪个才是真正的入口执行?
毫无疑问,肯定是复位异常首先被执行,那为什么需要配置"program entry"?
2、这得从系统的角度去寻找答案。在main函数运行之前,必须要有硬件初始化、软件初始化,其中软件初始化一般包含全局变量、0初始化变量、堆栈等等,这部分的初始化称为系统初始化,一般由启动代码完成。IAR IDE工具在默认的情况下会根据工程配置,在最终可执行的镜像中加入合适的启动代码。注意这里的【默认情况】是如何实现的:通常而言构建构成包括编译、链接,编译工程源代码得到目标文件,将目标文件和库文件作为输入,送给链接器链接得到可执行文件(镜像)。链接器链接时,解决了外部引用符号的重定位、地址安排等,但是,对于链接器而言,程序从哪里开始执行呢?
1)默认的main?非也,
2)链接器不需要知道程序从哪里开始执行,只需要强制低把启动代码加入,就可以实现从复位异常-》系统初始化-》main完成。但是,这样的链接器动作太过死板,如果用户需要另外的启动方式,则使用此链接器不能达到目标。
故,链接器必须知道程序的入口地址,当链接器得到程序的入口地址之后,才会链接包含入口地址的模块(库文件或者目标文件),并且依据此入口地址构建函数调用栈,在函数调用栈中的每一个函数都需要找到定义的模块,链接器由此得到所有需要链接的模块(库、目标文件),最后才是所有模块的链接形成可执行文件。
因此,“program entry”是为了给链接器指示用的。
3、异常向量表的复位异常跳转地址和“program entry”看来没有直接的关系,因为
1)芯片复位后,经复位异常跳转,不一定调到“program entry”处执行;
2)异常向量表需要配合目标系统硬件的启动方式、内存地址等众多因素,其最主要的作用是,芯片复位后调到启动代码处执行,以及发生中断、其他异常时,跳转到相应的处理代码处。
4、如果用户自行编写启动代码,只需要将该启动代码添加入工程,重新指定“program entry”则可以完成用户自定义的启动方式,重写了IAR IDE默认提供的cstartup.s启动文件。
5、为了验证上述猜想,做一个测试。新建一个简单的main工程,工程只有一个main.c源文件:
int main() { return 0; }
1)在默认情况下,工程选项配置成“__iar_program_entry”,如下图
编译后,查看生成的map文件:
******************************************************************************* *** ENTRY LIST *** Entry Address Size Type Object ----- ------- ---- ---- ------ ?main 0x00000100 Code Gb cmain.o [5] Abort_Handler 0x0000015c Code Wk vectortrap.o [5] CSTACK$$Base 0x00100000 -- Gb - Linker created - CSTACK$$Limit 0x00102000 -- Gb - Linker created - FIQ_Handler 0x0000015c Code Wk vectortrap.o [5] FIQ_STACK$$Base 0x00102100 -- Gb - Linker created - FIQ_STACK$$Limit 0x00102200 -- Gb - Linker created - IRQ_Handler 0x0000015c Code Wk vectortrap.o [5] IRQ_STACK$$Base 0x00102000 -- Gb - Linker created - IRQ_STACK$$Limit 0x00102100 -- Gb - Linker created - Prefetch_Handler 0x0000015c Code Wk vectortrap.o [5] Region$$Table$$Base 0x00000000 -- Gb - Linker created - Region$$Table$$Limit 0x00000000 -- Gb - Linker created - SWI_Handler 0x0000015c Code Wk vectortrap.o [5] Undefined_Handler 0x0000015c Code Wk vectortrap.o [5] __cmain 0x00000100 Code Gb cmain.o [5] __exit 0x00000138 Code Gb XXexit.o [5] __iar_init_vfp 0x000000ec Code Gb fpinit.o [4] __iar_init_vfp_v6 0x000000d8 Code Gb fpinit.o [4] __iar_program_start 0x00000080 Code Gb cstartup.o [5] __low_level_init 0x00000124 0x8 Code Gb low_level_init.o [3] __vector 0x00000000 Data Gb cstartup.o [5] _call_main 0x00000110 Code Gb cmain.o [5] _exit 0x0000014c Code Gb cexit.o [5] _main 0x00000118 Code Gb cmain.o [5] exit 0x00000134 0x4 Code Gb exit.o [3] main 0x0000012c 0x8 Code Gb main.o [1]
entry栏是函数名称,也是函数的入口地址;object栏是来源的目标文件。可以看到,“__iar_program_start“位于cstartup.o文件中,也就是IAR默认提供的启动文件。从__iar_program_start开始执行,还调用了其他的函数,所以在可执行文件中,还可以看到其他的entry,比如_main,?main,SWI_Handler等等(这些entry都在IAR安装目录下的armsrclibarm目录下的文件中定义)。这些都是因为从"__iar_program_entry"开始执行时的函数调用栈中的函数。
2)更改程序的入口为main,如下图:
编译后,查看生成的map文件
******************************************************************************* *** ENTRY LIST *** Entry Address Size Type Object ----- ------- ---- ---- ------ Region$$Table$$Base 0x00000000 -- Gb - Linker created - Region$$Table$$Limit 0x00000000 -- Gb - Linker created - __iar_init_vfp 0x00000094 Code Gb fpinit.o [4] __iar_init_vfp_v6 0x00000080 Code Gb fpinit.o [4] main 0x000000a8 0x8 Code Gb main.o [1]
对比可以发现,在最终可执行文件中,仅包括了main目标文件的main entry。链接器链接时,直接从mian entry开始执行,不再调用其他的函数,故不用链接到IAR系统提供的库,也不用自动加入启动代码。
总结:program entry是为了链接器服务的,它给出了链接器在链接时需要链接的模块,从而为用户提供重写启动代码的方法。
关于异常向量表如何配合目标硬件系统,将在后面的文件补充。