ARM Cortex-M/R 内核的复位启动过程也被称为复位序列(Reset sequence)。ARM Cortex-M/R内核的复位启动过程与其他大部分CPU不同,也与之前的ARM架构(ARM920T、ARM7TDMI等)不相同。大部分CPU复位后都是从 0x00000000 处取得第一条指令开始运行的,然而在ARM Cortex-M/R内核中并不是这样的。其复位序列为:
注意:中断向量表的位置可以改变,此处是默认情况下的设置。
在 ARM Cortex-M/R 内核中,发生异常后,并不是去执行中断向量表中对应位置处的代码,而是将对应位置处的数据存入PC中,然后去此地址处进行取指。简而言之,在ARM Cortex-M/R的中断向量表中不应该放置跳转指令,而是该放置 ISR 程序的入口地址。另外还有两个细节问题需要注意:
第一条是因为在ARM上编程,但凡涉及到调用,就需要遵循一套规约AAPCS ——《Procedure Call Standard for the ARM Architecture》。AAPCS 中对栈使用的约定是这样的:
5.2.1.1
Universal stack constraints
At all times the following basic constraints must hold:
Stack-limit < SP <= stack-base. The stack pointer must lie within the extent of the stack.
SP mod 4 = 0. The stack must at all times be aligned to a word boundary.
5.2.1.2
Stack constraints at a public interface
The stack must also conform to the following constraint at a public interface:
SP mod 8 = 0. The stack must be double-word aligned.
简而言之,规约规定栈任何时候都必须 4 字节对齐,在调用入口需8字节对齐,而且 SP 的最低两位在硬件上就被置为 0 了。
第二条与 ARM 模式与 Thumb 模式有关。ARM 中 PC 中的地址必须是 32 位对齐的,其最低两位也被硬件上置 0 了,故写入PC 中的数据最低两位并不代表真实的取址地址。ARM中使用最低一位来判断这条指令是 ARM 指令还是 Thumb 指令,若最低位为 0,代表 ARM 指令;若最低位为 1,代表 Thumb 指令。在 Cortex-M/R 内核中,并不支持 ARM 模式,若强行切换到 ARM 模式会引发一个 Hard Fault。
嵌入式应用程序在用户定义的 main() 函数启动之前需要初始化序列。 这称为启动代码或启动代码。 ARM C 库包含启动应用程序所必需的预编译和预组装代码段。链接应用程序时,链接器会根据应用程序从 C 库中包含必要的代码,以便为应用程序创建自定义启动代码。但需要注意的是,ARM 自己的编译套件提供了如下三种库:
根据ARM官方的文档说明,以下分析描述的启动代码适用于标准 ARM C 库。 它不适用于 ARM C 微库。 启动流程同样适用于 ARMv4T 及更高版本的架构。如下图清晰的表示了程序启动到用户的main() 之前的流程:
函数__main
是C库的入口点。 除非您更改它,否则__main
是ARM链接器(armlink)在创建映像时使用的ELF映像的缺省入口点。 下图显示了C库启动期间__main
调用的函数。
Application code and data can be in a root region or a non-root region. Root regions have the same load-time and execution-time addresses. Non-root regions have different load-time and execution-time addresses. The root region contains a region table output by the ARM linker.应用程序代码和数据可以位于根区域或非根区域中。 根区域具有相同的加载时间和执行时间地址。 非根区域具有不同的加载时间和执行时间地址。 根区域包含ARM链接器输出的区域表。
The region table contains the addresses of the non-root code and data regions that require initialization. The region table also contains a function pointer that indicates what initialization is needed for the region, for example a copying, zeroing, or decompressing function. region表包含需要初始化的非根代码和数据区域的地址。 区域表还包含一个函数指针,指示区域需要初始化,例如复制,归零或解压缩功能。
__scatterload
遍历区域表并初始化各种执行时区域。 功能如下:
__main
always calls this function during startup before calling __rt_entry
.
__main
调用__rt_entry
来初始化堆栈,堆和其他C库子系统。__rt_entry
调用各种初始化函数,然后调用用户级main()
。以下列出了_rt_entry可以调用的函数。 这些函数按它们被调用的顺序列出:
platform *函数不是标准C库的一部分。 如果你定义它们,则链接器会在__rt_entry中对它们进行调用。
main()
是用户级应用程序的入口点。 寄存器r0
和r1
包含main()
的参数。 如果main()
返回,则将其返回值传递给exit()
并退出应用程序。
__rt_entry
还负责设置堆栈和堆。 但是,设置堆栈和堆取决于用户指定的方法。 可以通过以下任何方法设置堆栈和堆:
__user_setup_stackheap
。 这也获得了堆使用的内存边界(堆顶部和堆基)。__initial_sp
的值加载 SP。__rt_entry and __rt_lib_init do not exist as complete functions in the C library. Small sections of these functions are present in several internal objects that are part of the C library. Not all of these code sections are useful for a given user application. The linker decides which subset of those code sections are needed for a given application, and includes just those sections in the startup code. The linker places these sections in the correct order to create custom __rt_entry and __rt_lib_init functions as required by the user application.__rt_entry和__rt_lib_init在C库中不作为完整函数存在。这些函数的小部分存在于作为类库一部分的几个内部对象中。并非所有这些代码段都对给定的用户应用程序有用。链接器决定给定应用程序需要这些代码段的哪个子集,并且只在启动代码中包含这些部分。链接器按照正确的顺序放置这些部分,以便根据用户应用程序的要求创建自定义的__rt_entry和__rt_lib_init函数。
The standard C library does not provide this function but you can define it if you require it. You can use this function to setup hardware for example. __rt_entry calls this function, if you define it, before the code that initializes the stack and heap. 标准C库不提供此函数,但您可以根据需要定义它。 例如,您可以使用此函数设置硬件。如果您定义了该函数,那么`__rt_entry会在初始化堆栈和堆的代码之前调用此函数。
This function enables you to setup and return the location of the initial stack and heap. The C library does not provide this function but you can define it if you require it. __rt_entry calls this function if you define it or if you define the legacy function __user_initial_stackheap . If you define __user_initial_stackheap , then the C library provides a default __user_setup_stackheap as a wrapper around your __user_initial_stackheap function. 此函数使您可以设置并返回初始堆栈和堆的位置。C库不提供此函数,但您可以根据需要定义它。 如果你定义了该函数或者定义了老版本的函数__user_initial_stackheap
,那么__rt_entry
会调用此函数。 如果定义了__user_initial_stackheap
,则C库提供默认的__user_setup_stackheap
作为__user_initial_stackheap
函数的包装器。
The C library does not provide this function but you can define it if you require it. You can use this function to setup hardware for example. __rt_entry calls this function, if you define it, after the code that initializes the stack and heap. C库不提供此功能,但您可以根据需要定义它。 例如,您可以使用此功能设置硬件。 如果您定义了该函数,那么__rt_entry
会在初始化堆栈和堆的代码之后调用此函数。
This function initializes the various C library subsystems. It initializes the referenced library functions, initializes the locale and, if necessary, sets up argc and argv for main() . __rt_entry calls this function always during startup.此函数初始化各种C库子系统。 它初始化引用的库函数,初始化语言环境,并在必要时为main()设置 argc 和 argv。 __rt_entry在启动期间始终调用此函数。
If you use the __user_setup_stackheap or __user_initial_stackheap functions to setup the stack pointer and heap, then the start and end address of the heap memory block are passed as arguments to __rt_lib_init in registers r0 and r1 respectively.如果使用了函数__user_setup_stackheap 或函数 __user_initial_stackheap来设置堆栈指针和堆,那么堆内存块的起始和结束地址将作为参数通过寄存器 r0 和 r1传递给__rt_lib_init。
The function returns argc and argv in registers r0 and r1 respectively if the user-level main() requires them. 如果用户级main()需要,该函数分别在寄存器 r0 和 r1 中返回argc和argv。
The linker includes various initialization code sections from the internal object files to create a custom __rt_lib_int function. The linker places a function in __rt_lib_init only if it is needed by the application. This lists the functions that _rt_lib_init can call. The functions are listed in the order they get called:链接器包括内部对象文件中的各种初始化代码部分,以创建自定义__rt_lib_int函数。 只有在应用程序需要时,链接器才会在__rt_lib_init中放置一个函数。 以下列出了_rt_lib_init可以调用的函数。 这些函数按它们被调用的顺序列出:
这里就不一一介绍各函数了,想进一步了解的去查看官方文档即可!
The C library does not provide this function but you can define it if you require it. You can use this function to setup hardware for example. __rt_entry calls this function, if you define it, after the call to __rt_lib_init and before the call to the user-level main() function. C库不提供此功能,但您可以根据需要定义它。 例如,您可以使用此功能设置硬件。 如果定义了该函数,那么__rt_entry在调用__rt_lib_init之后和调用用户级main()函数之前,调用此函数。
上面介绍了各函数,这些函数全部位于 ARM 提供的 C 库中。我们可以参看任意项目的map文件,来看看都用了哪些库,如下:
其中,左侧的图为 Image Symbol Table 部分;右侧图为 Image component sizes 部分。从中可以看到:
其他函数同理,这里暂不说明。在右侧可以看到,对应的 C 库文件为c_w.l
和fz_wm.l
。我们可以在 ARM 编译套件的目录下找到这两个文件,路径如下图所示:
下面我们使用 ARM 编译套件中相应的工具来看看具体文件。关于编译套件的详细使用说明可以参考博文《ARM 之 主流编译器(armcc、iar、gcc for arm)详细介绍》。具体使用的工具就是armar.exe
,这是 ARM 的库文件管理工具。
D:\ARM\ARM_Compiler_5.06u4>armar --zt ./lib/armlib/c_w.l
Code RO Data RW Data ZI Data Debug Object Name
48 0 0 0 84 version.o
58 0 0 0 68 __dczerorl.o
90 0 0 0 68 __dczerorl2.o
100 0 0 0 68 __dclz77c.o
0 0 28 0 0 dc.o
102 12 0 0 240 sys_io.o
18 0 0 0 76 sys_tmpnam.o
14 0 0 0 76 sys_wrch.o
18 0 0 0 76 sys_system.o
18 0 0 0 76 sys_remove.o
32 0 0 0 80 sys_rename.o
24 0 0 0 76 sys_command.o
4 0 0 0 68 getenv.o
32 0 4 0 84 sys_clock.o
18 0 0 0 76 sys_time.o
12 0 0 0 68 sys_exit.o
8 0 0 96 68 libspace.o
2 0 0 0 68 use_semi.o
2 0 0 0 68 use_no_semi.o
4 0 0 0 68 mutex_dummy.o
2 0 0 0 68 use_no_semi_2.o
0 0 0 0 0 indicate_semi.o
48 0 0 0 80 sys_stackheap.o
74 0 0 0 80 sys_stackheap_outer.o
96 0 0 0 0 tempstk.o
0 0 0 0 0 __rtentry.o
32 40 0 0 0 __rtentry2.o
4 8 0 0 0 __rtentry3.o
6 8 0 0 0 __rtentry4.o
8 24 0 0 0 __rtentry5.o
8 24 0 0 0 __rtentry6.o
............省略一大部分...........
0 0 0 0 0 maybefptrapshutdown1.o
0 0 0 0 0 maybefptrapshutdown2.o
16 0 0 0 76 fptrapshutdown.o
8 0 4 0 68 _signgam.o
22 0 0 0 100 _rserrno.o
18 0 0 0 76 isalnum.o
18 0 0 0 76 isalpha.o
18 0 0 0 76 iscntrl.o
24 0 0 0 76 isdigit.o
18 0 0 0 76 isgraph.o
18 0 0 0 76 islower.o
18 0 0 0 76 isprint.o
18 0 0 0 76 ispunct.o
18 0 0 0 76 isspace.o
18 0 0 0 76 isupper.o
22 0 0 0 76 isxdigit.o
26 0 0 0 76 tolower.o
30 0 0 0 76 toupper.o
172 4490 0 0 164 wclass.o
12 0 0 0 76 iswalnum.o
12 0 0 0 76 iswalpha.o
12 0 0 0 76 iswblank.o
12 0 0 0 76 iswcntrl.o
12 0 0 0 76 iswgraph.o
12 0 0 0 76 iswprint.o
12 0 0 0 76 iswpunct.o
4 0 0 0 68 iswspace.o
12 0 0 0 76 iswlower.o
12 0 0 0 76 iswupper.o
12 0 0 0 76 iswdigit.o
12 0 0 0 76 iswxdigit.o
148 888 0 0 152 towlower.o
156 948 0 0 152 towupper.o
356 0 0 0 92 wctype.o
64 0 0 0 92 wctrans.o
12 0 0 0 76 _iswspace.o
86 0 0 0 136 _small_iswspace.o
100 0 0 0 76 wclass_c.o
10 0 0 0 68 towlower_c.o
10 0 0 0 68 towupper_c.o
32 0 0 0 76 isblank.o
24 0 0 0 84 printf.o
12 0 0 0 68 vprintf.o
20 0 0 0 84 fprintf.o
10 0 0 0 68 vfprintf.o
44 0 0 0 84 sprintf.o
36 0 0 0 76 vsprintf.o
56 0 0 0 88 snprintf.o
52 0 0 0 80 vsnprintf.o
24 0 0 0 84 wprintf.o
12 0 0 0 68 vwprintf.o
20 0 0 0 84 fwprintf.o
10 0 0 0 68 vfwprintf.o
72 0 0 0 92 swprintf.o
72 0 0 0 80 vswprintf.o
54 0 0 0 92 asprintf.o
56 0 0 0 96 vasprintf.o
54 0 0 0 92 __ARM_asprintf.o
56 0 0 0 96 __ARM_vasprintf.o
56 0 0 0 88 __ARM_snprintf.o
52 0 0 0 80 __ARM_vsnprintf.o
24 0 0 0 84 _printf.o
12 0 0 0 68 _vprintf.o
20 0 0 0 84 _fprintf.o
10 0 0 0 68 _vfprintf.o
44 0 0 0 84 _sprintf.o
36 0 0 0 76 _vsprintf.o
56 0 0 0 88 _snprintf.o
52 0 0 0 80 _vsnprintf.o
24 0 0 0 84 _wprintf.o
12 0 0 0 68 _vwprintf.o
20 0 0 0 84 _fwprintf.o
10 0 0 0 68 _vfwprintf.o
72 0 0 0 92 _swprintf.o
72 0 0 0 80 _vswprintf.o
54 0 0 0 92 _asprintf.o
56 0 0 0 96 _vasprintf.o
54 0 0 0 92 ___ARM_asprintf.o
56 0 0 0 96 ___ARM_vasprintf.o
56 0 0 0 88 ___ARM_snprintf.o
52 0 0 0 80 ___ARM_vsnprintf.o
24 0 0 0 84 __0printf.o
12 0 0 0 68 __0vprintf.o
20 0 0 0 84 __0fprintf.o
10 0 0 0 68 __0vfprintf.o
44 0 0 0 84 __0sprintf.o
36 0 0 0 76 __0vsprintf.o
56 0 0 0 88 __0snprintf.o
52 0 0 0 80 __0vsnprintf.o
24 0 0 0 84 __0wprintf.o
12 0 0 0 68 __0vwprintf.o
20 0 0 0 84 __0fwprintf.o
10 0 0 0 68 __0vfwprintf.o
72 0 0 0 92 __0swprintf.o
72 0 0 0 80 __0vswprintf.o
54 0 0 0 92 __0asprintf.o
56 0 0 0 96 __0vasprintf.o
54 0 0 0 92 __0__ARM_asprintf.o
56 0 0 0 96 __0__ARM_vasprintf.o
56 0 0 0 88 __0__ARM_snprintf.o
52 0 0 0 80 __0__ARM_vsnprintf.o
24 0 0 0 84 c89printf.o
12 0 0 0 68 c89vprintf.o
20 0 0 0 84 c89fprintf.o
10 0 0 0 68 c89vfprintf.o
44 0 0 0 84 c89sprintf.o
36 0 0 0 76 c89vsprintf.o
56 0 0 0 88 c89snprintf.o
52 0 0 0 80 c89vsnprintf.o
24 0 0 0 84 c89wprintf.o
12 0 0 0 68 c89vwprintf.o
20 0 0 0 84 c89fwprintf.o
10 0 0 0 68 c89vfwprintf.o
72 0 0 0 92 c89swprintf.o
72 0 0 0 80 c89vswprintf.o
54 0 0 0 92 c89asprintf.o
56 0 0 0 96 c89vasprintf.o
54 0 0 0 92 c89__ARM_asprintf.o
56 0 0 0 96 c89__ARM_vasprintf.o
56 0 0 0 88 c89__ARM_snprintf.o
52 0 0 0 80 c89__ARM_vsnprintf.o
24 0 0 0 84 __2printf.o
12 0 0 0 68 __2vprintf.o
20 0 0 0 84 __2fprintf.o
10 0 0 0 68 __2vfprintf.o
44 0 0 0 84 __2sprintf.o
36 0 0 0 76 __2vsprintf.o
56 0 0 0 88 __2snprintf.o
52 0 0 0 80 __2vsnprintf.o
24 0 0 0 84 __2wprintf.o
12 0 0 0 68 __2vwprintf.o
20 0 0 0 84 __2fwprintf.o
10 0 0 0 68 __2vfwprintf.o
72 0 0 0 92 __2swprintf.o
72 0 0 0 80 __2vswprintf.o
54 0 0 0 92 __2asprintf.o
56 0 0 0 96 __2vasprintf.o
54 0 0 0 92 __2__ARM_asprintf.o
56 0 0 0 96 __2__ARM_vasprintf.o
56 0 0 0 88 __2__ARM_snprintf.o
52 0 0 0 80 __2__ARM_vsnprintf.o
24 0 0 0 84 __1printf.o
12 0 0 0 68 __1vprintf.o
20 0 0 0 84 __1fprintf.o
10 0 0 0 68 __1vfprintf.o
44 0 0 0 84 __1sprintf.o
36 0 0 0 76 __1vsprintf.o
56 0 0 0 88 __1snprintf.o
52 0 0 0 80 __1vsnprintf.o
24 0 0 0 84 __1wprintf.o
12 0 0 0 68 __1vwprintf.o
20 0 0 0 84 __1fwprintf.o
10 0 0 0 68 __1vfwprintf.o
72 0 0 0 92 __1swprintf.o
72 0 0 0 80 __1vswprintf.o
54 0 0 0 92 __1asprintf.o
56 0 0 0 96 __1vasprintf.o
54 0 0 0 92 __1__ARM_asprintf.o
56 0 0 0 96 __1__ARM_vasprintf.o
56 0 0 0 88 __1__ARM_snprintf.o
52 0 0 0 80 __1__ARM_vsnprintf.o
............省略一大部分...........
104 0 0 0 84 __printf.o
78 0 0 0 108 _printf_pad.o
36 0 0 0 84 _printf_truncate.o
82 0 0 0 80 _printf_str.o
178 0 0 0 88 _printf_intcommon.o
120 0 0 0 92 _printf_dec.o
40 0 0 0 68 _printf_charcount.o
1050 0 0 0 216 _printf_fp_dec.o
764 38 0 0 100 _printf_fp_hex.o
48 0 0 0 96 _printf_char_common.o
10 0 0 0 68 _sputc.o
16 0 0 0 68 _snputc.o
128 0 0 0 84 _printf_fp_infnan.o
40 0 0 0 84 __printf_nopercent.o
44 0 0 0 108 _printf_char.o
36 0 0 0 80 _printf_char_file.o
64 0 0 0 84 _printf_char_file_locked.o
188 8 0 0 92 _printf_wctomb.o
144 8 0 0 88 _printf_mbtowc.o
48 0 0 0 96 _printf_wchar_common.o
16 0 0 0 68 _snputwc.o
44 0 0 0 108 _printf_wchar.o
86 0 0 0 80 _printf_wc.o
36 0 0 0 80 _printf_wchar_file.o
64 0 0 0 84 _printf_wchar_file_locked.o
124 0 0 0 92 _printf_longlong_dec.o
160 0 0 0 84 _printf_fp_infnan_snan.o
84 0 0 0 80 _printf_oct_ll.o
80 0 0 0 88 _printf_oct_int.o
112 0 0 0 124 _printf_oct_int_ll.o
92 40 0 0 88 _printf_hex_ll.o
88 40 0 0 88 _printf_hex_int.o
124 40 0 0 140 _printf_hex_int_ll.o
84 40 0 0 88 _printf_hex_ptr.o
124 40 0 0 144 _printf_hex_int_ptr.o
120 40 0 0 124 _printf_hex_ll_ptr.o
148 40 0 0 160 _printf_hex_int_ll_ptr.o
148 17 0 0 84 __printf_flags.o
184 0 0 0 84 __printf_ss.o
236 17 0 0 88 __printf_flags_ss.o
284 0 0 0 156 __printf_wp.o
326 17 0 0 160 __printf_flags_wp.o
366 0 0 0 156 __printf_ss_wp.o
406 17 0 0 160 __printf_flags_ss_wp.o
6 0 0 0 0 _printf_c.o
6 0 0 0 0 _printf_s.o
6 0 0 0 0 _printf_n.o
6 0 0 0 0 _printf_x.o
6 0 0 0 0 _printf_p.o
6 0 0 0 0 _printf_o.o
6 0 0 0 0 _printf_i.o
6 0 0 0 0 _printf_d.o
6 0 0 0 0 _printf_u.o
6 0 0 0 0 _printf_f.o
6 0 0 0 0 _printf_e.o
6 0 0 0 0 _printf_g.o
6 0 0 0 0 _printf_a.o
0 0 0 0 0 _printf_percent.o
4 0 0 0 0 _printf_percent_end.o
6 0 0 0 0 _printf_lli.o
6 0 0 0 0 _printf_lld.o
6 0 0 0 0 _printf_llu.o
10 0 0 0 0 _printf_ll.o
10 0 0 0 0 _printf_l.o
6 0 0 0 0 _printf_lc.o
6 0 0 0 0 _printf_ls.o
6 0 0 0 0 _printf_llo.o
6 0 0 0 0 _printf_llx.o
32 0 0 0 84 scanf.o
24 0 0 0 84 fscanf.o
60 0 0 0 84 sscanf.o
24 0 0 0 80 vscanf.o
16 0 0 0 80 vfscanf.o
52 0 0 0 80 vsscanf.o
60 0 0 0 84 swscanf.o
52 0 0 0 80 vswscanf.o
52 0 0 0 80 __ARM_vsscanf.o
32 0 0 0 84 scanfn.o
24 0 0 0 84 fscanfn.o
60 0 0 0 84 sscanfn.o
............省略一大部分...........
16 0 0 0 80 vfwscanf.o
28 0 0 0 68 _chval.o
884 0 0 0 100 _scanf.o
342 0 0 0 100 _scanf_longlong.o
332 0 0 0 96 _scanf_int.o
224 0 0 0 96 _scanf_str.o
1202 0 0 0 216 scanf_fp.o
0 0 0 0 0 scanf_nofp.o
800 0 0 0 100 scanf_hexfp.o
44 0 0 0 84 scanf_char.o
64 0 0 0 84 _sgetc.o
26 0 0 0 80 atoi.o
26 0 0 0 80 atol.o
26 0 0 0 80 atoll.o
308 0 0 0 100 scanf_infnan.o
24 0 0 0 68 scanf_char_file.o
64 0 0 0 84 scanf_char_file_locked.o
............省略一大部分...........
8 0 0 0 68 feof.o
8 0 0 0 68 ferror.o
100 0 0 0 120 fflush.o
26 0 0 0 136 fgetc.o
32 0 0 0 80 fgetpos.o
74 0 0 0 84 fgets.o
570 0 0 0 132 filbuf.o
470 0 0 0 88 flsbuf.o
28 0 0 0 136 fputc.o
34 0 0 0 80 fputs.o
58 0 0 0 88 fread.o
248 0 0 0 84 fseek.o
40 0 0 0 80 fsetpos.o
66 0 0 0 76 ftell.o
50 0 0 0 84 fwrite.o
4 0 0 0 68 getc.o
12 0 0 0 68 getchar.o
56 0 0 0 80 gets.o
60 0 0 0 80 perror.o
4 0 0 0 68 putc.o
12 0 0 0 68 putchar.o
44 0 0 0 84 puts.o
22 0 0 0 80 rewind.o
70 0 0 0 80 setvbuf.o
240 0 0 0 156 stdio.o
88 0 4 256 108 tmpnam.o
72 0 0 0 68 ungetc.o
78 0 0 0 112 __dup.o
280 0 0 0 96 freadfast.o
214 0 0 0 88 fread_bytes_avail.o
26 0 0 0 76 fread_bytes_avail_replaced.o
188 0 0 0 88 fwritefast.o
0 0 4 0 0 streamlock.o
312 0 0 0 112 initio.o
236 0 0 0 128 fopen.o
20 0 0 0 68 setbuf.o
0 0 12 252 0 stdio_streams.o
76 0 0 0 88 fclose.o
56 0 0 0 80 flushlinebuffered.o
102 0 0 0 96 fclose_tmpfile.o
32 0 0 0 80 fgetc_locked.o
36 0 0 0 80 fputc_locked.o
32 0 0 0 80 ftell_locked.o
52 0 0 0 80 fclose_locked.o
36 0 0 0 80 ungetc_locked.o
44 0 0 0 84 fseek_locked.o
86 0 0 0 84 fgets_locked.o
76 0 0 0 80 gets_locked.o
54 0 0 0 80 fputs_locked.o
64 0 0 0 80 puts_locked.o
............省略一大部分...........
16 0 0 42 68 locale.o
114 0 0 0 76 _lconv.o
16 0 0 48 76 localeconv.o
36 0 0 0 76 aeabi_lconv.o
16 0 0 56 76 aeabi_localeconv.o
60 0 0 0 80 findlocale.o
428 0 0 0 188 locale_r.o
2 0 0 0 68 locale_8859.o
16 0 0 88 136 mbdata.o
68 0 0 0 84 mblen.o
122 0 0 0 88 mbtowc.o
56 0 0 0 80 wctomb.o
80 0 0 0 88 mbstowcs.o
100 0 0 0 92 wcstombs.o
14 0 0 0 68 mbsinit.o
36 0 0 0 88 mbrlen.o
120 0 0 0 88 mbrtowc.o
58 0 0 0 80 wcrtomb.o
114 0 0 0 92 mbsrtowcs.o
140 0 0 0 100 wcsrtombs.o
40 0 0 0 80 btowc.o
42 0 0 0 84 wctob.o
............省略一大部分...........
8 0 0 0 68 labs.o
20 0 0 0 76 ldiv.o
20 0 0 0 68 llabs.o
24 0 0 0 80 lldiv.o
20 0 0 0 68 imaxabs.o
24 0 0 0 80 imaxdiv.o
112 0 0 228 160 rand.o
36 0 4 0 84 ANSI_rand.o
120 0 0 0 92 rand_r.o
28 0 0 0 84 ANSI_rand_r.o
18 0 0 0 80 exit.o
22 0 0 0 80 abort.o
4 0 0 0 68 _Exit.o
............省略一大部分...........
20 0 0 0 68 strchr.o
32 0 0 0 80 strcspn.o
36 0 0 0 76 strncat.o
30 0 0 0 76 strpbrk.o
28 0 0 0 80 strspn.o
36 0 0 0 80 strstr.o
80 0 0 0 84 strxfrm.o
184 0 0 0 84 strlcat.o
142 0 0 0 84 strlcpy.o
88 0 0 0 76 memcmp.o
72 0 0 0 80 strcpy.o
50 0 0 0 84 strncasecmp.o
42 0 0 0 84 strcasecmp.o
22 0 0 0 68 strrchr.o
62 0 0 0 76 strlen.o
18 0 0 0 68 wcscpy.o
............省略一大部分...........
8 0 0 0 76 memcpy.o
8 0 0 0 76 memmove.o
214 0 0 0 68 rt_memcpy.o
202 0 0 0 68 rt_memmove.o
100 0 0 0 80 rt_memcpy_w.o
122 0 0 0 80 rt_memmove_w.o
22 0 0 0 76 memset.o
16 0 0 0 68 aeabi_memset.o
16 0 0 0 68 aeabi_memset4.o
18 0 0 0 68 rt_memset.o
68 0 0 0 68 rt_memclr.o
78 0 0 0 80 rt_memclr_w.o
250 0 0 0 196 rt_neon_memcpy_w.o
84 0 0 0 172 rt_neon_memclr_w.o
128 0 0 0 172 rt_neon_memmove_w.o
220 0 0 0 164 rt_neon_memcpy_ca8.o
228 0 0 0 164 rt_neon_memcpy_ca9.o
236 0 0 0 164 rt_neon_memmove_ca8.o
236 0 0 0 164 rt_neon_memmove_ca9.o
130 0 0 0 96 rt_memcpy_w_ca8.o
118 0 0 0 96 rt_memcpy_w_qsp.o
118 0 0 0 96 rt_memcpy_w_ca9.o
216 0 0 0 96 rt_memcpy_w_ldrd.o
118 0 0 0 192 rt_noneon_memcpy_w_ca9.o
............省略一大部分...........
14 0 0 0 80 ctime.o
4 0 0 0 68 gmtime.o
12 0 0 44 68 localtime.o
428 0 0 0 116 mktime.o
864 0 0 0 184 strftime.o
0 12 0 0 0 _monlen.o
0 0 0 0 0 asctime_r.o
0 0 0 0 0 localtime_r.o
104 0 0 0 88 asctime_internal.o
184 0 0 0 80 localtime_internal.o
............省略一大部分...........
0 0 0 0 0 cpuinit.o
264 0 0 0 96 _get_argv.o
302 0 0 0 168 _get_argv_nomalloc.o
4 0 0 0 84 _main.o
320 0 0 0 152 _main_redirect.o
2 0 0 0 68 _main_arg.o
4 0 0 0 68 argv_veneer.o
2 0 0 0 68 no_argv.o
2 0 0 0 68 no_excepts.o
2 0 0 0 68 no_errno.o
2 0 0 0 68 use_snan.o
2 0 0 0 0 libinit.o
136 0 0 0 0 libinit2.o
36 0 0 0 0 libinit3.o
36 0 0 0 0 libinit4.o
36 0 0 0 0 libinit5.o
2 0 0 0 0 libshutdown.o
28 0 0 0 0 libshutdown2.o
8 0 0 0 68 __main.o
52 0 0 0 68 __scatter.o
76 0 0 0 68 __scatters.o
26 0 0 0 68 __scatter_copy.o
28 0 0 0 68 __scatter_zi.o
32 0 0 0 272 irqnopfx.o
314 0 0 0 92 bitcpy0.o
322 0 0 0 92 bitcpy1.o
362 0 0 0 92 bitcpy2.o
608 0 0 0 100 bitcpy3.o
468 0 0 0 92 bitmove0.o
474 0 0 0 92 bitmove1.o
508 0 0 0 92 bitmove2.o
764 0 0 0 100 bitmove3.o
............省略一大部分...........
34 0 0 0 76 prim_new.o
2 8 0 0 68 init.o
36 8 0 0 80 init_aeabi.o
44 8 0 0 76 arm_relocate.o
36 8 0 0 80 preinit_aeabi.o
4 0 0 0 68 _memcpy.o
8 0 0 0 84 _urdwr4.o
............省略一大部分...........
112 0 0 0 104 fputwc.o
36 0 0 0 80 fputws.o
200 0 0 0 104 fgetwc.o
82 0 0 0 84 fgetws.o
4 0 0 0 68 getwc.o
12 0 0 0 68 getwchar.o
4 0 0 0 68 putwc.o
12 0 0 0 68 putwchar.o
108 0 0 0 68 ungetwc.o
44 0 0 0 84 backspacewc.o
44 0 0 0 68 fwide.o
36 0 0 0 84 filbuf_fwide.o
36 0 0 0 84 flsbuf_fwide.o
36 0 0 0 80 fputwc_locked.o
32 0 0 0 80 fgetwc_locked.o
66 0 0 0 80 fwide_locked.o
132 0 0 0 80 ungetwc_locked.o
32 0 0 0 80 backspacewc_locked.o
54 0 0 0 80 fputws_locked.o
94 0 0 0 84 fgetws_locked.o
73526 40563 112 1686 76484 TOTAL
ENTRY at offset 1 in section !!!main of __main.o
从中我们可以看到有__main.o等文件,接下来我们可以使用armar -x
命令将c_w.l
解压出以上全部文件,然后使用fromelf
来查看__main.o的详细信息,这里就不一一尝试了!
下面我们以STM32F407VG片子为例,看看其调试时的汇编代码(Keil5中)。直接进调试模式,注意:最好将汇编窗口右键改为assembly mode。
首先看看定义的中断向量表部分,如下图:
其中,SystemInit
为 ST 提供的时钟初始化函数(如果使用了外部RAM,可能还包含外部RAM的配置)。接着,就会有如下汇编代码(具体看里面的注释即可):
0x08000180 0243 DCW 0x0243 ; 小端模式,地址:0x08000243
0x08000182 0800 DCW 0x0800
0x08000184 0243 DCW 0x0243 ; 小端模式,地址:0x08000243
0x08000186 0800 DCW 0x0800
; 从此往上即为中断向量表,与开发环境的启动文件中的中断向量表相对应,32位地址,小端模式
; 下面就是上文介绍的启动流程:
__main:
0x08000188 F000F802 BL.W __scatterload (0x08000190) ; 负责把RW/RO输出段从装载域地址复制到运行域地址,并完成了ZI运行域的初始化工作。
0x0800018C F000F83C BL.W __rt_entry (0x08000208) ; 负责初始化堆、栈,完成C库函数的初始化。其会调用一系列初始化函数,最后自动跳转向main()函数。
; 执行完后,R10和R11就被赋给成了下面两个值,Map文件中的symbol:
; Region$$Table$$Base 0x08000898 Number 0 anon$$obj.o(Region$$Table)
; Region$$Table$$Limit 0x080008b8 Number 0 anon$$obj.o(Region$$Table)
; !!!-----------注意:以下注释中括号中的数据为我在调试时的值,自行调试时值可能有变化-----------!!!
; !!!-----------注意:以下注释中非括号中的数据一般为左侧地址或者立即数-----------!!!
__scatterload:
0x08000190 A00A ADR r0,{pc}+0x2C ; 将基于PC相对偏移的地址值(0x080001BC)读取到寄存器r0中。
0x08000192 E8900C00 LDM r0,{r10-r11} ; 将R0对应地址存放的的2个字copy到R10~R11中。即:r10 = 0x000006DC; r11 = 0x000006FC
0x08000196 4482 ADD r10,r10,r0 ; r10 = r10 + r0 = 0x000006DC + 0x080001BC = 0x08000898
0x08000198 4483 ADD r11,r11,r0 ; r11 = r11 + r0 = 0x000006FC + 0x080001BC = 0x080008B8
0x0800019A F1AA0701 SUB r7,r10,#0x01 ; r7 = r10 - 0x01 = 0x08000898 - 0x01 = 0x08000897
__scatterload_null:
0x0800019E 45DA CMP r10,r11 ; 比较r10和r11。实际做r10-r11操作,根据结果修改CPSR中条件标志位的值
0x080001A0 D101 BNE 0x080001A6 ; Z 标志位不等于零时, 跳转到0x080001A6
0x080001A2 F000F831 BL.W __rt_entry (0x08000208) ; 最后一步:在执行完__scatterload_copy和__scatterload_zeroinit后,r10 == r11 ,上面的跳转不成立,执行该句
0x080001A6 F2AF0E09 ADR.W lr,{pc}-0x07 ; 将基于PC相对偏移的地址值(0x0800019F)读取到寄存器lr中。lr = 0x0800019F
0x080001AA E8BA000F LDM r10!,{r0-r3} ; 将R10对应地址存放的的4个双字copy到R0~R3中,!表示执行语句后将地址赋值给r10。r10 = r10 + 4*4 = 0x08000898 + 0x10 = 0x080008A8.
; R0:表示的是程序加载视图的RW区的起始地址(0x080008B8)
; R1:要复制到的运行域地址(RAM中0x20000000)
; R2:要复制的RW数据的个数(0x00000020)
; R3:是__scatterload_copy函数的起始地址0x080001C4
0x080001AE F0130F01 TST r3,#0x01 ;
0x080001B2 BF18 IT NE
0x080001B4 1AFB SUBNE r3,r7,r3
0x080001B6 F0430301 ORR r3,r3,#0x01
0x080001BA 4718 BX r3 ; 跳转到r3,也就是__scatterload_copy函数,开始复制数据
0x080001BC 06DC DCW 0x06DC
0x080001BE 0000 DCW 0x0000
0x080001C0 06FC DCW 0x06FC
0x080001C2 0000 DCW 0x0000
__scatterload_copy: ; 该函数负责将镜像中的RW数据复制到芯片的ARM中
0x080001C4 3A10 SUBS r2,r2,#0x10 ; 循环:R2=R2-0x10,SUBS中S表示把进位结果写入CPSR。
0x080001C6 BF24 ITT CS
0x080001C8 C878 LDMCS r0!,{r3-r6}
0x080001CA C178 STMCS r1!,{r3-r6}
0x080001CC D8FA BHI __scatterload_copy (0x080001C4) ; 循环:条件成立跳转到回去
0x080001CE 0752 LSLS r2,r2,#29
0x080001D0 BF24 ITT CS
0x080001D2 C830 LDMCS r0!,{r4-r5}
0x080001D4 C130 STMCS r1!,{r4-r5}
0x080001D6 BF44 ITT MI
0x080001D8 6804 LDRMI r4,[r0,#0x00]
0x080001DA 600C STRMI r4,[r1,#0x00]
0x080001DC 4770 BX lr ; 这里返回到lr = 0x0800019F,即在此调用__scatterload_null。会继续将R10对应地址存放的的4个双字copy到R0~R3中,但是,此时的R10由于之前的一次调用,其值已经增加
; R0:是程序加载视图的RW区的起始地址(0x080008D8 = 0x080008B8 + 0x20 上面执行后面的部分)
; R1:是要输出的执行视图的RW区的地址(RAM中 0x20000020)
; R2:要复制的RW数据的个数(0x00000660)
; R3:是__scatterload_zeroinit函数的起始地址0x080001E0
; 接下来就会跳转到__scatterload_zeroinit
0x080001DE 0000 MOVS r0,r0
__scatterload_zeroinit:
0x080001E0 2300 MOVS r3,#0x00
0x080001E2 2400 MOVS r4,#0x00
0x080001E4 2500 MOVS r5,#0x00
0x080001E6 2600 MOVS r6,#0x00
0x080001E8 3A10 SUBS r2,r2,#0x10 ; 循环:
0x080001EA BF28 IT CS
0x080001EC C178 STMCS r1!,{r3-r6}
0x080001EE D8FB BHI 0x080001E8 ; 循环:条件成立跳转到回去
0x080001F0 0752 LSLS r2,r2,#29
0x080001F2 BF28 IT CS
0x080001F4 C130 STMCS r1!,{r4-r5}
0x080001F6 BF48 IT MI
0x080001F8 600B STRMI r3,[r1,#0x00]
0x080001FA 4770 BX lr ; 这里返回到lr = 0x0800019F,即在此调用__scatterload_null。这次回去后r10 == r11.回去后会跳转到__rt_entry
__rt_lib_init:
0x080001FC B51F PUSH {r0-r4,lr}
__rt_lib_init_fp_1:
0x080001FE F000FB45 BL.W _fp_init (0x0800088C)
__rt_lib_init_alloca_1:
0x08000202 BD1F POP {r0-r4,pc}
__rt_lib_shutdown:
0x08000204 B510 PUSH {r4,lr}
__rt_lib_shutdown_cpp_1:
0x08000206 BD10 POP {r4,pc}
__rt_entry: ; 在执行完__scatterload后,会紧接着执行该函数
0x08000208 F000F831 BL.W __user_setup_stackheap (0x0800026E) ; 设置堆栈的函数,该函数中会调用由用户实现的__user_initial_stackheap函数,如第二节的启动文件中最后的代码即为用户实现的堆栈初始化该函数
0x0800020C 4611 MOV r1,r2
__rt_entry_li:
0x0800020E F7FFFFF5 BL.W __rt_lib_init (0x080001FC) ; 初始化C库
__rt_entry_main:
0x08000212 F000FA3B BL.W main (0x0800068C) ; 跳转到C的main函数
0x08000216 F000F84F BL.W exit (0x080002B8) ; 如果main返回,则执行该函数,结束程序运行
__rt_exit:
0x0800021A B403 PUSH {r0-r1}
__rt_exit_ls:
0x0800021C F7FFFFF2 BL.W __rt_lib_shutdown (0x08000204)
__rt_exit_exit:
0x08000220 BC03 POP {r0-r1}
0x08000222 F000F857 BL.W _sys_exit (0x080002D4)
0x08000226 0000 MOVS r0,r0
0x08000226 0000 MOVS r0,r0
; 此处往下,就是定义的各个中断向量的实现代码,第一个就是复位中断,程序就是从复位中断开始执行的
Reset_Handler:
0x08000228 4809 LDR r0,[pc,#36] ; @0x08000250
0x0800022A 4780 BLX r0
0x0800022C 4809 LDR r0,[pc,#36] ; @0x08000254
0x0800022E 4700 BX r0
NMI_Handler:
0x08000230 E7FE B NMI_Handler (0x08000230)
HardFault_Handler:
0x08000232 E7FE B HardFault_Handler (0x08000232)
MemManage_Handler:
0x08000234 E7FE B MemManage_Handler (0x08000234)
BusFault_Handler:
0x08000236 E7FE B BusFault_Handler (0x08000236)
UsageFault_Handler:
0x08000238 E7FE B UsageFault_Handler (0x08000238)
SVC_Handler:
0x0800023A E7FE B SVC_Handler (0x0800023A)
DebugMon_Handler:
0x0800023C E7FE B DebugMon_Handler (0x0800023C)
PendSV_Handler:
0x0800023E E7FE B PendSV_Handler (0x0800023E)
SysTick_Handler:
0x08000240 E7FE B SysTick_Handler (0x08000240)
Default_Handler:
0x08000242 E7FE B Default_Handler (0x08000242)
__user_initial_stackheap:
0x08000244 4804 LDR r0,[pc,#16] ; @0x08000258
0x08000246 4905 LDR r1,[pc,#20] ; @0x0800025C
0x08000248 4A05 LDR r2,[pc,#20] ; @0x08000260
0x0800024A 4B06 LDR r3,[pc,#24] ; @0x08000264
0x0800024C 4770 BX lr
0x0800024E 0000 DCW 0x0000
0x08000250 0621 DCW 0x0621
0x08000252 0800 DCW 0x0800
0x08000254 0189 DCW 0x0189
0x08000256 0800 DCW 0x0800
0x08000258 0080 DCW 0x0080
0x0800025A 2000 DCW 0x2000
0x0800025C 0680 DCW 0x0680
0x0800025E 2000 DCW 0x2000
0x08000260 0280 DCW 0x0280
0x08000262 2000 DCW 0x2000
0x08000264 0280 DCW 0x0280
0x08000266 2000 DCW 0x2000
__use_two_region_memory:
0x08000268 4770 BX lr
__rt_heap_escrow$2region:
0x0800026A 4770 BX lr
__rt_heap_expand$2region:
0x0800026C 4770 BX lr
__user_setup_stackheap:
0x0800026E 4675 MOV r5,lr
0x08000270 F000F82C BL.W __user_libspace (0x080002CC)
0x08000274 46AE MOV lr,r5
0x08000276 0005 MOVS r5,r0
0x08000278 4669 MOV r1,sp
0x0800027A 4653 MOV r3,r10
0x0800027C F0200007 BIC r0,r0,#0x07
0x08000280 4685 MOV sp,r0
0x08000282 B018 ADD sp,sp,#0x60
0x08000284 B520 PUSH {r5,lr}
0x08000286 F7FFFFDD BL.W __user_initial_stackheap (0x08000244)
0x0800028A E8BD4020 POP {r5,lr}
0x0800028E F04F0600 MOV r6,#0x00
0x08000292 F04F0700 MOV r7,#0x00
0x08000296 F04F0800 MOV r8,#0x00
0x0800029A F04F0B00 MOV r11,#0x00
0x0800029E F0210107 BIC r1,r1,#0x07
0x080002A2 46AC MOV r12,r5
0x080002A4 E8AC09C0 STM r12!,{r6-r8,r11}
0x080002A8 E8AC09C0 STM r12!,{r6-r8,r11}
0x080002AC E8AC09C0 STM r12!,{r6-r8,r11}
0x080002B0 E8AC09C0 STM r12!,{r6-r8,r11}
0x080002B4 468D MOV sp,r1
0x080002B6 4770 BX lr
exit:
0x080002B8 B510 PUSH {r4,lr}
0x080002BA 4604 MOV r4,r0
0x080002BC F3AF8000 NOP.W
0x080002C0 4620 MOV r0,r4
0x080002C2 E8BD4010 POP {r4,lr}
0x080002C6 F7FFBFA8 B.W __rt_exit (0x0800021A)
0x080002CA 0000 MOVS r0,r0
__user_libspace:
0x080002CC 4800 LDR r0,[pc,#0] ; @0x080002D0
0x080002CE 4770 BX lr
0x080002D0 0020 DCW 0x0020
0x080002D2 2000 DCW 0x2000
_sys_exit:
0x080002D4 4901 LDR r1,[pc,#4] ; @0x080002DC
0x080002D6 2018 MOVS r0,#0x18
0x080002D8 BEAB BKPT 0xAB
0x080002DA E7FE B 0x080002DA
0x080002DC 0026 DCW 0x0026
0x080002DE 0002 DCW 0x0002
目前,多数 MCU 厂商都提供一个启动文件。当然,编程者也可以自己编写启动文件,具体编写要求ARM的网站上都有相关文档进行说明。下面分析一下 STM32 启动文件startup_stm32f407xx.s
,具体看里面的注释。
;******************** (C) COPYRIGHT 2017 STMicroelectronics ********************
;* File Name : startup_stm32f407xx.s
;* Author : MCD Application Team
;* Version : V2.6.1
;* Date : 14-February-2017
;* Description : STM32F407xx devices vector table for MDK-ARM toolchain.
;* This module performs:
;* - Set the initial SP
;* - Set the initial PC == Reset_Handler
;* - Set the vector table entries with the exceptions ISR address
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the CortexM4 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.
;* <<< Use Configuration Wizard in Context Menu >>>
;*******************************************************************************
;
;* Redistribution and use in source and binary forms, with or without modification,
;* are permitted provided that the following conditions are met:
;* 1. Redistributions of source code must retain the above copyright notice,
;* this list of conditions and the following disclaimer.
;* 2. Redistributions in binary form must reproduce the above copyright notice,
;* this list of conditions and the following disclaimer in the documentation
;* and/or other materials provided with the distribution.
;* 3. Neither the name of STMicroelectronics nor the names of its contributors
;* may be used to endorse or promote products derived from this software
;* without specific prior written permission.
;*
;* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
;* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
;* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
;* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
;* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
;* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
;* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;
;*******************************************************************************
; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x0800 ; 定义栈大小
; AREA 命令指示汇编器汇编一个新的代码段或数据段。
AREA STACK, NOINIT, READWRITE, ALIGN=3 ; 代码段名称为STACK,未初始化,允许读写,8字节对齐
Stack_Mem SPACE Stack_Size ; 分配Stack_Size的栈空间,首地址赋给Stack_Mem
__initial_sp ; 栈顶指针,全局变量
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base ; 堆末底部地址
Heap_Mem SPACE Heap_Size
__heap_limit ; 堆界限地址
PRESERVE8 ; 指定当前文件保持堆栈八字节对齐
THUMB ; Thumb命令模式
; Vector Table Mapped to Address 0 at Reset ; 终端向量表 重启时程序从这里运行,必须将该地址映射到0x00000000
AREA RESET, DATA, READONLY ; 代码段名称为RESET,DATA类型,只读
EXPORT __Vectors ; 导出中断向量表地址(供外部可以使用)
EXPORT __Vectors_End ; 导出中断向量表结束指针(供外部可以使用)
EXPORT __Vectors_Size ; 中断向量表大小(供外部可以使用)
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window WatchDog
DCD PVD_IRQHandler ; PVD through EXTI Line detection
DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line
DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line
DCD FLASH_IRQHandler ; FLASH
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line0
DCD EXTI1_IRQHandler ; EXTI Line1
DCD EXTI2_IRQHandler ; EXTI Line2
DCD EXTI3_IRQHandler ; EXTI Line3
DCD EXTI4_IRQHandler ; EXTI Line4
DCD DMA1_Stream0_IRQHandler ; DMA1 Stream 0
DCD DMA1_Stream1_IRQHandler ; DMA1 Stream 1
DCD DMA1_Stream2_IRQHandler ; DMA1 Stream 2
DCD DMA1_Stream3_IRQHandler ; DMA1 Stream 3
DCD DMA1_Stream4_IRQHandler ; DMA1 Stream 4
DCD DMA1_Stream5_IRQHandler ; DMA1 Stream 5
DCD DMA1_Stream6_IRQHandler ; DMA1 Stream 6
DCD ADC_IRQHandler ; ADC1, ADC2 and ADC3s
DCD CAN1_TX_IRQHandler ; CAN1 TX
DCD CAN1_RX0_IRQHandler ; CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; External Line[9:5]s
DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9
DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10
DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; External Line[15:10]s
DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI Line
DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line
DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12
DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13
DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD DMA1_Stream7_IRQHandler ; DMA1 Stream7
DCD FMC_IRQHandler ; FMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&2 underrun errors
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Stream0_IRQHandler ; DMA2 Stream 0
DCD DMA2_Stream1_IRQHandler ; DMA2 Stream 1
DCD DMA2_Stream2_IRQHandler ; DMA2 Stream 2
DCD DMA2_Stream3_IRQHandler ; DMA2 Stream 3
DCD DMA2_Stream4_IRQHandler ; DMA2 Stream 4
DCD ETH_IRQHandler ; Ethernet
DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line
DCD CAN2_TX_IRQHandler ; CAN2 TX
DCD CAN2_RX0_IRQHandler ; CAN2 RX0
DCD CAN2_RX1_IRQHandler ; CAN2 RX1
DCD CAN2_SCE_IRQHandler ; CAN2 SCE
DCD OTG_FS_IRQHandler ; USB OTG FS
DCD DMA2_Stream5_IRQHandler ; DMA2 Stream 5
DCD DMA2_Stream6_IRQHandler ; DMA2 Stream 6
DCD DMA2_Stream7_IRQHandler ; DMA2 Stream 7
DCD USART6_IRQHandler ; USART6
DCD I2C3_EV_IRQHandler ; I2C3 event
DCD I2C3_ER_IRQHandler ; I2C3 error
DCD OTG_HS_EP1_OUT_IRQHandler ; USB OTG HS End Point 1 Out
DCD OTG_HS_EP1_IN_IRQHandler ; USB OTG HS End Point 1 In
DCD OTG_HS_WKUP_IRQHandler ; USB OTG HS Wakeup through EXTI
DCD OTG_HS_IRQHandler ; USB OTG HS
DCD DCMI_IRQHandler ; DCMI
DCD 0 ; Reserved
DCD HASH_RNG_IRQHandler ; Hash and Rng
DCD FPU_IRQHandler ; FPU
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors ; 计算中断向量表的大小
AREA |.text|, CODE, READONLY ; 代码段,|.text| 用于表示由 C 编译程序产生的代码段,或用于以某种方式与 C 库关联的代码段。 CODE类型,只读
; 以下开始定义各种中断,第一个便是复位中断,顺序与上面的终端向量表一致!
; Reset handler
Reset_Handler PROC ; 代码开始,与ENDP成对出现
EXPORT Reset_Handler [WEAK] ; 复位中断,[WEAK]修饰代表其他文件有函数定义优先调用
IMPORT SystemInit ; 导入外部函数SystemInit
IMPORT __main ; 导入外部函数__main
LDR R0, =SystemInit
BLX R0 ; 无返回调用SystemInit
LDR R0, =__main
BX R0 ; 有返回调用__main
ENDP ; 代码结束,与PROC成对出现
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
; 终端向量表的External Interrupts部分。 默认的外部中断,通常有外部实现。先导出各种符号以供外部使用,然后时默认的定义
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
EXPORT RTC_WKUP_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Stream0_IRQHandler [WEAK]
EXPORT DMA1_Stream1_IRQHandler [WEAK]
EXPORT DMA1_Stream2_IRQHandler [WEAK]
EXPORT DMA1_Stream3_IRQHandler [WEAK]
EXPORT DMA1_Stream4_IRQHandler [WEAK]
EXPORT DMA1_Stream5_IRQHandler [WEAK]
EXPORT DMA1_Stream6_IRQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_TIM9_IRQHandler [WEAK]
EXPORT TIM1_UP_TIM10_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_TIM11_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT OTG_FS_WKUP_IRQHandler [WEAK]
EXPORT TIM8_BRK_TIM12_IRQHandler [WEAK]
EXPORT TIM8_UP_TIM13_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_TIM14_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT DMA1_Stream7_IRQHandler [WEAK]
EXPORT FMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_DAC_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Stream0_IRQHandler [WEAK]
EXPORT DMA2_Stream1_IRQHandler [WEAK]
EXPORT DMA2_Stream2_IRQHandler [WEAK]
EXPORT DMA2_Stream3_IRQHandler [WEAK]
EXPORT DMA2_Stream4_IRQHandler [WEAK]
EXPORT ETH_IRQHandler [WEAK]
EXPORT ETH_WKUP_IRQHandler [WEAK]
EXPORT CAN2_TX_IRQHandler [WEAK]
EXPORT CAN2_RX0_IRQHandler [WEAK]
EXPORT CAN2_RX1_IRQHandler [WEAK]
EXPORT CAN2_SCE_IRQHandler [WEAK]
EXPORT OTG_FS_IRQHandler [WEAK]
EXPORT DMA2_Stream5_IRQHandler [WEAK]
EXPORT DMA2_Stream6_IRQHandler [WEAK]
EXPORT DMA2_Stream7_IRQHandler [WEAK]
EXPORT USART6_IRQHandler [WEAK]
EXPORT I2C3_EV_IRQHandler [WEAK]
EXPORT I2C3_ER_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_OUT_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_IN_IRQHandler [WEAK]
EXPORT OTG_HS_WKUP_IRQHandler [WEAK]
EXPORT OTG_HS_IRQHandler [WEAK]
EXPORT DCMI_IRQHandler [WEAK]
EXPORT HASH_RNG_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMP_STAMP_IRQHandler
RTC_WKUP_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Stream0_IRQHandler
DMA1_Stream1_IRQHandler
DMA1_Stream2_IRQHandler
DMA1_Stream3_IRQHandler
DMA1_Stream4_IRQHandler
DMA1_Stream5_IRQHandler
DMA1_Stream6_IRQHandler
ADC_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_TIM9_IRQHandler
TIM1_UP_TIM10_IRQHandler
TIM1_TRG_COM_TIM11_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTC_Alarm_IRQHandler
OTG_FS_WKUP_IRQHandler
TIM8_BRK_TIM12_IRQHandler
TIM8_UP_TIM13_IRQHandler
TIM8_TRG_COM_TIM14_IRQHandler
TIM8_CC_IRQHandler
DMA1_Stream7_IRQHandler
FMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_DAC_IRQHandler
TIM7_IRQHandler
DMA2_Stream0_IRQHandler
DMA2_Stream1_IRQHandler
DMA2_Stream2_IRQHandler
DMA2_Stream3_IRQHandler
DMA2_Stream4_IRQHandler
ETH_IRQHandler
ETH_WKUP_IRQHandler
CAN2_TX_IRQHandler
CAN2_RX0_IRQHandler
CAN2_RX1_IRQHandler
CAN2_SCE_IRQHandler
OTG_FS_IRQHandler
DMA2_Stream5_IRQHandler
DMA2_Stream6_IRQHandler
DMA2_Stream7_IRQHandler
USART6_IRQHandler
I2C3_EV_IRQHandler
I2C3_ER_IRQHandler
OTG_HS_EP1_OUT_IRQHandler
OTG_HS_EP1_IN_IRQHandler
OTG_HS_WKUP_IRQHandler
OTG_HS_IRQHandler
DCMI_IRQHandler
HASH_RNG_IRQHandler
FPU_IRQHandler
B .
ENDP
ALIGN
;*******************************************************************************
; User Stack and Heap initialization 编译器预处理命令,主要是用来初始化用户堆栈
;*******************************************************************************
IF :DEF:__MICROLIB ; "DEF"的用法为 :DEF:X 就是说X定义了则为真,否则为假。若定义了__MICROLIB,则将__initial_sp,__heap_base,__heap_limit亦即栈顶地址,堆始末地址赋予全局属性,使外部程序可以使用。
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE ; 如果没定义__MICROLIB,则使用默认的C运行时库
IMPORT __use_two_region_memory ; 用于指定存储器模式为双段模式,即一部分储存区用于栈空间,其他的存储区用于堆空间,堆区空间可以为0,但是,这样就不能调用malloc()内存分配函数;堆区空间也可以由存储器分配,也可以从执行环境中继承。在汇编代码中,通过 IMPORT __use_two_region_memory 表明使用双段模式;在C语言中,通过 #pragma import(__use_two_region_memory)语句表明使用双段模式。
EXPORT __user_initial_stackheap
__user_initial_stackheap ; 此处是初始化两区的堆栈空间,堆是从由低到高的增长,栈是由高向低生长的,两个是互相独立的数据段,并不能交叉使用。
LDR R0, = Heap_Mem ; 保存堆始地址
LDR R1, =(Stack_Mem + Stack_Size) ; 保存栈的大小
LDR R2, = (Heap_Mem + Heap_Size) ; 保存堆的大小
LDR R3, = Stack_Mem ; 保存栈顶指针
BX LR
ALIGN ; 填充字节使地址对齐
ENDIF
END ; END 命令指示汇编器,已到达一个源文件的末尾。
;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****