当前位置:  编程技术>c/c++/嵌入式

基于C程序启动代码的深入分析

    来源: 互联网  发布时间:2014-10-15

    本文导语:  一、映像文件基本组成映像文件加载时域包括RO和RW段,运行时域则包括RO、RW和ZI三个段。其中RO和RW段的内容在加载时和运行时是一样的,只是存储空间可能不同,而ZI段则是运行时由初始化函数创建的。RO段:Read-Only段,包括...

一、映像文件基本组成
映像文件加载时域包括RO和RW段,运行时域则包括RO、RW和ZI三个段。其中RO和RW段的内容在加载时和运行时是一样的,只是存储空间可能不同,而ZI段则是运行时由初始化函数创建的。
RO段:Read-Only段,包括源程序中的CODE段,只读数据段(包括变量的初始化值——可以是任意变量,全局/局部、静态/动态变量的初值;还包括数据常量——这个常量也可以是全局的或局部的。也就是说,编译器既要为变量分配存储空间——变量是可读写的,并不放在RO段,又要为变量的初值分配存储空间,两者是两回事)。
RW段:可读写段,主要指RW-DATA,也可能有RW-CODE。RW-DATA是指已经初始化的全局变量。
ZI段:Zero-Initialized段,主要包括未初始化的全局变量,编译器用0值对其进行初始化。该段中的数据由于是变量,因而也是可读写的,但在映像文件加载时,并不为ZI段分配存储空间,虽然在ADS编译器的Memory map文件中认为Total RW Size = (RW Data + ZI Data)。

二、代码,数据和变量在映像文件中的位置
上面简单总结了映像文件各段的组成。从程序的组成看,可以分为变量、数据和代码,其中变量又分为全局/局部的或静态/动态的,它们的存储空间又是如何分配的呢?
代码:一般是只读的,由编译器分配存储空间并放到映像文件的RO段。
数据:这里所指的数据都是常量(若可变则为变量),也包括指针常量,那么也属于只读的数据,也由编译器分配存储空间放到映像文件的RO段。
变量:主要根据生存期来分,因为生存期是按在内存中的生存时间来定义的,而作用域与存储空间分配无关。
1.全局变量和静态变量:包括静态局部变量和全局/静态指针变量在内,由编译器分配存储空间,已初始化的放到RW段,否则放到ZI段;
2.动态变量:主要是指局部变量,包括局部指针变量,函数参数,返回值等在内,占用栈空间。
 
三、启动过程中的堆栈初始化释疑
堆与栈:对于ARM,堆是向上生长的,栈是向下生长的。
局部变量占用栈(stack)空间(但其初始化值为数据,占用RO空间);
程序中动态申请的如malloc()和new函数申请的内存空间占用堆(heap)空间。
————×以下讨论不使用semihosting机制×————
因此,在转入C应用程序前,必须要为C程序准备堆栈空间。根据具体的目标平台的存储器资源,要对堆栈的初始化函数__user_initial_stackheap( )进行移植,主要是正确设置堆(heap)和栈(stack)的地址。它可以使用C或ARM汇编语言来编写,并至少返回堆基址(保存在R0中),栈基址(保存在R1)可选。因而一个简单的汇编语言编写的__user_initial_stackheap( )函数如下:
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDRR0, =0x20000 ;heap base
LDRR1, =0x40000 ;stack base, optional
MOV      PC, R14
注意,如果在工程中没有自定义这个函数,那么缺省情况下,编译器/链接器会把|Image$$ZI$$Limit|作为堆(heap)的基址(即把heap和stack区放置在ZI区域的上方,这也被认为是标准的实现[7])。但是,如果使用scatter文件实现分散加载机制,链接器并不生成符号|Image$$ZI$$Limit|,这时就必须自己重新实现__user_initial_stackheap( )函数并且设置好堆基址和栈顶,否则链接时会报错。
堆栈区还分为单区模型和双区模型,在双区模型中,还必须设置堆栈限制[4,6,7]。
关于重定义__user_initial_stackheap( )函数时几点要注意的地方:一是不要使用超过96字节的stack,二是不要影响到R12(IP,用作进程间调用的暂存寄存器),三是按规则返回参数值(R0:heap base;R1:stack base;R2:heap limit;R3:stack limit),四是让堆区保持8字节对齐[6]。

