做网站的时候遇到的问题,网站模板更换,外贸商城wordpress,宁夏城乡和住房建设厅网站映像文件分析#xff0c;ADS 中startup.s 文件启动分析#xff0c;学嵌入式开发ADS 必看2010-04-17 10:21声明#xff1a; 我也是转来的#xff0c;不是原创#xff0c;由于别人是网易的日志#xff0c;不能直接转#xff0c;所以…… 感谢原创#xff01;让我明白了st…映像文件分析ADS 中startup.s 文件启动分析学嵌入式开发ADS 必看2010-04-17 10:21声明 我也是转来的不是原创由于别人是网易的日志不能直接转所以…… 感谢原创让我明白了startup.s 文件中的一些代码。1、什么是arm 的映像文件arm 映像文件其实就是可执行文件包括bin 或hex 两种格式可以直接烧到ROM 里执行。在axd 调试过程中我们调试的是axf 文件其实这也是一种映像文件它只是在bin 文件中加了一个文件头和一些调试信息。映像文件一般由域组成域最多由三个输出段组成(RO,RW,ZI)输出段又由输入段组成。所谓域指的就是整个bin 映像文件所处在的区域它又分为加载域和运行域。对于嵌入式系统而言程序映象都是存储在Flash 存储器等一些非易失性器件中的而在运行时程序中的RW 段必须重新装载到可读写的RAM 中。简单来说程序的加载时域就是指程序烧入Flash中的状态运行时域是指程序执行时的状态。一般来说flash 里的整个bin 文件所在的地址空间就是加载域当然在程序一般都不会放在flash 里执行一般都会搬到sdram 里运行工作它们在被搬到sdram 里工作所处的地址空间就是运行域。我们输入的代码一般有代码部分和数据部分这就是所谓的输入段经过编译后就变成了bin 文件中ro 段和rw 段还有所谓的zi 段这就是输出段。在ARM 的集成开发环境中只读的代码段和常量被称作RO 段(ReadOnly)可读写的全局变量和静态变量被称作RW 段(ReadWrite)RW 段中要被初始化为零的变量被称为ZI 段(ZeroInit)。对于加载域中的输出段一般来说RO 段后面紧跟着RW 段RW 段后面紧跟着ZI 段。在运行域中这些输出段并不连续但RW 和ZI 一定是连着的。ZI段和RW 段中的数据其实可以是RW 属性。2、简单地址映射对于比较简单的情况可以在ADS 集成开发环境的ARM LINKER 选项output 中指定RO Base和RW Base即在simple 模式下告知连接器RO 和RW 的连接基地址。这种模式下ARM Linker 会输出以下符号它们指示了在运行域中各个输出段所处的地址空间在使用的时候可以用IMPORT 引入| Image$$RO$$Base| 表示RO 段在运行域中的起始地址|Image$$RO$$Limit|表示RO 区末地址后面的地址,即RW 数据源的起始地址|Image$$RW$$Base|RW 区在RAM 里的执行区起始地址,也就是编译器选项RW_Base 指定的地址|Image$$ZI$$Base|ZI 区在RAM 里面的起始地址|Image$$ZI$$Limit|ZI 区在RAM 里面的结束地址后面的一个地址RO Base 对应的就是| Image$$RO$$Base|RW Base 对应的是|Image$$RW$$Base|由于ZI 段是包含在RW 段里的所以|Image$$RW$$Limit| 就等于|Image$$ZI$$limit| 。下面给出一个例子假设RO Base 设为0x00000000后面的RW Base 地址是0x30000000然后在Options 选项中有Image entry point 是一个初始程序的入口地址设为0x00000000(程序的入口地址都是从代码段RO开始的)。现在要做的就是将RWsection 移到以0x30000000开始的地方,并且创造一个ZI section。首先比较Image$$RO$$Limit 和Image$$RW$$Base,如果相等,说明execution view 下RWsection 的地址和load view 下RW section 的地址相同,这样,不需要移动RW section;如果不等,说明需要移动RW section 到它在execution view 中的地方把ROM 里|Image$$RO$$Limt|开始的RW 初始数据拷贝到RAM 里面|Image$$RW$$Base|开始的地址,当RAM 这边的目标地址到达|Image$$ZI$$Base|后就表示RW 区的结束和ZI 区的开始,接下去就对这片ZI 区进行清零操作,直到遇到结束地址|Image$$ZI$$Limit|。ARM 映像文件及其地址映射二示例代码如下IMPORT |Image$$RO$$Limit|IMPORT |Image$$RW$$Base|IMPORT |Image$$ZI$$Base|IMPORT |Image$$ZI$$Limit|IMPORT main ; 声明C 程序中的Main()函数AREA Start,CODE,READONLY ; 声明代码段StartENTRY ; 标识程序入口CODE32 ; 声明32 位ARM 指令Reset LDR SP0x40003F00; 初始化C 程序的运行环境LDR R0|Image$$RO$$Limit| 得到RW 数据源的起始地址LDR R1|Image$$RW$$Base| RW 区在RAM 里的执行区起始地址LDR R3|Image$$ZI$$Base| ZI 区在RAM 里面的起始地址CMP R0,R1 检查RWsection 的地址在load view 和execution view 下是否相等BEQ LOOP1 如果相等就不移动RWsection,直接建立ZI scetionLOOP0 否则就copy RWsection 到execution view 下指定的地址CMP R1,R3LDRCC R2,[R0],#4 它把从R0 中的地址开始的section copy 到R1 中的地址开始的sectionSTRCC R2,[R1],#4BCC LOOP0LOOP1LDR R1,|Image$$ZI$$Limit| ZI section 末地址MOV R2,#0 将ZI section 需要的初始化量装入R2LOOP2CMP R3,R1 建立并初始化ZI sectionSTRCC R2,[R3],#4BCC LOOP2B main ; 跳转到C 程序代码Main()函数END注LDRCC R2,[R0],#4 将地址为R0 的内存单元数据读取到R2 中然后R0R04CC小于EQ相等为条件码。当我们把程序编写好以后就要进行编译和链接了在ADS1.2 中选择MAKE 按钮会出现一个Errors and Warnings 的对话框在该栏中显示编译和链接的结果如果没有错误在文件的最后应该能看到Image component sizes后面紧跟的依次是CodeRO Data RW Data ZI Data Debug 各个项目的字节数最后会有他们的一个统计数据后面的字节数是根据用户不同的程序而来的。Image component sizesCode RO Data RWData ZI Data Debug17256 158096 8 184 112580 ObjectTotals1064 299 0 0 796Library TotalsCode RO Data RWData ZI Data Debug18320 158395 8 184 113376Grand TotalsTotal RO Size(CodeRO Data) 176715(172.57KB)Total RWSize(RWDataZI Data) 192 ( 0.19KB)Total ROM Size(CodeRO DataRWData) 176723(172.58KB)Code 显示代码占用了多少字节。RO Data 显示只读数据占用了多少字节。RW Data 显示读写数据占用了多少字节。ZI Data 显示零初始化的数据占用了多少字节。Debug 显示调试数据占用了多少字节。Object Totals 显示链接到一起以后生成映像的对象占用了多少字节。Library Totals 显示已提取并作为单个对象添加到映像中的库成员占用了多少字节。Grand Totals 显示映像的真实大小。Grand TotalsLibrary TotalsObject Totals下面就以上面的数据为例来介绍几个变量的计算|Image$$RO$$Base|Image entry point0x00000000表示程序代码存放的起始地址|Image$$RO$$Limit||Image$$RO$$Base|Total RO Size CodeRo Data 0x017671510x0002B24C因为要满足4 的倍数所以1|Image$$RW$$Base|0x30000000由RW Base 指定|Image$$RW$$Limit||Image$$RW$$Base|Total RW Size RW DataZI Data 0x300000001920x300000C0|Image$$ZI$$Base||Image$$RW$$Base|RWData0x3000000080x30000008|Image$$ZI$$Limit||Image$$RW$$Limit|3、复杂地址映射对于复杂情况如RO段被分成几部分并映射到存储空间的多个地方时需要创建一个称为“分布装载描述文件”的文本文件通知连接器把程序的某一部分连接在存储器的某个地址空间。需要指出的是分布装载描述文件中的定义要按照系统重定向后的存储器分布情况进行。在引导程序完成初始化的任务后应该把主程序转移到RAM 中去运行以加快系统的运行速度。如下图为了解决复杂memory map 的问题需要用到scatter load 机制。__main() 和main()之不同当所有的系统初始化工作完成之后就需要把程序流程转入主应用程序即呼叫主应用程序。最简单的一种情况是IMPORT mainB main直接从启动代码跳转到应用程序的主函数入口当然主函数名字可以由用户随便定义。在ARM ADS 环境中还另外提供了一套系统级的呼叫机制。IMPORT _mainB _main_main()是编译系统提供的一个函数负责完成库函数的初始化和初始化应用程序执行环境最后自动跳转到main()。所以说前者(_main)是库函数后者就是我们自己编写的main()主函数因此我们用的B _main 其实是执行库函数,然后该库函数再调用我们的main() 函数,因此在单步调试时会看到先要跑一段程序(其实是库函数),然后再单步到我们自己的main 函数(这个同时也说明如果有B _main 则就对应必须有main 函数,否则编译出错),如果我们用B main 来进入我们的主函数的话,那在单步调试时就看到直接进入到我们自己的main 函数了,中间不会看到其他程序;那么用B _main 和用B main 这两这进入我们的main 函数方式有什么不同呢?如果采用前者则会由编译器加入一段段拷贝程序,即我们说的从加载域到执行域转化程序;而采用后者就没有这个了因此如果要进行段拷贝只能自己动手编写程序来实现了,完成段拷贝后就可以进入我们的主函数了,当然这个主函数不一定是叫做main(),可以起个其他好听的名字,这个有别于使用B __main 方式;不管采用哪种方式进入我们的程序,都要有一段段拷贝程序,跑完了段拷贝后才能可以进入我们主程序了!顺便提一下:startup.s 这个文件并没有所谓的段拷贝功能,再看也无益!对含有启动程序来说,执行地址与加载地址相同不容易实现如果执行地址与加载地址相同哪当然不需要做段拷贝,但是个人理解编译器还会加入段拷贝程序(如果用B __main 的话),只是因为条件不满足而不执行而已;但是对含有启动程序来说,执行地址与加载地址相同就不容易了.因为启动程序是要烧到非易失存储器里,用来在上电执行的,而这个程序必定会有RW 段,如果RW 放在非易失存储器,如FLASH,那就不好实现RW 功能了,因此要给RW 移动到能够实现RW 功能的存储器,如SRAM 等.因此,对含有启动程序来说,执行地址与加载地址相同就不容易实现;程序的入口点在C 库中的__main 处在该点库代码执行以下操作1. 将非零只读和读写运行区域从其载入地址复制到运行地址。2. 清零ZI 区域。3. 跳转到__rt_entry。