MDK main函数运行前的详细分析

合集下载

main 方法

main 方法

main 方法首先,让我们来看一下 main 方法的基本语法。

在Java中,main 方法是程序的入口点,它的语法格式如下:```java。

public class Main {。

public static void main(String[] args) {。

// 在这里编写你的程序代码。

}。

}。

```。

在这段代码中,我们定义了一个名为 Main 的类,并在其中定义了一个名为main 的方法。

该方法被声明为 public(公共的),static(静态的)和 void(无返回值)。

它接受一个类型为 String 数组的参数 args。

在 main 方法中,我们可以编写我们的程序代码,以实现我们想要的功能。

接下来,让我们来看一下 main 方法的作用。

作为程序的入口,main 方法是程序开始执行的地方。

当我们运行一个 Java 程序时,虚拟机会自动查找并执行名为main 的方法。

因此,我们可以将 main 方法看作是程序的起点,所有的程序逻辑都将从这里开始执行。

除了作为程序的入口,main 方法还可以接受命令行参数。

在上面的语法中,我们可以看到 main 方法接受一个类型为 String 数组的参数 args。

这些参数可以在程序运行时从命令行传入,我们可以根据这些参数来定制程序的行为。

在实际应用中,我们经常会用到 main 方法来执行一些初始化操作,比如初始化变量、加载配置文件等。

此外,我们还可以在 main 方法中调用其他方法,实现程序的逻辑功能。

总之,main 方法是程序的核心,它承担着程序执行的重要任务。

在使用 main 方法时,我们需要注意一些细节。

首先,main 方法必须被声明为public 和 static,这是因为虚拟机需要通过这个方法来启动程序。

其次,main 方法的参数类型必须是 String 数组,这是因为命令行参数都以字符串的形式传入。

最后,main 方法没有返回值,它的返回类型被声明为 void。

main函数原型

main函数原型

main函数原型Main函数原型是编程语言中的一个重要概念,它定义了程序的入口点和参数。

在这篇文章中,我们将深入探讨main函数的原型,包括其语法、参数和返回值等方面。

让我们来了解一下main函数的语法。

在大多数编程语言中,main 函数的原型通常是这样的:int main(int argc, char *argv[])其中,int表示main函数的返回值类型,通常为整数类型;argc 表示命令行参数的个数;char *argv[]表示命令行参数的数组。

接着是参数int argc,它表示命令行参数的个数。

命令行参数是指在程序运行时从命令行中输入的参数,比如在命令行中输入"program.exe arg1 arg2",其中arg1和arg2就是命令行参数。

argc参数的值为命令行参数的个数加1,其中包括程序名称本身。

最后是参数char *argv[],它表示命令行参数的数组。

argv数组中的每个元素都是一个字符串,表示一个命令行参数。

argv[0]表示程序的名称,argv[1]、argv[2]等表示其他命令行参数。

通过遍历argv数组,我们可以获取每个命令行参数的值,从而对程序进行相应的操作。

在main函数中,我们可以根据需要使用命令行参数来控制程序的行为。

比如,我们可以根据命令行参数的值来判断程序是执行某个功能还是执行另一个功能;或者我们可以根据命令行参数的值来读取相应的输入文件或输出文件等等。

除了命令行参数,main函数还可以使用其他方式来获取输入。

比如,我们可以使用标准输入流stdin来获取用户的输入,然后根据输入来执行相应的操作。

类似地,main函数也可以使用标准输出流stdout来输出结果。

在编写main函数时,我们需要注意一些细节。

首先,我们应该对命令行参数的个数进行合理的判断和处理,以避免出现数组越界的错误。

其次,我们应该对命令行参数的值进行合法性检查,以防止恶意输入或非法操作。

关于main函数的叙述

关于main函数的叙述

关于main函数的叙述main函数是C语言中的一个非常重要的函数,它是程序的入口点,也是程序执行的起始点。

在一个C语言程序中,必须有且只能有一个main函数。

本文将从几个方面详细介绍关于main函数的一些重要内容。

main函数是C语言程序的入口函数。

当程序被运行时,操作系统会首先调用main函数开始执行程序。

main函数的定义通常是这样的:int main() {}。

