嵌入式linux开发 (二十) 内存管理(3.2) STM32F407ZGT6内存管理之从链接到执行的过程(MPU-Without)

链接

  • 链接做了什么
1.3 What the linker does when constructing an executable image
armlink performs many operations, depending on the content of the input files and the command-line options you specify.

When you use the linker to construct an executable image, it:
Resolves symbolic references between the input object files.
Extracts object modules from libraries to satisfy otherwise unsatisfied symbolic references.
Removes unused sections.
Eliminates duplicate common groups and common code, data, and debug sections.
Sorts input sections according to their attributes and names, and merges sections with similar attributes and names into contiguous chunks.
Organizes object fragments into memory regions according to the grouping and placement information provided.
Assigns addresses to relocatable values.
Generates an executable image.
  • 链接脚本
链接的时候以(不同符号的相对位置信息)为输入,对所有.o中的符号进行重定位,可以通过三种方式设置,参考http://blog.sina.com.cn/s/blog_13f1300a80102w8de.html
1、 适用于低复杂度链接模式.直接通过Linker选项卡中的R/O  Base和R/W Base来设定链接信息
	ro-base和rw-base主要是解决加载域和执行域这些问题的,详细的可以到网上去搜RealView Compilation Tools的Linker and Utilities Guide,其中第五章Using Scatter-loading Description Files对你的问题有非常详细的解答。
	ro-base和rw-base都是在使用简单链接模式时用到的命令行参数,而在使用分散加载文件的时候就彻底失去意义,你在程序中自己指定这些值是没用的,只能在链接时修改参数来指定。ARM的链接器在链接后产生的相应变量名分别是Image$$RW$$Base和Image$$RO$$Base 。
2、 适用于中复杂度链接模式.采用间接生成的分散加载文件:采用target对话框中的ROM和RAM地址(地址需要开发者填写),自动生成一个加载文件 
3、 适用于高复杂度链接模式.采用分散加载文件(该文件需要开发者编写)
// 连接脚本中有 加载区域 和 执行区域的概念
-------------------- 位置在哪里
// 加载区域 : 一般为主Flash. hex文件的存储位置(包括Code RO-data RW-data ZI-data)
// 执行区域 : 一般为主Flash(包括Code RO-data) SRAM1及SRAM2(包括RW-data ZI-data) 
-------------------- 怎么用
程序的入口点位于C库中的_main,流程为
1. 将 non-root (RO and RW) execution regions 从 加载地址 拷贝到 执行地址.如果data sections被压缩,则将它们从加载地址(加载区域)解压缩到执行地址(执行区域)//具体实现在(InRoot$$Sections) 
	// 其实 RO(RO-data) 的 加载地址 和 执行地址是一样的.都在0x08000000(主Flash上)
	// RW(RW-data) 加载地址在 0x08000000 + sizeof(RO-data),执行地址在0x20000000(SRAM1)
	// ZI(ZI-data) 加载地址在 0x08000000 + sizeof(RO-data),执行地址在0x20000000(SRAM1) // 其实这一段并没有加载,因为加载了也要清0,所以干脆留空.只记录 ZI-data 执行地址的开始地址和结束地址
2. 将ZI regions清0
3. 跳转到__rt_entry

  • hex 文件
分散加载文件影响了 hex文件内容布局
hex文件格式:
	1.由N行冒号(:)开头的行组成
	2.每行的含义
	冒号(:) + 2字节长度数据长度(表示数据有x个字节) + 四字节数据地址 + 两字节数据类型 + x字节数据 + 2字节校验

------------------------------------重要信息的体现

hex文件内容第一行
	:020000040800F2
	表示从第2行开始,0x0800000的数据.(:02000004/0800/F2)(加载地址在这里体现)
	另外内容中有很多(00000020)的身影,估计是执行地址(0x20000000)的体现
	另外栈地址(0x20000738)在第二行体现(:100000003807002029020008C10300087703000810),也在其他一行体现,不知道是做什么的
	另外堆地址(0x20000138)39行体现,:100250000105000889010008380100203807002046

