ADS中startup.s文件启动分析

映像文件分析,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 下RW
section 的地址和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 ; 声明代码段Start
ENTRY ; 标识程序入口
CODE32 ; 声明32 位ARM 指令
Reset LDR SP,=0x40003F00
; 初始化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 scetion
LOOP0 ;否则就copy RWsection 到execution view 下指定的地址
CMP R1,R3
LDRCC R2,[R0],#4 ;它把从R0 中的地址开始的section copy 到R1 中的地址开
始的section
STRCC R2,[R1],#4
BCC LOOP0
LOOP1
LDR R1,=|Image$$ZI$$Limit| ;ZI section 末地址
MOV R2,#0 ;将ZI section 需要的初始化量装入R2
LOOP2
CMP R3,R1 ;建立并初始化ZI section
STRCC R2,[R3],#4
BCC LOOP2
B main ; 跳转到C 程序代码Main()函数
END
注:LDRCC R2,[R0],#4 ;将地址为R0 的内存单元数据读取到R2 中,然后R0=R0+4
CC(小于),EQ(相等)为条件码。
当我们把程序编写好以后,就要进行编译和链接了,在ADS1.2 中选择MAKE 按钮,会出现
一个Errors and Warnings 的对话框,在该栏中显示编译和链接的结果,如果没有错误,在文
件的最后应该能看到Image component sizes,后面紧跟的依次是Code,RO Data ,RW Data ,
ZI Data ,Debug 各个项目的字节数,最后会有他们的一个统计数据,后面的字节数是根据
用户不同的程序而来的。
Image component sizes
Code RO Data RWData ZI Data Debug
17256 158096 8 184 112580 Object
Totals
1064 299 0 0 796
Library Totals
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Code RO Data RWData ZI Data Debug
18320 158395 8 184 113376
Grand Totals
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Total RO Size(Code+RO Data) 176715(172.57KB)
Total RWSize(RWData+ZI Data) 192 ( 0.19KB)
Total ROM Size(Code+RO Data+RWData) 176723(172.58KB)
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Code :显示代码占用了多少字节。
RO Data 显示只读数据占用了多少字节。
RW Data 显示读写数据占用了多少字节。
ZI Data 显示零初始化的数据占用了多少字节。
Debug 显示调试数据占用了多少字节。
Object Totals 显示链接到一起以后生成映像的对象占用了多少字节。
Library Totals 显示已提取并作为单个对象添加到映像中的库成员占用了多少字节。
Grand Totals 显示映像的真实大小。Grand Totals=Library Totals+Object Totals
下面就以上面的数据为例来介绍几个变量的计算:
|Image$$RO$$Base|=Image entry point=0x00000000;表示程序代码存放的起始地址
|Image$$RO$$Limit|=|Image$$RO$$Base|+Total RO Size ( Code+Ro Data )
=0x0+176715+1=0x0002B24C(因为要满足4 的倍数,所以+1)
|Image$$RW$$Base|=0x30000000;由RW Base 指定
|Image$$RW$$Limit|=|Image$$RW$$Base|+Total RW Size ( RW Data+ZI Data )
=0x30000000+192=0x300000C0
|Image$$ZI$$Base|=|Image$$RW$$Base|+RWData=0x30000000+8=0x30000008
|Image$$ZI$$Limit|=|Image$$RW$$Limit|
3、复杂地址映射
对于复杂情况,如RO段被分成几部分并映射到存储空间的多个地方时,需要创建一个称为“分
布装载描述文件”的文本文件,通知连接器把程序的某一部分连接在存储器的某个地址空间。
需要指出的是,分布装载描述文件中的定义要按照系统重定向后的存储器分布情况进行。在
引导程序完成初始化的任务后,应该把主程序转移到RAM 中去运行,以加快系统的运行速
度。
如下图,为了解决复杂memory map 的问题需要用到scatter load 机制。
__main() 和main()之不同:
当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序,即呼叫主应用程序。
最简单的一种情况是:
IMPORT main
B main
直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
在ARM ADS 环境中,还另外提供了一套系统级的呼叫机制。
IMPORT _main
B _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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

String Statistics(2008年珠海市ACM程序设计竞赛)