其中,int是main函数的返回类型,表示main 函数执行完毕后会返回一个整数值;main是函数名;空括号表示main函数不接收任何参数。

我们可以根据需要给main函数添加参数,例如int main(int argc, char *argv[]),这样main函数就可以接收命令行参数。

main函数的返回值可以用来表示程序的执行状态。

根据C语言标准,main函数的返回值可以是0或者非0的整数。

返回值为0通常表示程序执行成功,而非0的返回值则表示程序执行失败或出现了错误。

我们可以根据main函数的返回值来判断程序的执行结果,从而进行相应的处理。

比如,在shell脚本中,可以根据程序的返回值来判断是否执行成功。

main函数可以调用其他函数来完成特定的任务。

在C语言中,我们可以在main函数内部调用其他函数,这些函数可以是由我们自己定义的,也可以是系统提供的函数库中的函数。

通过调用其他函数,我们可以将程序的功能模块化,提高代码的可读性和可维护性。

例如,我们可以将一些复杂的计算逻辑封装成一个函数,然后在main函数中调用这个函数来完成相应的计算。

main函数还可以接收命令行参数。

在命令行中执行程序时,可以给程序传递一些参数,这些参数可以在main函数中进行处理。

命令行参数通常以字符串的形式传递给main函数,我们可以使用argc 和argv两个参数来接收这些参数。

其中,argc表示命令行参数的个数,argv是一个指针数组,每个元素指向一个命令行参数的字符串。

c语言main函数

c语言main函数

c语言main函数C语言是一门广泛使用的编程语言,它的主要特点是简单易学、功能强大、易于移植等等。

而在C语言中,main函数则是最为重要的部分之一。

本文将详细介绍C语言中的main函数,包括其定义、作用、参数、返回值等等。

一、main函数的定义在C语言中,main函数是程序的入口点,也就是程序执行的第一个函数。

它的定义方式如下:```int main(int argc, char *argv[]){// 函数体return 0;}```其中,int表示函数返回值的类型,main表示函数名,括号中的两个参数分别是整型变量argc和字符型指针变量argv[]。

大括号内是函数的具体实现。

二、main函数的作用main函数是程序的起点,它的作用是为程序提供一个执行的入口。

当程序运行时,操作系统会调用main函数,并执行其中的代码。

因此,main函数的作用是初始化程序,分配内存空间,读取输入数据,处理逻辑等等。

三、main函数的参数在main函数中,argc和argv[]是两个重要的参数。

它们分别表示命令行参数的个数和具体的参数列表。

argc是一个整型变量,用于记录命令行参数的个数。

例如,如果在命令行中输入了“./program arg1 arg2 arg3”,那么argc的值就为4,因为除了程序名之外,还有三个参数。

argv[]是一个字符型指针数组,用于存储具体的命令行参数。

例如,如果在命令行中输入了“./program arg1 arg2 arg3”,那么argv[0]的值就是程序名“./program”,argv[1]的值是“arg1”,argv[2]的值是“arg2”,argv[3]的值是“arg3”。

四、main函数的返回值在C语言中,main函数的返回值是一个整型值。

它表示程序的执行结果,通常有以下两种情况:1. 返回0:表示程序执行成功,没有错误发生。

2. 返回非0值:表示程序执行失败,发生了某些错误。

dlmain使用技巧

dlmain使用技巧

dlmain使用技巧在进行编程开发时,我们经常会遇到需要使用main函数的情况。

main函数是每个C/C++程序的入口函数,它是程序开始执行的地方。

下面是一些使用main函数的技巧。

1. 程序的执行逻辑:在编写程序时,我们需要考虑程序的执行逻辑。

我们可以使用多个函数实现不同的功能,然后在main函数中按照顺序调用这些函数。

这样可以使程序结构清晰,易于阅读和维护。

2. 命令行参数:main函数可以接受命令行参数。

通过命令行参数,我们可以在程序运行时向程序传递一些参数。

在main函数的参数列表中,argc表示命令行参数的个数,而argv是一个字符串数组,其中存储了这些参数的值。

利用这些参数,我们可以实现程序的不同功能和配置。