也就是说,所有的运行相关的东西都会在hex文件中体现,(毕竟运行时的状况来自flash,flash中的内容来自hex,)
	
  • 烧写过程
运行jflash软件,读取hex文件,然后通过jlink盒子,输出符合Jtag协议的时序
	1.将主flash烧写算法弄到SRAM1中.
	2.读取hex中的数据的一部分,并烧写到sram1.
	3.运行sram1中的算法,将sram1的数据烧写到主flash,再转到2,直到hex中数据完全读取完毕.
	// 因为sram1只有112KB,而主flash有1M,所以如果烧写的flash比较大(例如大于等于112KB)的话,肯定需要循环
	// jflash软件 <--jlink驱动--> <--usb--> jlink盒子(可以是stm32做的) <--jtag--> 目标芯片(stm32f407zgt6)
	// 输入文件有 1. hex文件  2. flash烧写算法.
-------------------- hex文件内容全部烧写到主flash中去了吗?
少写了hex文件中的部分内容.
hex文件中有部分内容是用来指示烧写的.例如:
1.数据类型不是数据记录的行 // 该行中的08000000地址作为jtag中的时序存在,标识地址
2.数据类型为数据记录的行 的 冒号(:) 2字节长度数据长度(表示数据有x个字节)   四字节数据地址 2字节校验



  • Flash
------------------------------------重要信息的体现
第一个加载域应该在Flash数据中找不到了(上电即执行0x08000000的代码)2及N个加载域 应该在flash中找得到(毕竟代码中要执行.跳转指令肯定标识了第2个以及第N个加载域的起始地址)
执行域肯定是需要体现的
// 对比hex和flash中的数据区别,除了之前说的辅助信息的区别,其他有无区别(待验证???)
// 可以反读flash中的数据进行验证.
  • 执行
1. RESET
2. XIP on 主Flash
3. 代码执行序列到_main
	3.1 将 non-root (RO and RW) execution regions 从 加载地址 拷贝到 执行地址.如果data sections被压缩,则将它们从加载地址(加载区域)解压缩到执行地址(执行区域)//具体实现在(InRoot$$Sections) 
	------------对应 .code 段 和 .ro-data 段
	// 其实 RO(RO-data) 的 加载地址 和 执行地址是一样的.都在0x08000000(主Flash上)
	------------对应 .data 段
	// RW(RW-data) 加载地址在 0x08000000 + sizeof(RO-data),执行地址在0x20000000(SRAM1)
	------------对应 .bss 段
	// ZI(ZI-data) 加载地址在 0x08000000 + sizeof(RO-data),执行地址在0x20000000(SRAM1) // 其实这一段并没有加载,因为加载了也要清0,所以干脆留空.只记录 ZI-data 执行地址的开始地址和结束地址
	------------对应 .bss 段
	3.2. 将ZI regions清0
	3.3. 跳转到__rt_entry
		------------对应 .stack 段 和 .heap段
		// 栈的初始化不涉及到内存,只涉及到SP寄存器
		// 堆的初始化涉及到内存, 1.清零堆空间 2.在堆中建立数据结构来对堆进行分块 
		3.3.1. Sets up the stack and the heap by one of a number of means that include calling __user_setup_stackheap() , calling __rt_stackheap_init() , or loading the absolute addresses of scatter-loaded regions.
		3.3.2. Calls __rt_lib_init() to initialize referenced library functions, initialize the locale and, if necessary, set up argc and argv for main() . For C++, calls the constructors for any top-level objects by way of __cpp_initialize__aeabi_ .
		3.3.3. Calls main() , the user-level root of the application. From main() , your program might call, among other things, library functions.
		3.3.4. Calls exit() with the value returned by main() 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章