本帖最后由 小恩GG 于 2021-7-26 15:50 编辑
ELF文件
ELF文件(Executable and Linkable Format)的内部格式如下所示,
图 1
而格式中间的sections(节)存储的对应目标类型的详情如下所示。 - .text: 放编译好的二进制可执行代码
- .data: 已经初始化好的全局变量
- .rodata: 只读数据,例如字符串常量、const 的变量
- .bss: 未初始化全局变量,运行时会置 0
- .symtab: 符号表,记录的则是函数和变量
- .strtab: 字符串表、字符串常量和变量名
虽然数据段中的data、bss section(节) 也跟代码段一样,作为ELF文件的一部分会被一并烧录到Flash中,但在加载程序代码后,它却要寻址到RAM中的地址,所以在MCU启动后,跳转到Main()函数之前,需要将ELE文件格式中的数据段悉数拷贝到对应的RAM区域内。 ResetISR()正如前文所讲,在RT MCU启动成功后,跳转到应用程序的main()之前,会先跳到ResetISR()中断函数内进行“系统环境”的初始化工作,而其中最重要的一项:即是将ELE文件格式中的数据段拷贝到对应的RAM区域内。 通常ResetISR()中断函数定义在startup文件中,而不同的IDE提供的startup文件又不一样,故ResetISR()中断函数也会有所区别,本篇讨论的是基于MCUXpresso IDE的ResetISR()中断函数,代码具体如下所示。 - void ResetISR(void) {
- // Disable interrupts
- __asm volatile ("cpsid i");
- #if defined (__USE_CMSIS)
- // If __USE_CMSIS defined, then call CMSIS SystemInit code
- SystemInit();
- #else
- // Disable Watchdog
- volatile unsigned int *WDOG1_WCR = (unsigned int *) 0x400B8000;
- *WDOG1_WCR = *WDOG1_WCR & ~(1 << 2);
- volatile unsigned int *WDOG2_WCR = (unsigned int *) 0x400D0000;
- *WDOG2_WCR = *WDOG2_WCR & ~(1 << 2);
- // Write watchdog update key to unlock
- *((volatile unsigned int *)0x400BC004) = 0xD928C520;
- // Set timeout value
- *((volatile unsigned int *)0x400BC008) = 0xFFFF;
- // Now disable watchdog via control register
- volatile unsigned int *RTWDOG_CS = (unsigned int *) 0x400BC000;
- *RTWDOG_CS = (*RTWDOG_CS & ~(1 << 7)) | (1 << 5);
- #endif // (__USE_CMSIS)
- //
- // Copy the data sections from flash to SRAM.
- //
- unsigned int LoadAddr, ExeAddr, SectionLen;
- unsigned int *SectionTableAddr;
- // Load base address of Global Section Table
- SectionTableAddr = &__data_section_table;
- // Copy the data sections from flash to SRAM.
- while (SectionTableAddr < &__data_section_table_end) {
- LoadAddr = *SectionTableAddr++;
- ExeAddr = *SectionTableAddr++;
- SectionLen = *SectionTableAddr++;
- data_init(LoadAddr, ExeAddr, SectionLen);
- }
- // At this point, SectionTableAddr = &__bss_section_table;
- // Zero fill the bss segment
- while (SectionTableAddr < &__bss_section_table_end) {
- ExeAddr = *SectionTableAddr++;
- SectionLen = *SectionTableAddr++;
- bss_init(ExeAddr, SectionLen);
- }
- #if !defined (__USE_CMSIS)
- // Assume that if __USE_CMSIS defined, then CMSIS SystemInit code
- // will setup the VTOR register
- // Check to see if we are running the code from a non-zero
- // address (eg RAM, external flash), in which case we need
- // to modify the VTOR register to tell the CPU that the
- // vector table is located at a non-0x0 address.
- unsigned int * pSCB_VTOR = (unsigned int *) 0xE000ED08;
- if ((unsigned int *)g_pfnVectors!=(unsigned int *) 0x00000000) {
- *pSCB_VTOR = (unsigned int)g_pfnVectors;
- }
- #endif // (__USE_CMSIS)
- #if defined (__cplusplus)
- //
- // Call C++ library initialisation
- //
- __libc_init_array();
- #endif
- // Reenable interrupts
- __asm volatile ("cpsie i");
- #if defined (__REDLIB__)
- // Call the Redlib library, which in turn calls main()
- __main();
- #else
- main();
- #endif
- //
- // main() shouldn't return, but if it does, we'll just enter an infinite loop
- //
- while (1) {
- ;
- }
- }
复制代码 代码分析在ResetISR()中断函数中,如下代码片段根据__data_section_table、__data_section_table_end、__bss_section_table、__bss_section_table_end以彼此相邻的三个变量(4个byte)为一组来分别提取数据拷贝源地址、数据拷贝目的地址及拷贝数据的字节量,从而实现Data_section,BSS_Section初始化,流程框图如下所示。 - // Copy the data sections from flash to SRAM.
- while (SectionTableAddr < &__data_section_table_end) {
- LoadAddr = *SectionTableAddr++;
- ExeAddr = *SectionTableAddr++;
- SectionLen = *SectionTableAddr++;
- data_init(LoadAddr, ExeAddr, SectionLen);
- }
- // At this point, SectionTableAddr = &__bss_section_table;
- // Zero fill the bss segment
- while (SectionTableAddr < &__bss_section_table_end) {
- ExeAddr = *SectionTableAddr++;
- SectionLen = *SectionTableAddr++;
- bss_init(ExeAddr, SectionLen);
- }
复制代码
图 2 那么上图中的__data_section_table、__data_section_table_end、__bss_section_table、__bss_section_table_end存储在ELF文件的哪个位置呢? 以evkmimxrt1064_hello_world工程为例,我们可以通过查看Map文件获知上述四个变量存储在中断向量表的尾部区域,如下所示。
图 3 从中可以看出data_section和bss_section需映射在RAM区域的0x20000000和0x20000404地址,并且区域大小分别为0x404和0x108。 为了验证data_section和bss_section映射地址是否正确,通过命令readelf -S evkmimxrt1064_hello_world.axf查看这个ELF文件中所有的Section信息(如图4所示),可以发现data_section和bss_section映射地址和大小确如上文所讲。
图 4 欢迎留言和我分享你的疑惑和见解 ,也欢迎收藏或转发
|