3. 程序的返回值:main函数可以有返回值,它可以用来告诉操作系统程序的执行结果。

一般来说,返回0表示程序正常退出,而非零值表示程序出现了错误或异常情况。

通过返回值,可以方便地在脚本或其他编程语言中调用和处理C/C++程序。

4. 调试技巧:在调试程序时,我们经常会使用断点来暂停程序的执行,然后逐步跟踪代码的执行过程。

在main函数中设置断点,可以方便地查看程序执行到哪个位置,变量的值是多少,以及函数的调用顺序等。

这样可以快速定位和解决程序的问题。

5. 程序的异常处理:在程序出现异常情况时,我们可以使用try-catch语句来捕获和处理异常。

将主要的程序逻辑放在try块中,当程序抛出异常时,可以在catch块中进行处理。

这样可以保证程序的健壮性和可靠性,避免程序崩溃或产生不确定的行为。

6. 程序的多线程处理:在程序需要处理大量数据或并行计算时,我们可以使用多线程来提高程序的性能和效率。

在main函数中创建多个线程,每个线程负责一部分任务,然后通过线程间的通信来实现数据共享和同步。

这样可以有效地利用计算资源,加速程序的运行。

7. 程序的模块化设计:在大型项目中,我们通常会使用模块化设计来组织代码。

C++main函数与命令行参数,退出程序

C++main函数与命令行参数,退出程序

C++main函数与命令⾏参数,退出程序 (除动态链接库dll,静态链接库lib⼯程外)所有的C++程序都必须有⼀个main函数。

如果你编译⼀个没有main函数的C++exe⼯程,编译器会提⽰错误。

main函数是你的代码开始执⾏的地⽅,但在main函数调⽤前,所有的没有被显⽰初始化的static类成员都被设置为零。

在微软C++中,调⽤main函数前全局静态对象也会被初始化。

main函数相对于其他函数⽽⾔有以下不同之处:1. 不能被重载2. 不能被声明为inline,内联函数3. 不能被声明为static4. 不能获取它的地址,Cannot have its address taken5. 不能被调⽤ main函数并没有声明,因为它是语⾔内置的。

如果有的话,其形式如下所⽰:int main();int main(int argc, char *argv[], char *envp[]);Microsoft Specific 如果你的代码⽂件使⽤的是Unicode wide character,你可以使⽤wmain,是main函数的宽字符版本,其声明如下:int wmain( );int wmain(int argc, wchar_t *argv[], wchar_t *envp[]); 你还可以使⽤_tmain,该函数定义在tchar.h。

_tmain被解释为main,除⾮你定义了_UNICODE(在这种情况下,_tmain被解释为wmain)。

如果main函数的返回值没有指定,编译器会提供⼀个值为零的返回值。

另外,main wmain可以被声明为返回为void(没有返回值)。

如果你将main wmain声明为void,那么你就不能通过return语句将exit code返回给程序的⽗进程或系统。

在main wmain是void的时候,你需要使⽤the exit function来返回exit code。

main函数参数解析

main函数参数解析

main函数参数解析int argc,char *argvagrc表⽰参数的个数argv储存参数这个函数的意思是逐⼀输出参数实际上,main函数也可以带参数。

带参数main函数的定义格式如下:void main(int argc, char *argv[]){... ...}argc和argv是main函数的形式参数。

这两个形式参数的类型是系统规定的。

如果main函数要带参数,就是这两个类型的参数;否则main函数就没有参数。

变量名称argc和argv是常规的名称,当然也可以换成其他名称。

那么,实际参数是如何传递给main函数的argc和argv的呢?我们知道,C程序在编译和链接后,都⽣成⼀个exe⽂件,执⾏该exe⽂件时,可以直接执⾏;也可以在命令⾏下带参数执⾏,命令⾏执⾏的形式为:可执⾏⽂件名称参数1 参数2 ... ... 参数n可执⾏⽂件名称和参数、参数之间均使⽤空格隔开。

例如,我们在DOS下运⾏copy c:\test.txt d:\test.txt,可执⾏⽂件名称为copy,参数1为字符串“c:\test.txt”,参数2为“d:\test.txt”。

