006:__Main介绍(ADS下)(转)
__Main
在软件仿真的情况下,执行“B __main”指令,能使程序跳到C文件的main函数,但用硬件仿真时,还没执行到main函数时就进入了异常中断。
原因是执行“B __main”指令后,程序先跳到__main库函数的入口,再进行一些初始化操作,最后再跳入用户的main函数。但在初始化过程中,由于堆栈或其它原因造成程序出错。有两种方法可以解决这个问题。第一:将“B __main”指令直接改成“B main”,使程序不进行初始化而直接跳入用户的main()函数。第二:合理初始化堆栈。由于考虑到刚接触ARM和将问题简单化,我选择了第一种方法。
第二种答案如下:
IMPORT __main
.... ....
BL TargetResetInit
B __main
1、此后流程将跳转到ADS提供的启动代码__main函数处,负责完成库函数的初始化及相关功能,并最终引导处理器入口用户代码main()函数,其代码流程为:
嵌入式系统在进入应用主程序之前必须有一个初始化的过程,该过程完成系统的启动和初始化功能,初始化过程可以分成两部分来看:
√ _main负责设置运行映像存储器映射;
√ _rt_entry负责库函数的初始化。
_main完成代码和数据的复制,并把ZI数据区清零,这一步当代码和数据区在存储和运行时处于不同的存储器位置时有意义。接着_main跳进_rt_entry,进行STACK和HEAP等的初始化。最后_rt_entry跳进应用程序的入口main()。当应用程序执行完时,_rt_entry又将控制权交还给C库函数,函数main()在ADS中有特殊的意义。当一个程序工程项目中存在main()时,连接器会把_main和_rt_entry中的初始化代码连接进来;如果没有main()函数,初始化过程就不会被连接,初始化过程中一些标准的C库函数失效。而用户应用程序初始化过程并未显式的完成这些堆栈初始化及数据拷贝操作,应用程序将启动不成功。
2、__main()为编译系统提供的一个库函数,使用__main标号引导系统时必须将应用程序的入口数定义为main();
3、若希望系统进入应用程序前自动完成系统调用(如库函数的初始化、RW、ZI数据从Flash到RAM的拷贝(加载映像--->执行映像)等)的初始化过程,必须使用__main标号以跳至ADS提供的初始化函数库,这种情况一般需做一些库函数移植及重定向问题解决;这一点上,应用程序入口必须以main函数标识;
4、若所有的初始化步骤都已显式的完成(如堆栈初始化、加载映像到执行映像RW、ZI数据的拷贝等),应用程序入口函数XXXX()可以任意定义(非main,这样可以避免自动链接上__main,从而跳过__main),初始化完成后直接B XXXX即可,这一点上,main()函数并不具有特殊性;
5、若必须的初始化步骤都未显示的完成,一意孤行的B XXXX,程序运行的结果将不可预料。一个典型的例子是:若用户并未显示完成加载映像--->执行映像的数据初始化而直接B XXXX,则应用程序中所有的全局变量并不会被预期的初始化,而所有的ZI变量也不会被预期的清零;
6、本人在调试LPC2294 LCD模块时才认识到上述第五点(5、)的重要性:程序指令流并没有错,但LCD上显示的全部是乱码。我用两种方法解决了这一bug:
(1)将所有的字库数据定义为const常量,程序运行正确;
(2)将B Main 修改成B __main接着将应用入口改为int main(void) 而非int Main(void),程序也得到正确的执行;
Initializing memory required by C code
The initial values for any initialized variables (RW) must be copied from ROM to RAM.
All other ZI variables must be initialized to zero. The library initialization code called
at __main performs the copying and initialization.
Using the main function
When the compiler compiles a function called main(), it generates a reference to the
symbol __main to force the linker to include the basic C run-time system from the ANSI
C library. (The symbol __main is marked as an entry point.)
建议和我一样的初学者还是乖乖的B __main接着将应用入口设置为main()函数的好。
;------------------------------------------------------------------------------------------------------
关于_main 和main ,__rt_lib_init的讨论18573103324
2009-11-13 21:59:16 收藏 | 打印 | 投票(0) | 评论(0) | 阅读(13156) ◇字体:[大 中 小]
根据网上的文章,关于ADS的C程序入口一些问题,矛盾之处甚多,笔记一下,求答。
问题(参照3篇文章):
1.根据A的观点,_main()函数是在我们的主函数main()之前完成一些初始化工作的,由编译系统自动生成,那么_main()是不需要我们调用也是我们无法调用的,既然如此,B,C说法就矛盾了,B说所以我们不用__main()函数初始化运行环境的时候,要自己编写相应的代码来完成相应的内容,C说,如果在C入口没有调用编译器的链接库(__main),不是说_main()不需要我们调用,而且在main()调用前C库已经调用了,怎么存在不调用的情况?
实际问题就是:_main()是否是编译系统自动调用,并且是在main()函数之前?
2.A原文说,用main()函数的返回值作参数调用exit()。 在main()函数返回之后,相当于程序结束了,那返回个值有什么意义?假如说调用exit()是释放资源,直接返回不就行了?
A原文描述:
http://www.west263.com/info/html/chengxusheji/C-C--/20080224/10517.html
;-------------------------------------------------------------
引 言
随着对高处理能力、实时多任务、超低功耗等方面需求的增长,高端嵌入式处理器已进入了国内研发人员的视野,并在国内得到了普遍的重视和应用。ARM是现在嵌入式领域应用最广泛的RISC微处理器结构,凭借低成本、低功耗、高性能等长处占据了嵌入式系统应用领域的领先地位。ADS是ARM公司推出的ARM集成研发环境,提供了对C和C 的支持,是现在研发ARM的主要工具。本文针对日益缩短的嵌入式研发周期,结合ARM系统研发调试经验,对使用ARM标准库进行应用程式研发作了比较系统的分析。
1 ARM标准库介绍
ADS提供了ANSI C和C 标准库,本文仅讨论ANSI C库,该库包含下面几个部分:
◇IS0 C库标准所定义的函数;
◇在semlhosted环境下用来实现C库函数和目标相关的函数;
◇C和C 编译器要使用的heIper函数。
该库提供的诸如文档输入输出之类的设备,使用了标准的ARM semihosted执行环境(semihosting是针对ARM目标机的一种机制,他能够根据应用程式代码的输入/输出请求,和运行有调度功能的主机通信,这种技术允许主机为通常没有输入和输出功能的目标硬件提供主机资源)。ARMulator、Angel和Multi-lCE都支持这个环境,能够使用ADs中提供的研发工具研发应用程式,然后在ARMulator或是研发板上运行和调试该程式。假如要使应用系统单独于这个环境,则必须重新实现C库中依赖于这个环境的相关函数,根据用户系统的运行环境对C库进行适当的裁减。 ..
使用ANSI标准C库进行程式研发,不但能够提高研发效率而且能够增强程式的可移植性。在程式中使用库函数,必须先建立一个库函数能够执行的环境,这些工作都由库中的函数完成。当应用程式链接了C库中的函数时,C库中的函数将完成:
◇创建C程式所需的执行环境(建立栈,假如需要创建一个堆,初始化程式使用的部分库);
◇调用main()函数开始执行C程式;
◇支持程式使用的Is0定义的函数;
◇捕获运行时的错误和信号,假如需要,根据错误终止执行或程式退出。 版权申明:本站文章均来自网络,本站所有转载文章言论不代表本站观点
2 裁减ARM标准C函数库
标准库中包含了部分依赖于ARM semihosted执行环境的函数,这部分函数的函数名中包含有单个或两个下划线“-”,需要重新实现这部分函数。假如在程式中定义这些函数,则编译器就会使用新定义的函数,这个过程称为库函数的裁减。一般情况下,只需要重新定义很少的几个函数就能够使用C库。
ARM应用系统开始执行用户应用程式,必须先将应用程式加载到执行域,建立应用程式的执行环境。使用C库时,这些繁琐的工作就大部分由c函数来完成了。汇编程式完成系统初始化后,跳转到C程式的人口_main()(注意:不是main(),当C程式中定义了main()主函数时,编译器就会生成_main代码)。由_main()引导库函数完成C执行环境的初始化,具体过程如下:
◇将非启动代码的RO和RW执行域代码从加载域地址复制到执行域地址;
◇将ZI域清零;
◇跳转到_rt_entry。
调用_main()将大大简化汇编启动代码的编写,汇编代码仅需完成系统硬件的初始化,而没有必要将代码从加载域地址复制到执行域地址,连同ZI域清零等工作。特别是当使用分布式加载时_main()的作用就更加明显了。但是_main()并没有建立C库运行必须的环境,这项工作由_rt_entry()完成,主要调用过程为: 特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系.
◇调用_rt_stackheap_init()建立堆和栈;
◇调用_rt_lib_init()初始化引用的库函数;假如需要,建立main()函数的参数argc和argv等;
◇调用main()函数,执行应用程式,能够应用库函数;
◇用main()函数的返回值作参数调用exit()。
_rt_entry并不是C函数,他是用ARM C库编程的起始点。_rt_entry不能用C语言宴现,因为这时候堆栈还没有建立,堆栈由_ rt_stackheap_init()来建立。
上面简单介绍了C程式使用库函数时的调用过程,由_rt—stackheap_init()建立C库使用的内存模型--堆和栈。因为ARM库是建立在semihosted执行环境的,他实现的内存模型是基于这个环境的,所以必须修改这个内存模型建立机制。表1列出了需要重新实现的函数,实现了这些函数,应用程式就能够脱离宿主机环境单独运行了。其中,必须重新实现的是_user initial_stackheap(),因为默认的实现是基于semihosted执行环境的,该函数被_n_stackheap_init()调用创建内存模型,其他两个函数没有默认的实现。 .
实现该函数,必须满足下面的条件:
◇使用不超过96字节的栈空间;
◇除了R12(ip)外不要污染其他寄存器;
◇将堆基址、栈基址、堆边界和栈边界分别存在RO~R3作为返回参数;
◇堆必须保持8个字节对齐。
实现例程如下: .
为了提高应用程式研发效率和可移植性,希望在目标系统上使用ARM库提供的标准输人输出库函数。
高层输入输出函数是不依赖于目标系统环境的,但是高层输入输出函数必须调用依赖于目标系统的底层函数,才能实现应用系统的输入输出。依据目标系统硬件环境重新定义这些底层函数,就能够使用库提供的标准input/output库函数了。下面以裁减ARM标准库提供的printf系列输出函数为例来作说明。
标准I/O库中最常用的是printf系列函数,包括_printf()、printf()、_fprintf()、fprintf()、vprintf()和vfprintf()。任何这些函数非透明地使用_FILE,并且仅依赖于fputc()和ferror()两个函数。函数_printf()和_fprintf()和printf()和fprintf()的区别仅在于前两个函数不能格式化浮点值。只要定义了自己的_FILE版本和fputc()、ferror()函数,外加定义一个具备FILE类型的_stdout变量,就能够不作任何修改地使用printf系列、fwrite()、fputs()和puts()函数了。
下面给出了具体实现的模板,能够根据实际需要修改。
#include<stdio.h>
struct__FILE
{
int handle; 版权申明:本站文章均来自网络,本站所有转载文章言论不代表本站观点
/*用户需要的任何代码(假如使用文档仅是为了调试使用prinft在标准输出端输出信息,则无需任何文档处理代码)*/
版权申明:本站文章均来自网络,如有侵权,请联系028-86262244-215 ,我们收到后立即删除,谢谢!