|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
- B) Z i8 l& ^: f! k# L9 V- r8 M+ v启动流程/ ]# k0 j# a1 u8 G
嵌入式应用程序在用户定义的 main() 函数启动之前需要初始化序列。 这称为启动代码或启动代码。 ARM C 库包含启动应用程序所必需的预编译和预组装代码段。链接应用程序时,链接器会根据应用程序从 C 库中包含必要的代码,以便为应用程序创建自定义启动代码。但需要注意的是,ARM 自己的编译套件提供了如下三种库:
* |; w4 w. U" S4 V- y
; J& W; x. s( e- ?" q, v9 k+ ~# Y
* D# H' p, x6 J' z$ h
. g% \+ ~5 U8 E7 G( o根据ARM官方的文档说明,以下分析描述的启动代码适用于标准 ARM C 库。 它不适用于 ARM C 微库。 启动流程同样适用于 ARMv4T 及更高版本的架构。如下图清晰的表示了程序启动到用户的main() 之前的流程:! X6 h4 J/ T- x
0 X, z! \0 A' ~
& z0 E7 R" J/ a+ f' q( ?) r, s( k3 d* U5 I0 k4 u
__main
' z$ h) j. z7 c 函数__main是C库的入口点。 除非您更改它,否则__main是ARM链接器(armlink)在创建映像时使用的ELF映像的缺省入口点。 下图显示了C库启动期间__main调用的函数。
4 K ^$ M3 q c0 E9 f, K9 `9 A5 _2 S7 m- s/ F& S
/ X5 N, k3 @: E1 k" S" u/ j2 g4 d( X0 B
$ T1 n! S1 ~! v__scatterload
/ C" O- A3 Q1 m/ g7 X 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链接器输出的区域表。
: q: c2 R5 C6 X7 M 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表包含需要初始化的非根代码和数据区域的地址。 区域表还包含一个函数指针,指示区域需要初始化,例如复制,归零或解压缩功能。
4 i# F! _2 L3 T, j__scatterload遍历区域表并初始化各种执行时区域。 功能如下:
. t' I/ x, `. f S. k7 Y0 V% G& d! ~( J9 i; B( f$ Z3 ]
Initializes the Zero Initialized (ZI) regions to zero M, I/ k; ]+ C5 z i$ j# i7 r
Copies or decompresses the non-root code and data region from their load-time locations to the execute-time regions.3 A% T6 f: @, C: n7 f: J! v
__main always calls this function during startup before calling __rt_entry .
5 u" d r5 [! B& G8 g
4 A. e: i" \7 e; R7 p__rt_entry' T" x# j8 C. h m
__main调用__rt_entry来初始化堆栈,堆和其他C库子系统。__rt_entry调用各种初始化函数,然后调用用户级main()。以下列出了_rt_entry可以调用的函数。 这些函数按它们被调用的顺序列出:7 I9 x5 x. T9 w, ?9 x6 h! T
! W% Y5 E6 t& {. m; J_platform_pre_stackheap_init7 l' ~% \, a+ M! T
__user_setup_stackheap or setup the Stack Pointer (SP) by another method
0 S* j0 N) i) ~7 ?- z# w_platform_post_stackheap_init2 I; Y8 ~' D& D4 g
__rt_lib_init
; J8 } f/ j% |3 ]_platform_post_lib_init
9 [ E9 v+ `" R9 l+ o+ p* mmain()6 x3 N. {. p- r K2 ^4 c5 o) N
exit()
6 v4 H# P8 k- Tplatform *函数不是标准C库的一部分。 如果你定义它们,则链接器会在__rt_entry中对它们进行调用。$ t& V; I( }' Z2 G8 z
main()是用户级应用程序的入口点。 寄存器r0和r1包含main()的参数。 如果main()返回,则将其返回值传递给exit()并退出应用程序。
q) O; s* G# h6 j% T__rt_entry还负责设置堆栈和堆。 但是,设置堆栈和堆取决于用户指定的方法。 可以通过以下任何方法设置堆栈和堆:* K" D. y ?* [ C0 b9 V( n6 I3 {( W2 B
, Q: W9 G- x: a" I
调用__user_setup_stackheap。 这也获得了堆使用的内存边界(堆顶部和堆基)。
: d9 S/ X2 U8 n5 r5 ~2 Q2 U3 o+ K% @使用符号__initial_sp的值加载 SP。
7 K% k/ b8 D9 c" b4 _9 X) DUsing the top of the ARM_LIB_STACK or ARM_LIB_STACKHEAP region specified in the linker scatter file.' Y2 U5 h8 t0 B( d% j
__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函数。: f9 ]$ G5 G% k" D. w7 i: T r4 Z
- _6 S% X. H1 u! J& N
_platform_pre_stackheap_init( \1 a# x* H- I( @" ^
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会在初始化堆栈和堆的代码之前调用此函数。* @' G& Y8 h/ Q, \' U
& T' n' J+ b# i. s4 d* f__user_setup_stackheap9 ?& P, Y' ]+ Z$ U
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函数的包装器。
% L ^- A. V& `9 w( R5 s* R
, Q& A" O2 l6 n$ `% s* p_platform_post_stackheap_init
2 I( O* f! ~* D$ G# ?5 U% F, g5 X' [ 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会在初始化堆栈和堆的代码之后调用此函数。
* Y8 D( l$ r3 @% S3 ^4 i+ D# X2 E+ v) V7 i- V
__rt_lib_init- g8 |9 u0 O# ]3 n" ?% \: C
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在启动期间始终调用此函数。
0 N% ~& v8 O/ _3 K0 D 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。
9 i8 Z* u7 Q- X! ?4 U" u The function returns argc and argv in registers r0 and r1 respectively if the user-level main() requires them. 如果用户级main()需要,该函数分别在寄存器 r0 和 r1 中返回argc和argv。
v1 _* h) F8 X: |( R5 ^/ N: R
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可以调用的函数。 这些函数按它们被调用的顺序列出:- z k. x& v! _( s
9 T2 o; A) c V! V4 f3 v; X1 r+ F
_fp_init2 O5 x: \( b0 z% X& X/ A
_init_alloc; Q7 G& A/ @ C, L5 f( Q6 N
_rand_init
. y/ a1 e9 q% M/ w5 l6 P_get_lc_collate0 X, l6 a1 F( F5 d
_get_lc_ctype
2 f/ O6 [0 l, a( __get_lc_monetary0 K3 K; b" y5 C! M& L% M
_get_lc_numeric, u6 e5 x$ {/ Q. W
_get_lc_time4 d, x! Z" K+ z7 g& s% p) m$ [7 o0 w
_atexit_init
3 e$ z7 T2 \5 V n. ^7 u_signal_init5 Y' V5 k' j* j) \/ F
_fp_trap_init; Y& f" y' ^+ p* x! {
_clock_init
! | y. `% T# z_getenv_init0 w( P3 m9 y# G3 h
_initio) h4 Y& O! t: ~7 F4 f1 U! k
_ARM_get_argv
" I4 I3 S5 C& I' Q9 g_alloca_initialize5 i* z8 C! ]' {% A4 q
_ARM_exceptions_init
( p" g+ C# F/ Z& @7 o7 ]" \ m& d; {) U_cpp_initialize__aeabi
S* P* m4 O# i$ m这里就不一一介绍各函数了,想进一步了解的去查看官方文档即可!
" B x" ~% [3 z
$ D2 n, e3 R3 ~_platform_post_lib_init1 \/ j7 X% h5 }! p/ W
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()函数之前,调用此函数。 |
|