结果copy命令将c:\test.txt拷贝到d盘,⽬标⽂件取为test.txt。

如果按照这种⽅法执⾏,命令⾏字符串将作为实际参数传递给main函数。

具体为:(1) 可执⾏⽂件名称和所有参数的个数之和传递给argc;(2) 可执⾏⽂件名称(包括路径名称)作为⼀个字符串,⾸地址被赋给argv[0],参数1也作为⼀个字符串,⾸地址被赋给argv[1],... ...依次类推。

例如,现在运⾏命令⾏(test是编译后的exe⽂件名称):C:\TC\test how are you那么test⼯程的main函数参数argc=4;argv[0]将保存字符串"C:\TC\test"的⾸地址;argv[1]将保存字符串"how"的⾸地址;argv[2]将保存字符串"are"的⾸地址;argv[3]将保存字符串"you"的⾸地址;下⾯的例⼦打印所有的argv参数:main(int argc, char *argv[]){int i;printf("\nTotal %d arguments",argc);for(i=0;i<argc;i++){printf("\nArgument %d = %s ",i+1, argv[i]);}}如果编译后的exe⽂件名称为test,在DOS下运⾏C:\TC\test how are you,结果输出:Total 4 argumentsArgument 1 = C:\TC\test.exeArgument 2 = howArgument 3 = areArgument 4 = you我们也可以在调试状态下输⼊命令⾏参数,⽅法是:在TurboC的Options菜单下有⼀个⼦菜单Arguments,选择该项并确定,弹出输⼊窗⼝;在输⼊窗⼝键⼊命令⾏参数即可。

C51–在main__之前到底做了什么

C51–在main__之前到底做了什么

