早期接触eCos内存布局文件时,对它只是朦朦胧胧的理解,比较肤浅。然而通过本次对eCos布局文件的深入分析,发现对它有了更加深刻的理解。相信大家对比前后的文章就可以看得出来。
言归正传,在上一篇文章中,对eCos内存布局文件进行了一般介绍。本篇文章主要对eCos内存布局文件及其头文件的内容进行详细介绍和分析。
内存布局文件内容分析
ldi文件内容大同小异。以mlt_cortexm_stm3210e_eval_rom.ldi文件进行说明,内容如下:
// eCos memory layout #include <pkgconf/hal.h> #include <cyg/infra/cyg_type.inc> MEMORY { sram : ORIGIN = 0x20000000, LENGTH = 0x00010000-CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE flash : ORIGIN = 0x08000000, LENGTH = 0x00080000 rom : ORIGIN = 0x64000000, LENGTH = 0x01000000 ram : ORIGIN = 0x68000000, LENGTH = 0x00100000 } SECTIONS { SECTIONS_BEGIN SECTION_rom_vectors (flash, 0x08000000, LMA_EQ_VMA) SECTION_RELOCS (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_text (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_fini (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_rodata (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_rodata1 (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_fixup (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_gcc_except_table (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_eh_frame (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_got (flash, ALIGN (0x8), LMA_EQ_VMA) SECTION_sram (sram, 0x20000400, FOLLOWING (.got)) SECTION_data (ram, 0x68000000, FOLLOWING (.sram)) SECTION_bss (ram, ALIGN (0x8), LMA_EQ_VMA) CYG_LABEL_DEFN(__heap1) = ALIGN (0x8); SECTIONS_END } hal_vsr_table = 0x20000000; hal_virtual_vector_table = hal_vsr_table + 128*4; hal_startup_stack = 0x20000000 + 1024*64;
ldi文件分为4部分。
第1部分:包含头文件部分,包含下面两个头文件:
#include <pkgconf/hal.h> #include <cyg/infra/cyg_type.inc>
第2部分:MEMORY部分,定义了目标板上RAM和flash等存储器的配置,包括存储器的起始地址和长度。MEMORY是链接器命令语言的一个关键字,大括号中的每一行定义一种存储器。对于存储器的内存配置,需预留一部分内存用作中断的栈。因为片内RAM访问速度最快,所以一般使用片内RAM的一部分用作中断的栈。中断的栈大小由宏CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE指定。该宏可通过eCos配置工具进行设定。
第3部分:SECTIONS部分,描述了程序中段(section)的定义,包括text段(代码段)、data段(已初始化数据段)、rodata段(已初始化只读数据段)、bss段(未初始化数据段)等。与MEMORY一样,SECTIONS也是链接器命令语言的关键字,用于定义程序中的各个section。
在SECTIONS大括号中,使用体系架构模块链接脚本文件提供的宏来定义各个section。对于cortex-m处理器,这些宏定义在cortexm.ld文件中,具有一些共同的参数,如下表所述。
表1 SECTION_xxx宏参数描述
宏参数 |
说明 |
_region_ | 指示定义的section位于哪个存储区域,MEMORY部分定义了可使用的存储区域。 |
_vma_ | VMA,virtual memory address,虚拟存储地址,指目标程序执行时section的地址。有下面2种取值:
|
_lma_ | LMA,load memory address,加载存储地址,指该section被加载时所在的地址。有下面3种取值:
注:在嵌入式系统中,大多数情况下,LMA等于VMA。 |
下表说明了SECTIONS大括号中每行的含义。
表2 SECTION_xxx宏说明
SECTION宏 | 说明 |
SECTIONS_BEGIN | 定义调试信息段。这些段以debug开头。从该宏的名称可以看出,它应该放在SECTIONS部分的开始位置。 |
SECTION_rom_vectors | 定义vectors段,即向量表段。向量表段主要包含中断向量表和初始化代码。移植的时候,需要注意向量表段放置的位置要与启动方式对应起来。根据eCos启动方式,向量表段应放置在对应存储器指定位置。一般而言,向量表段被放置在存储器的开始位置。 |
SECTION_RELOCS | 定义重定位表段。这些段存储对应段的重定位表信息。链接时,链接器可能会修改函数调用或变量的地址。链接器怎么知道哪些函数或变量需要修改地址呢?重定位表就记录了哪些段的哪些函数调用地址或变量地址需要进行修改。 |
SECTION_text | 定义text段,即代码段。 |
SECTION_fini | 定义fini段。fini段中的代码会在main退出之后被执行。大多数情况下,最后编译出的目标程序都没有该段。 |
SECTION_rodata | 定义rodata段。rodata段存放全局的已初始化的只读变量,一般指const修饰的全局变量。 |
SECTION_rodata1 | 定义rodata1段。功能与rodata段类似。 |
SECTION_fixup | 定义fixup段。 |
SECTION_gcc_except_table | 定义gcc_except_table段。 |
SECTION_eh_frame | 定义eh_frame段。 |
SECTION_got | 定义got段。 |
SECTION_sram | 定义sram段。sram段指片内sram存储器的一段内存区域。注:sram段并非每种处理器都有。 |
SECTION_data | 定义data段。data段存放全局的已初始化变量。 |
SECTION_bss | 定义bss段。bss段存放全局的未初始化变量。 |
CYG_LABEL_DEFN(__heap1) | 定义一个名为__heap1的符号。这个符号指示堆的起始地址。 |
SECTIONS_END | 指示结束section的定义。 |
SECTIONS部分各段的定义都是基于GNU链接器标准的。对于一个目标程序而言,并不要求使用全部的段。
第4部分是向量服务程序表、虚拟向量表和启动栈的定义。
- hal_vsr_table:向量服务程序表,简称向量表。存放cortex-m处理器的异常向量表。对于cortex-m处理器,片内sram访问速度最快,所以向量表从片内sram起始地址开始铺设。
- hal_virtual_vector_table:虚拟向量表。虚拟向量是eCos定义的一组指向服务函数和变量的指针。
- hal_startup_stack:指定栈顶地址。cortex-m处理器的栈是向下生长的。
内存布局头文件
每个ldi文件都对应一个内存布局头文件,供eCos其它模块包含。
下面是mlt_cortexm_stm3210e_eval_rom.h头文件的源码。
// eCos memory layout #ifndef __ASSEMBLER__ #include <cyg/infra/cyg_type.h> #include <stddef.h> #endif #define CYGMEM_REGION_sram (0x20000000) #define CYGMEM_REGION_sram_SIZE (0x00010000-CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE) #define CYGMEM_REGION_sram_ATTR (CYGMEM_REGION_ATTR_R | CYGMEM_REGION_ATTR_W) #define CYGMEM_REGION_flash (0x08000000) #define CYGMEM_REGION_flash_SIZE (0x00080000) #define CYGMEM_REGION_flash_ATTR (CYGMEM_REGION_ATTR_R | CYGMEM_REGION_ATTR_W) #define CYGMEM_REGION_ram (0x68000000) #define CYGMEM_REGION_ram_SIZE (0x00100000) #define CYGMEM_REGION_ram_ATTR (CYGMEM_REGION_ATTR_R | CYGMEM_REGION_ATTR_W) #define CYGMEM_REGION_rom (0x64000000) #define CYGMEM_REGION_rom_SIZE (0x01000000) #define CYGMEM_REGION_rom_ATTR (CYGMEM_REGION_ATTR_R) #ifndef __ASSEMBLER__ extern char CYG_LABEL_NAME (__heap1) []; #endif #define CYGMEM_SECTION_heap1 (CYG_LABEL_NAME (__heap1)) #define CYGMEM_SECTION_heap1_SIZE (CYGMEM_REGION_ram+CYGMEM_REGION_ram_SIZE - (size_t) CYG_LABEL_NAME (__heap1))
从代码可知,主要是与内存区域相关宏的定义、与堆相关变量和宏的定义。与内存区域相关宏的定义要与ldi文件中内存配置必须保持一致。
注:本文节选自我正在编写的《嵌入式eCos开发详解》教程。