在启动代码中,还要对各个处理器模式的栈指针进行初始化。这个问题很容易与上面谈到的__user_initial_stackheap()函数的作用相混淆。可从以下几点来加以说明:
(1)在嵌入式应用中,启动代码分为两个部分:一是系统的初始化,包括中断向量表的建立、时钟、存储系统初始化、关键I/O口初始化、各处理器模式下的栈指针初始化等;二是应用程序初始化(或说C库函数初始化),包括RW段的搬移和ZI段的清零、C应用程序堆栈区的建立(__user_initial_stackheap()函数初始化堆栈指针)等。
从这个意义上说,两者并没有直接关系。
(2)但两者并不是没有联系的。以单区模型的堆栈区为例,由于栈是向下生长的,堆是向上生长的,系统模式的栈指针(与用户模式相同,共用一个R13寄存器来描述)实际上定义了用户模式下单区模型堆栈区的上限,而__user_initial_stackheap()函数中指定的heap基址则成为该堆栈区的下限。
因此,如果之前已经对系统模式(用户模式)的栈指针进行了初始化,则在重定义__user_initial_stackheap()函数时,就不需要重新定义stack base了。

四、启动代码的内容和初始化顺序探讨
前面已经指出,启动代码包括系统初始化以及应用程序运行环境的初始化两个部分,完成初始化后,就可以呼叫用户主程序了。参考资料[1]、[3]和[5]等都对两个部分的内容以及过程列出了非常清晰但又简单明了的步骤,这对于初学者来说稍微有点抽象。
如果不需要使用MMU进行地址重映射,那么,结合网上可以搜集的示例boot代码以及分析文档,加上自己动手移植和调试,也是比较容易理解的。如果是使用处理器自带的Remap控制寄存器来进行地址重映射,网上也有相关的代码,例如网友twentyone的boot代码【4510 bootloader的实现与分析(附源代码)】就非常清楚,另外,在《ARM学习报告》系列文章中也对其有详细的分析。
对于在启动过程中要使用MMU进行地址重映射的系统初始化顺序,在《使用AXD调试MMU地址映射程序手记(二)》一文中给出了一个参考步骤,并做了一定的说明。通过进一步参考权威资料,这里,对系统初始化顺序作了小的改进与修正如下:
①禁止所有中断→②初始化时钟→③初始化存储器→④初始化各模式下的栈指针→⑤初始化GPIO→⑥拷贝映像文件到SDRAM→⑦建立地址重映射表→⑧使能MMU→⑨应用程序初始化(RW&ZI区)→⑩使能异常中断→⑾呼叫主程序(dummyOS)。
主要对使能异常中断和应用程序初始化的顺序做了调整,即先进行应用程序的初始化,再使能异常中断。
......

    
 
 