String Statistics 时间限制(普通/Java):1000MS/3000MS 运行内存限制:65536KByte 描述 You have an n n matrix of lower-case letters. You start from somewhere in the matrix, walk in one of the eight directions (left, right, up, down, up-left, up-right, …

从0-1背包问题学习回溯法、分支界限法、动态规划

一、0-1背包问题的描述 下面将使用回溯法、分支界限法、动态规划法来分析和解决此问题。 二、回溯法 (1)算法步骤 (2)代码如下(没有裁剪函数): 用i和n来判断结束与否,是因为解空间…

高可用性的HDFS—Hadoop分布式文件系统深度实践

《高可用性的HDFS—Hadoop分布式文件系统深度实践》基本信息作者: 文艾 王磊 出版社:清华大学出版社 ISBN:9787302282587上架时间:2012-5-14出版日期:2012 年5月开本:16开页码:371版次&#xff…

推荐:解析“extern”

http://blog.csdn.net/keensword/article/details/401114 写的很不错转载于:https://www.cnblogs.com/ATually/archive/2012/04/05/2432826.html

【字符串问题】求一个字符串中重复出现的最长的子串

2013-09-14 15:34:16 用后缀数组求一个字符串中重复出现的最长的子串。 用C中的string类可以很方便地进行操作&#xff0c;需将后缀数组保存在vector<string>&#xff0c;如下面代码中的string版本所示&#xff0c;但这样就会因为<string>有很大的开销&#xff1b;…

关于scanf和的问题

简单地说&#xff0c;如果使用scanf函数把一个字符串读进一个字符数组中&#xff0c;不要使用&&#xff1b;读取其他基本变量类型时&#xff0c;在变量名之前添加&。或者说&#xff0c;字符串名字本身就是一个地址了&#xff0c;所以不用取地址符。 如以下代码&#xf…

Oracle lower() Upper()函数

即将某字段信息中的字母转成小写、大写的方法。 将 user表里的user_name字段信息中含有字母的全部转成大写的方法&#xff1a; update user set  user_nameUpper(user_name) 同理得到将 user表里的user_name字段信息中含有字母的全部转成小写的方法&#xff1a; update user …

【作品】超级玛丽射击版

【下载地址】稍后上传 【以下内容摘自试验报告&#xff0c;可能狗屁不通&#xff0c;尽情谅解】 &#xff0d;游戏介绍: 简单的射击游戏,键盘控制动作[角色的移动,跳跃等],鼠标控制攻击的位置方向, 单击鼠标即射击,同时可以设置游戏规定时间,时间到则结束游戏. &#xff0d;游戏…

嵌入式系统Linux内核开发工程师必须掌握的三十道题

嵌入式系统Linux内核开发工程师必须掌握的三十道题 如果你能正确回答以下问题并理解相关知识点原理&#xff0c;那么你就可以算得上是基本合格的Linux内核开发工程师&#xff0c;试试看&#xff01; 1) Linux中主要有哪几种内核锁&#xff1f; Linux的内核锁主要是自旋锁和信号…

MySQL锁的用法之行级锁

2019独角兽企业重金招聘Python工程师标准>>> 行级锁是MySQL中粒度最小的一种锁&#xff0c;他能大大减少数据库操作的冲突。但是粒度越小&#xff0c;实现的成本也越高。MYISAM引擎只支持表级锁&#xff0c;而INNODB引擎能够支持行级锁&#xff0c;下面的内容也是针…

matlab中二维插值函数interp2的使用

下面是一段产生log-normal分布的代码&#xff0c;以此进行说明。 clear all; clc; for t1:100Traffic(t) curve(t); end MaxTraffic max(Traffic); w 0.2; Wmax 2*pi*w/3000; x[0:10:300]; y[0:10:300]; Nxlength(x); Nylength(y); Sigma 0.53; t 0&#xff1b; M 10*cu…

8天玩转并行开发——第五天 同步机制(下)

承接上一篇&#xff0c;我们继续说下.net4.0中的同步机制&#xff0c;是的&#xff0c;当出现了并行计算的时候&#xff0c;轻量级别的同步机制应运而生&#xff0c;在信号量这一块 出现了一系列的轻量级&#xff0c;今天继续介绍下面的3个信号量 CountdownEvent&#xff0c;Se…

hive中not in优化

比如&#xff1a;A,B两表&#xff0c;找到ID字段中&#xff0c;存在A表&#xff0c;但不存在B表的数据。A表共13w&#xff0c;去重后3w,B表共2W&#xff0c;且有索引方法一not in&#xff0c;易理解&#xff0c;效率低&#xff0c;时间&#xff1a;1.395sselect distinct A.id …

Quartz 2D编程笔记

当我们需要在一个图形上下文中构建一个路径时&#xff0c;我们需要调用CGContextBeginPath来标记Quartz。然后&#xff0c;我们调用函数CGContextMovePoint来设置每一个图形或子路径的起始点。在构建起始点后&#xff0c;我们可以添加直线、弧、曲线。记住如下规则&#xff1a;…

bitmap算法

博文转载于http://www.cnblogs.com/dyllove98/archive/2013/07/26/3217741.html概述所谓bitmap就是用一个bit位来标记某个元素对应的value&#xff0c;而key即是这个元素。由于采用bit为单位来存储数据&#xff0c;因此在可以大大的节省存储空间算法思想 32位机器上&#xff0c…

Ubuntu下如何解压缩zip,tar,tar.gz,tar.bz2文件

tar解包&#xff1a;tar xvf FileName.tar打包&#xff1a;tar cvf FileName.tar DirName&#xff08;注&#xff1a;tar是打包&#xff0c;不是压缩&#xff01;&#xff09;———————————————.gz解压1&#xff1a;gunzip FileName.gz解压2&#xff1a;gzip -d F…

SELECT INTO 和 INSERT INTO SELECT 两种表复制语句

Insert是T-sql中常用语句&#xff0c;Insert INTO table(field1,field2,...) values(value1,value2,...)这种形式的在应用程序开发中必不可少。但我们在开发、测试过程中&#xff0c;经常会遇到需要表复制的情况&#xff0c;如将一个table1的数据的部分字段复制到table2中&…

向前插入迭代器

**向前插入迭代器&#xff0c;是架构在具有push_front向前插入函数的序列容器&#xff08;比如deque list&#xff09;上的输出迭代器&#xff0c;可以利用*改迭代器提供的“”&#xff0c;“*”&#xff0c;“”以“*fii9;”的类似的形式完成容器元素的赋值。**其中“*”和&qu…

Discuz!X集群部署的系统方案和改造方式讨论

多WEB部署时&#xff0c;面临的核心问题是WEB服务器间的数据共享和同步。就数据存储的方式而言&#xff0c;Discuz数据包含两部分&#xff0c;一部分存储在MySQL数据库中&#xff08;用户、帖子等文本类、结构化的数据&#xff09;&#xff0c;一部分存储为文件&#xff08;附件…

static和extern的用法总结

static用法见&#xff1a; 1、http://www.cnblogs.com/yezhenhan/archive/2011/10/31/2229724.html2、http://www.52rd.com/Blog/Detail_RD.Blog_imjacob_5297.htmlextern用法&#xff1a;1、http://blog.csdn.net/cameracanon/article/details/40614192、http://blog.csdn.net…