|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
- q# L8 @3 L+ h
启动流程
& I/ G" y/ T; v- l: H0 h' @7 k* @ d 嵌入式应用程序在用户定义的 main() 函数启动之前需要初始化序列。 这称为启动代码或启动代码。 ARM C 库包含启动应用程序所必需的预编译和预组装代码段。链接应用程序时,链接器会根据应用程序从 C 库中包含必要的代码,以便为应用程序创建自定义启动代码。但需要注意的是,ARM 自己的编译套件提供了如下三种库:! ?% ]) `, _3 w. H6 ?% J* W7 ~
8 j3 b8 m, U1 u0 ^+ s
* C' f1 Z, z/ ]1 b/ H' Y
* i( Y2 V! Y2 a, f+ `& K
根据ARM官方的文档说明,以下分析描述的启动代码适用于标准 ARM C 库。 它不适用于 ARM C 微库。 启动流程同样适用于 ARMv4T 及更高版本的架构。如下图清晰的表示了程序启动到用户的main() 之前的流程:& ]+ G1 W- J9 b% c3 ?5 R
6 O0 Z, l+ s0 e
! u7 K9 ?% ^0 H9 u* r$ f0 g
5 w3 I" ?. c: X. T__main! M: ?, k4 Y# U* J$ `1 k
函数__main是C库的入口点。 除非您更改它,否则__main是ARM链接器(armlink)在创建映像时使用的ELF映像的缺省入口点。 下图显示了C库启动期间__main调用的函数。7 ?1 `# l b2 U6 s/ G# @# {
1 ^& | W2 c! |0 h: Y
+ ^% G, M$ }- {" Y& w' w% ^* ^" l) x! u6 {! [7 G9 }. D
__scatterload
7 N( _* |8 E3 i. [/ ?7 A8 l 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链接器输出的区域表。
6 I$ E8 z: N6 H+ Q8 y9 Y 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表包含需要初始化的非根代码和数据区域的地址。 区域表还包含一个函数指针,指示区域需要初始化,例如复制,归零或解压缩功能。2 D* r6 _8 y+ D3 Q7 E. U) @# v- p
__scatterload遍历区域表并初始化各种执行时区域。 功能如下:6 f' M- `, {, F2 v
! \* x6 @3 U" Z" _. y4 }
Initializes the Zero Initialized (ZI) regions to zero
5 a3 d% ]; v4 v! I. t3 T; Q% `7 ^Copies or decompresses the non-root code and data region from their load-time locations to the execute-time regions.
' X* b$ a7 ?+ N/ T' ]__main always calls this function during startup before calling __rt_entry .
5 O/ o* v) ?& t/ z9 l. N [3 C9 S( i! _; `' y5 U y
__rt_entry
: c7 r s7 e+ k) N7 o+ I __main调用__rt_entry来初始化堆栈,堆和其他C库子系统。__rt_entry调用各种初始化函数,然后调用用户级main()。以下列出了_rt_entry可以调用的函数。 这些函数按它们被调用的顺序列出:
: B! q7 g% `6 \" S0 i5 u& I
" L% ^" R- _" o_platform_pre_stackheap_init+ d [7 f6 h3 _0 ]
__user_setup_stackheap or setup the Stack Pointer (SP) by another method& L9 i% T$ r* n4 I
_platform_post_stackheap_init
' M, Z, T, v0 @ H( u* s' x__rt_lib_init
) z6 V& Q: V& A, V. c- q6 c_platform_post_lib_init
O3 {0 z+ W/ o2 Y3 _0 Wmain()
7 }; ]- i' S$ J, U* a% C/ R7 n! nexit()
0 a1 n" F4 n) W4 x( i" G3 X; Dplatform *函数不是标准C库的一部分。 如果你定义它们,则链接器会在__rt_entry中对它们进行调用。$ f! l7 M1 n% a/ j
main()是用户级应用程序的入口点。 寄存器r0和r1包含main()的参数。 如果main()返回,则将其返回值传递给exit()并退出应用程序。
6 k1 H6 D+ H9 d: l# [. I__rt_entry还负责设置堆栈和堆。 但是,设置堆栈和堆取决于用户指定的方法。 可以通过以下任何方法设置堆栈和堆:
; a& B0 s4 G/ _! \; r _2 y1 u: j, \6 D' C4 v9 g9 w
调用__user_setup_stackheap。 这也获得了堆使用的内存边界(堆顶部和堆基)。1 Y4 p, \& E9 [! _& Y; X
使用符号__initial_sp的值加载 SP。: e" B' d, f2 K" y4 D) F
Using the top of the ARM_LIB_STACK or ARM_LIB_STACKHEAP region specified in the linker scatter file.
\' ?6 N% X9 t __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函数。
) I' Z6 L0 s" O, ]- L" a/ D5 t( m& T _; y% V1 S3 g4 x" q
_platform_pre_stackheap_init: h. O5 q2 Q- ~8 e" U4 |) X
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会在初始化堆栈和堆的代码之前调用此函数。
, P# q5 K$ y$ H5 N& X
0 E, M) ]. d# b$ O- ?/ @__user_setup_stackheap; q7 M6 Q. a3 x+ O
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函数的包装器。, Z1 L0 C) C1 G; j
9 k }2 ?. T' u0 E! K_platform_post_stackheap_init
3 C# I3 S: \, v 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会在初始化堆栈和堆的代码之后调用此函数。1 d1 R, K# i) @4 E
Q9 m$ x4 s# q2 H% ]
__rt_lib_init7 x7 B( p9 }# {. A
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在启动期间始终调用此函数。
3 f# l) c3 m# q+ A$ C 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。
8 R0 z1 L/ y9 }+ O The function returns argc and argv in registers r0 and r1 respectively if the user-level main() requires them. 如果用户级main()需要,该函数分别在寄存器 r0 和 r1 中返回argc和argv。
# F8 t* k) |' C6 G/ {0 s
* p5 q2 H3 Q! p+ D& hThe 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可以调用的函数。 这些函数按它们被调用的顺序列出:8 v, J* ]! D% @2 S. }
4 c( C/ Y. |! d0 r: Y
_fp_init
- V. I6 V# c0 L_init_alloc
6 H7 Y; j* W& f& p W& s_rand_init0 _- E& }: d2 }: I5 k5 O
_get_lc_collate
; R+ _' s! A5 Z+ [! f8 I/ X_get_lc_ctype
7 s$ W/ B! b2 Y8 m_get_lc_monetary
( Y9 r5 E; ?# ^0 m9 D& }2 I, D B: e_get_lc_numeric$ v# K. f- r- W
_get_lc_time" H* j/ |% U0 m6 s# |- N. c
_atexit_init. r8 u& A6 M' u( K# d( g
_signal_init. }8 Y v) J. n- `0 d
_fp_trap_init7 d: G+ C( D" i# Q
_clock_init
3 e3 w$ k$ G: @! M3 O_getenv_init
# f# n7 }& }4 d+ \_initio
5 @: l; W& y# S- C! y_ARM_get_argv
0 n- |. b7 A6 z) w_alloca_initialize
7 T y; l+ b% @_ARM_exceptions_init0 R7 K; @9 X( Q
_cpp_initialize__aeabi5 `) k, w, D6 S& t: X8 o- R
这里就不一一介绍各函数了,想进一步了解的去查看官方文档即可!; U7 ]& b3 N' I1 V/ m
M2 b4 M- ?1 |( M) P
_platform_post_lib_init
, P H8 ?5 X: W3 Z 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()函数之前,调用此函数。 |
|