您可能感兴趣的文章:

  • U-BOOT得掌握到什么程序,用不用深入去学
  • 深入C#任务管理器中应用程序选项隐藏程序本身的方法详解
  • 我是linux初学者,想一直深入到linux程序及内核的编程,请帮我规划一个书籍(资料)树
  • MFC程序执行过程深入剖析
  • [献给想深入学习Linux开发的网友] Linux 应用程序开发到内核开发的简明指南
  • java中子类继承父类,程序运行顺序的深入分析
  • 深入详解C编写Windows服务程序的五个步骤
  • Android 静默方式实现批量安装卸载应用程序的深入分析
  • Ubuntu程序开机自动启动设置(服务和自动运行配置文件)的几种方法
  • 关于系统变量,开机启动程序,和定时启动程序
  • 请问Linux如何象Windows 那样把我的程序放到启动里面,登录后启动我的程序
  • 怎么在HP11.00平台启动时运行一个程序,就是让该程序随系统启动,麻烦各位教我,谢谢了!
  • 请问Linux如何象Windows 那样把我的程序放到启动里面,登录后启动我的程序
  • 紧急问题:我启动一个java程序之后用ps一看,为和显示我启动了多个程序?然后我kill-9第一个pid之后,又全部没了
  • 板卡程序自启动和在用户模式下启动反差比较大
  • 怎样设置应用程序随linux启动而启动!
  • 为什么我在java程序里启动的一个程序在java程序关闭后,该程序的所有进程都关掉了
  • 为什么我的Linux系统拔掉网线后启动程序启动的非常慢?
  • 如何让自己做的一个程序在虚拟机启动时也自动启动
  • 使用了QWidget的程序,如何使用后台程序启动它?
  • 请问如何通过telnet的方式启动服务器(solaris)上的用.sh角本方式启动java写的应用程序,在退出telnet时服务器上的应用程序不会退出?
  • 请问如何设置驱动程序和应用程序的启动顺序和优先级呢?
  • 菜鸟求助:Linux 应用程序后台启动后关闭窗口程序退出
  • 请问如何让一个程序在系统启动的时候自动启动
  • 为什么,我在rc.local中加上几个自动启动的程序,linux启动时就会停在enabling swap space这里不动了
  • 请问gdb怎么处理带有启动参数的程序和多进程程序?
  • 我用的是putty软件SSH到linux上,但启动程序后关闭终端程序就退出了,在线等,请高人指教!
  • 运行什么程序都提示没有找到msvbvm5.0.dll,因此这个应用程序未能启动
  • 程序调用脚本,脚本启动另一个程序,如何让原始程序彻底释放资源?
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • c语言判断某一年是否为闰年的各种实现程序代码
  • 一个静态库包含多个函数,应用程序连接了库中的某个函数,应用程序目标代码中是否还包含了该静态库中的其他函数代码?
  • 使用libpcap实现抓包程序的步骤及代码示例
  • windows下如何把汇编代码和C代码编译成一个程序呢?
  • 谁有LINUX设备驱动程序第三版的程序代码,发我一份,非常的感谢!
  • 在哪里可以下载OREILLY的linux设备驱动程序一书的程序源代码?在线送分!
  • JSP和Servlet程序设计使用专辑---的程序代码哪有?
  • 为什么执行完execlp函数后程序就退出了,怎样才能让程序继续执行后面的代码
  • 如何编译一个包含用户级代码和内核级代码的程序
  • 想找一个java 做的文件下载程序(最好用URL类吧),那位有这方面的程序或代码?100分必送
  • 怎样看到java程序经过编译后的代码内容(bytecode的)或者在bytecode在JVM执行时JVM所解析的代码
  • Makefile,如何传递宏定义DEBUG?以便于程序编译或者跳过程序中#ifdef DEBUG中间的代码段?
  • 各位好,如何将VC++写的程序代码移植到Linux上?用GCC编译的话,程序需要做那些改动?
  • 关于软件或者程序的源代码问题??
  • 高分请教:redhat 的各种应用程序源代码放在哪儿?
  • java开发知识 iis7站长之家
  • C或C++恶意代码源程序
  • 如何用shell检查程序的返回代码
  • 想找些小程序练习(2K行左右的代码量),不知各位老手有何建议?
  • 请问在应用程序里用什么代码获得本机的IP,以及用什么代码设置本机的IP?谢谢
  • 求助 程序中添加互斥锁代码后编译怎么通不过 ?
  • 重装服务器后IIS网站错误(应用程序中的服务器错误)
  • Linux 编程怎么样在程序开启一个程序,和关闭一个程序?
  • windows server2008上PowerBuilder程序系统错误解决方法
  • 请问从一个java程序中如何调用另一java程序,并控制可以其状态(最小化、最大化、结束程序),还有怎样知道那个被调用的程序结束。
  • 程序员的八种级别,你属于哪一级?
  • Linux下的程序是在内存中运行的吗?为什么在程序运行的时候可以删除程序文件?
  • Linux下指定运行时加载动态库路径及shell下执行程序默认路径
  • 我的程序是用c写的gtk+程序,有个函数的参数要传给它图片的文件名,但是图片和原程序不在同一目录下怎么办?怎么在程序里指定文件的路径
  • 在docker容器中通过apt-get安装新的程序
  • 为什么linux下的C++程序这么少见? 请问那里有linux下的C++程序?什么类型的程序都可以.


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3