1C51–在main()之前到底做了什么??用C 写过东西的都知道,程序是从main()函数开始执行的,在main()之前会有一系列的初始化工作,那么这些初始化到底做了些什么了??大家也都知道在MCU 中,程序是在Flash 里面,而数据是在RAM 里面,掉电之后,Flash 里面的东西还在,RAM 里面的东西就没有了.那么你把程序下载到芯片里面之后,数据是在哪里了?你会想,它当然是在Flash 里面的.那数据又是在什么时候跑到RAM 里面了呢?结合C51生成的M51文件,大概的说一下main()函数之前,到底都做了哪些初始化的工作.程序编译(assembly,compile),连接(link)过后会生成HEX 文件.整个HEX 文件是由程序和数据(主要是global 和static 的数据)组成的.当然程序和数据会放在不同的地址,这些地址是绝对的地址.绝对的地址是在连接(link)过程中生成的.如果是C51的话,何以看一下他的M51文件.M51文件就是link 的MAP 文件. 例如:MCU 是C8051F340,IDE 是uVersion4,C51是v9.01,main()函数如下://main.cunsigned int a = 0;unsigned int b = 0xff;void main(void){unsigned int c;unsigned int d;d = c;while(1);} build output 的信息是:Build target ‘Target 1′assembling STARTUP.A51…compiling main.c…linking…Program Size: data=17.0 xdata=0 code=160“test” – 0 Error(s), 0 Warning(s).可以看到,data用里17Byte,code是160Byte,没有用到xdata.查看M51文件:BL51 BANKED LINKER/LOCATER V6.22 04/25/2010 11:27:23 PAGE 1BL51 BANKED LINKER/LOCATER V6.22, INVOKED BY:D:\PROGRAM FILES\KEIL\C51\BIN\BL51.EXE STARTUP.obj, main.obj TO test RAMSIZE (256)MEMORY MODEL: SMALLINPUT MODULES INCLUDED:STARTUP.obj (?C_STARTUP)main.obj (MAIN)D:\PROGRAM FILES\KEIL\C51\LIB\C51S.LIB (?C_INIT)LINK MAP OF MODULE: test (?C_STARTUP)TYPE BASE LENGTH RELOCATION SEGMENT NAME—————————————————–* * * * * * * D A T A M E M O R Y * * * * * * *REG 0000H 0008H ABSOLUTE “REG BANK 0″DATA 0008H 0004H UNIT ?DT?MAINDATA 000CH 0004H UNIT _DATA_GROUP_IDATA 0010H 0001H UNIT ?STACK* * * * * * * C O D E M E M O R Y * * * * * * *CODE 0000H 0003H ABSOLUTECODE 0003H 008CH UNIT ?C_C51STARTUPCODE 008FH 0009H UNIT ?C_INITSEGCODE 0098H 0008H UNIT ?PR?MAIN?MAIN说一下里面的意义,首先是DATA MEMORY,REG是特殊寄存器(SFR)R0-R7,SFR是映射到RAM上的.BASE是0000H,LENGTH是0008H.后面是DATA,从0008H开始,LENGTH是0004H,TYPE是UINT,这个是全局变量a,b在RAM中的位置.2后面是_DATA_GROUP_,是存放临时变量时用的,_DATA_GROUP_在C51中解释的原话是“The _DATA_GROUP_ is a segment that contains all the automatic variables and function arguments from your program that are stored in DATA memory.These segments are created so that the linker can perform overlay analysis and better optimize your use of DATA and BIT memory.”上面的意思是说_DATA_GROUP_好像是执行overlay analysis的.我现在还不清楚的是它的具体作用,还有他和SP指向的栈有什么区别?我记得好像是函数的局部变量是放在栈里面的,函数退出的时候,会执行POP命令.因为我在main()里面声明了两个局部变量,这两个变量是放在_DATA_GROUP_里面的吗??后面的IDATA是堆栈的位置.堆栈在RAM中的0×0010的地方,但是LENGTH只有0001H,不知道为什么是这样的?CODE MEMORY中开始是0000H,也是绝对地址0,一上电,PC指针指向的地方,这个地方肯定有一个LJMP,跳转到STARTIP1这个标号的位置,这个指令占三个字节.后面的是C_C51STARTUP段,这个段就是startup.a51中的内容,0003H也就是STARTUP1在Flash中的位置,程序上电后就跳到这个地方,主要的作用是初始化内存,把RAM中00H-7FH全部清零.后面的是C_INITSEG段,看一下?C_INITSEG原话解释:The initial values of global and static variables are stored in ROM in a segment called ?C_INITSEG. They are then copied to the relevent RAM locations after the code in STARTUP.A51 has been executed and before the main() function is called. The code that performs the variable initialization may be found in the file INIT.A51 inside the LIB folder.从中知道就是把global and static variables 在ROM中的段就是?C_INITSEG段.那么这个段中的数据时怎么到RAM中区的呢?把这个段中的数据copy到RAM中去,看?C_INITSEG解释的最后一句,使用的文件是INIT.A51,但是现在好像不是显式的添加这个文件,而是用的库文件,可以在M51的头部看到,INPUT MODULES INCLUDED:STARTUP.obj (?C_STARTUP)main.obj (MAIN)D:\PROGRAM FILES\KEIL\C51\LIB\C51S.LIB (?C_INIT)也就是说在STARTUP.A51之后,在main()之前,执行的是INIT.A51的程序,也就是把ROM中的数据copy到RAM中区.这个之后,main()函数之前的所有准备工作也就做完了.可以去执行main()函数了.小结:以前一直没有搞清楚这个程序到底是怎么运行的,隐约的觉得数据一开始是在Flash中的,但是什么时候跑到RAM中去了呢??现在基本搞清楚了.下面想仔细研究一下在main()函数之前执行的程序究竟是什么意思.主要也就是STARTUP.A51和INIT.A51.结合A51和C51的datasheet看.3。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

图 2-10 MAP 文件分析 0x0800 015D 地址是函数_scatterload_copy 的入口,该函数到底 copy 了什么 值呢?在此之前我们先要熟悉一下.map 文件 .map 文件是值包括了映像文件信息图和其它信息的一个映射文件, 该文件包 含了: (1) 从映像文件中删除的输入段中未使用段的统计信息,对应参数-remove; (2) 域符号映射和全局、局部符号及生成符号映射统计信息,对应参数 -symbol; (3) 映射文件的信息图,对应参数-map,该信息中包含映像文件中的每个加载 域、运行域和输入段的大小和地址,如 2-11 图、2-12 图所示:
图 2-19
经 过 该 函 数 处 理 得 : r0=0x2000 0098,r1=0x2000 0698,r2=0x2000 0298,r3=0x2000 0298。最后用户栈顶被设置成 0x2000 0698,完成了堆栈的初始 化工作,程序返回到 rt_entry_main
三、 总结
最终函数终于跳转到我们的 main 函数执行我们写的代码。 总结启动文件的整个过程,分为如下: (1) 系统初始化,包括对中断向量表的重新映射; (2) 加载 RW 段; (3) ZI 段清零; (4) 初始化用户堆栈; (5) 初始化微库(具体干什么我也不知道,屏蔽此处函数好像也能正常运行) ; (6) 调用 main 函数。
Main 函数运行前的分析(原创,转载请注明出处)
一、 启动文件的介绍
在 MDK 的启动文件 startup_stm32f10x_md_vl 中,该文件分别定义了栈段、 堆段、存放中断向量表的数据段、还有一个代码段 大小为 0x400 的栈段定义如图 1-1:
图 1-1 大小为 0x200 的堆段如图 1-2:
图 2-15
_scatterload_copy 函数分析 经过_scatter_null 函数我们得到 r2=0x00000034, 这正是需要初始化全局变量 的大小,由此我们猜测 copy 的值是全局变量。 第一条指令是得 r2=0x0000 0024,然后判断标志位 C 是否为 1,如果是 1,则 执行下面两条语句: LDM r0! , (r3-r6); STM r1! , (r3-r6); r0=0x0800 2040, r1=0x2000 0000,而 0x20000000 正好是 SRAM 的起始地址, 所以 上面两条语句是把以 0x8002040 为起始的地址复制到以 0x2000 0000 为起始的地 址,该循环类似我们的 for(r0=0x0800 2040, r1=0x2000 0000;r2=r2-0x10;r2>0), 直到 r2 为负数 LSLS r2,r2,#29 ITT CS 如果 r2 除以 16 是 8 的整数,则复制该 8 字节 LDM r0!,{r4-r5} STM r1!,{r4-r5} ITT MI 如果 r2 除以 16 是 4 的整数,则复制 4 或者 12 字节 LDR r4,[r0,#0x00] STR r4,[r1,#0x00] 这样_scatterload_copy 完成了需要初始化全局变量 RW 的装载过程,最后函 数返回到_scatterload_null。 _scatterload_zeroinit 函数分析 然后判断 r10 是否与 r11 相等,很明显不等,r3=0x0800 0178,函数跳转,进入 到_scatterload_zeroinit 函数,此时 r0=0x0800 2074,r1=0x20000034, r2=0x0000 0664 _scatterload_zeroinit 函数代码如图 2-16:
图 1-2 由其定义属性可知,栈和堆都未初始化,该过程由后面的_user_initial_stackheap 来完成。 存放中断向量的数据段,如图 1-3 所示:
图 1-3 10 个系统异常过程段和在同一地址的外部中断过程段, 下面我们就详细介绍 上电复位的代码段,如图 1-4 所示:
图 1-4
二、Reset_Handler 段分析
图 2-4 该函数首先保存跳转前的有关状态,然后根据使用的芯片,进行相应的初始化操 作,函数最后重新映射了中断向量的存放地址,如图 2-5:
图 2-5
查看反汇编窗口,如图 2-6:
图 2-6 根据分析可知,该段代码是把 0x0800 0000 存放到地址为 0xE000 ED08 处, 查找 Cortex_M3 手册如图 2-7 所示,该地址是向量表偏移寄存器,也就是说,这 条语句把中断向量表重新映射到地址为 0x0800 0000。
图 2-11
图 2-12 (4) 映像文件的每个输入文件或库的 RO、 RW、 ZI 等统计信息, 对应参数-info sizes,示例如图 2-13:
图 2-13 (5) 文件对象类和库总的 RO、RW、ZI 等下大小,对应参数-info totals,示例 如图 2-14:
图 2-14 由此,根据.map 文件我们得到 RW=0x34, ZI=0x0644, Code+RO=0x2040, RW+ZI=0x0698 现在我们回到_scatterload_copy 中,看看到底 copy 了什么,其代码如图 2-15 所示:
图 2-18 _user_setup_stackheap 函数的第一条指令是保存函数的返回地址,此处为 什么没有用 PUSH ?因为此时堆栈还没有初始化好。第二条指令是跳转到 _user_libspace 进行一些微库的初始化工作,后面的几条语句是建立一个大小为 90 字节的临时栈,然后程序跳转到_user_inital_stackheap 进行用户栈的初始化, 这也就是启动文件 startup_stm32f10x_md_vl 中初始化堆栈段的那些语句,如图 2-19
1. _systeminit 函数分析
STM32 上电启动, 首先从 0x0000 0000 处初始化 sp 的值, 然后从 0x0000 0004 处取得复位中断处理的地址 0x0800 1F6D,程序跳转
图 2-1 但是 Reset_Handler 的地址为 0x0800 1F6C, 这是因为 Cortex-M3 使用的是 thumb-2 指令集,其最低位必须为 1;如果为 0,则会出现异常,如图 2-1 所示。 查看反汇编窗口,如图 2-2 所示:
图 2-9 查看.map 文件,知道 0x08002020 称为 Region$$Table$$Base,0x08002040 称为 Region$$Table$$Limit。 _scatterload_null 函数分析 如图 2-10 所示,程序继续执行到_scatterload_null 函数,首先比较 r10、r11 是否相等,如果不等则跳转到 0x0800013E。很明显不等,程序跳转,第一条指 令是把 0x08000137f 赋值给 lr,其实就是保存_scatterload_null 的入口地址;第二 条指令是把 r10=0x08002020 为起始,0x08002030 为终止的地址内容分别赋值给 r0-r3,最后我们得到 r0=0x08002040, r1=0x20000000, r2=0x00000034, r3=0x0800 015C,r10=0x08002030;第三条指令是判断 r3 是否为 1,很明显不为 1,IT 指令 等效于 if-then 模式,最后几条指令可以得到 r3=0x0800 015D,程序跳转
程序最后返回到_scatterload,接着跳转到_rt_entry,如图 2-17
图 2-17 2. _rt_entry 函数分析 _rt_entry 的 第 一 条 指 令 又 是 一 条 跳 转 指 令 , 程 序 再 次 跳 转 到 _user_setup_stackheap,如图 2-18
图 2-7
2. _main 函数分析
进行完相应的初始化,函数跳转到_main 函数, _main 函数的入口地址从 0x08001F98 取得,通过查找,发现 _main 函数的地址为 0x08000121, 跳转到 0x0800120,证明此处就是我们要找的_main 函数入口,如图 2-8
图 2-8 _main 函数到底进行了哪些操作呢,下面我们就一一逐步分析过去。 _scatterload 函数分析 首先是一条跳转指令,跳转到_scatterload 函数,该函数的第一条指令是把地 址 0x08000154 赋值给 r0(但是此处 PC+4 取得值应该是 0x0800012C,为什么会 是 0x08000154 呢?根据 ARMv7-M Architecture Reference Manual,ADR 的二进 制码, 相差 0x28) , 第二条指令是分别把 0x08000154、 0x08000158 存放的 0x0000 1ECC、0x0000 1EEC 给 r10、r11,如图 2-9。经过该函数的第三、第四条指令, 我们可以得到 r10=0x08002020,r11=0x08002040,r7=0x08uanzhipingysu2011@,谢谢!
图 2-16 通过_scatterload_copy 我们可以猜测_scatterload_zeroinit 是一个清零过程, 但 是对什么需要清零呢?当然是 ZI 段,由 r2=0x00000664 这正是 ZI 的大小,所以 该过程是以 0x20000034 为起始地址,大小为 r2=0x00000664 的清零过程,具体 分析和_scatterload_copy 类似,不再重复。
图 2-2 可以知道先取得 SystemInit 函数的地址,该地址是多少,我们可以跳转到 0x08001F94 看看,如下所示,该地址保存了值为 0x0800045D 的数,如图 2-3:
图 2-3 然后我们跳转到 0x0800045D,发现该处正是我们需要的 SystemInit 函数入口地 址,如图 2-4:
相关文档
最新文档