ARM 编译器 C 库启动和初始化

1 介绍

本文档描述了 C 库启动代码和在使用 ARM 编译器编译的应用程序启动期间可能调用的初始化函数。该文档概述了启动代码中的功能的作用以及它们存在的原因。您可以使用此文档来验证您的应用程序的启动代码。

1.1 版本

本文档描述了 ARM 编译器的启动代码。启动代码中的函数可能会在工具链的不同版本和补丁之间发生变化。本文档不保证库启动代码在后续版本或工具链补丁中的持续运行。

1.2 补充阅读

本节列出了 ARM 和第三方的出版物。请参阅 Infocenter,http://infocenter.arm.com,以访问 ARM 文档。

1.2.1 ARM 出版物

以下文档包含与本文档相关的信息:

         • ARM 编译器工具链 ARM 处理器开发软件 (ARM DUI 0471)

         • ARM 编译器工具链 ARM C 和 C++ 库和浮点支持参考 (ARM DUI 0492)

         • 使用 ARM 的 ARM 编译器工具链C 和 C++ 库和浮点支持 (ARM DUI 0475)

        • ARM 编译器工具链链接器参考 (ARM DUI 0493)。

2 启动代码

嵌入式应用程序需要在用户定义的 main() 函数启动之前进行初始化序列。这称为启动代码或引导代码。 ARM C 库包含启动应用程序所需的预编译和预汇编代码部分。链接您的应用程序时,链接器会根据应用程序从 C 库中包含必要的代码,以便为应用程序创建自定义启动代码。

笔记:在目标上运行的嵌入式应用程序可以在调用 C 库启动代码之前执行其他目标硬件初始化。有关详细信息,请参阅为 ARM 处理器开发软件中的重置和初始化。

一个应用程序的启动代码可能与另一个应用程序的启动代码不同。该文档没有描述任何特定用户应用程序的精确启动代码。此外,该文档没有描述如何自己自定义启动代码。有关如何自定义启动代码的信息,请参阅为 ARM 处理器开发软件。本文档中描述的启动代码适用于标准 ARM C 库。它不适用于 ARM C 微库。这对于 ARMv4T 及更高版本的架构也很常见。

3 C 库的入口点

函数 __main 是 C 库的入口点。除非您更改它,否则 __main 是 ARM 链接器 (armlink) 在创建映像时使用的 ELF 映像的默认入口点。图 1 显示了 C 库启动期间 __main 调用的函数。

图 1 C 库启动时调用的函数概述

__rt_entry 和 __rt_entry 调用的函数在 __rt_entry 调用的函数中进行了描述。

3.1 __scatterload

应用程序代码和数据可以位于根区域或非根区域。根区域具有相同的加载时间和执行时间地址。非根区域具有不同的加载时间和执行时间地址。根区域包含 ARM 链接器输出的区域表。区域表包含需要初始化的非根代码和数据区域的地址。区域表还包含一个函数指针,该指针指示该区域需要什么初始化,例如复制、归零或解压缩函数。

__scatterload 遍历区域表并初始化各种执行时区域。功能:

        • 将零初始化(ZI) 区域初始化为零

        • 将非根代码和数据区域从加载时位置复制或解压缩到执行时区域。

__main 总是在启动期间调用此函数,然后再调用 __rt_entry。

3.2 参见

使用 ARM C 和 C++ 库和浮点支持:

        • 初始化执行环境和执行应用程序。

ARM C 和 C++ 库和浮点支持参考:

        • 线程安全的 C 库函数。

为 ARM 处理器开发软件:

        • 为您的目标硬件定制映像内存映射

        • 本地内存设置注意事项

        • 应用程序启动

        • 重置和初始化

        • 分散加载描述文件

        • 根区域。

4 __rt_entry 调用的函数

__main 调用 __rt_entry 来初始化栈、堆和其他 C 库子系统。 __rt_entry 调用各种初始化函数,然后调用用户级main()。

这列出了 _rt_entry 可以调用的函数。这些函数按它们被调用的顺序列出:

        1. _platform_pre_stackheap_init

        2. __user_setup_stackheap or setup the Stack Pointer (SP) by another method

        3. _platform_post_stackheap_init

        4. __rt_lib_init

        5. _platform_post_lib_init

        6. main()

        7. exit()

_platform_* 函数不是标准 C 库的一部分。如果您定义了它们,那么链接器会在 __rt_entry 中调用它们。

main() 是用户级应用程序的入口点。寄存器 r0 和 r1 包含 main() 的参数。如果 main() 返回,则将其返回值传递给 exit() 并退出应用程序。

__rt_entry 还负责设置栈和堆。但是,设置堆栈和堆取决于用户指定的方法。堆栈和堆可以通过以下任何一种方法设置:

        • 调用__user_setup_stackheap。这也获得了堆使用的内存边界(堆顶和堆底)。

        • 使用符号__initial_sp 的值加载SP。

        • 使用链接器分散文件中指定的ARM_LIB_STACK 或ARM_LIB_STACKHEAP 区域的顶部。

__rt_entry 和 __rt_lib_init 在 C 库中不作为完整函数存在。这些函数的一小部分存在于作为 C 库一部分的几个内部对象中。并非所有这些代码段都对给定的用户应用程序有用。链接器决定给定应用程序需要这些代码段的哪个子集,并仅在启动代码中包含这些段。链接器以正确的顺序放置这些部分,以根据用户应用程序的要求创建自定义的 __rt_entry 和 __rt_lib_init 函数。

__rt_lib_init 调用的函数在 __rt_lib_init 调用的函数中进行了描述。

4.1 _platform_pre_stackheap_init

标准 C 库不提供此功能,但您可以根据需要定义它。例如,您可以使用此功能设置硬件。 __rt_entry 在初始化堆栈和堆的代码之前调用此函数(如果您定义它)​​。

4.2 __user_setup_stackheap

此函数使您能够设置和返回初始堆栈和堆的位置。 C 库不提供此功能,但您可以根据需要定义它。 __rt_entry 如果您定义它或定义旧函数 __user_initial_stackheap,则调用此函数。如果定义 __user_initial_stackheap,则 C 库提供默认的 __user_setup_stackheap 作为 __user_initial_stackheap 函数的包装器。

4.3 _platform_post_stackheap_init

C 库不提供此功能,但您可以根据需要定义它。例如,您可以使用此功能设置硬件。 __rt_entry 在初始化堆栈和堆的代码之后调用此函数(如果您定义它)​​。

4.4 __rt_lib_init

此函数初始化各种 C 库子系统。它初始化引用的库函数,初始化语言环境,并在必要时为 main() 设置 argc 和 argv。 __rt_entry 总是在启动期间调用此函数。

如果使用 __user_setup_stackheap 或 __user_initial_stackheap 函数来设置堆栈指针和堆,则堆内存块的起始地址和结束地址分别作为参数传递给寄存器 r0 和 r1 中的 __rt_lib_init。

如果用户级 main() 需要,该函数分别在寄存器 r0 和 r1 中返回 argc 和 argv。

4.5 _platform_post_lib_init

C 库不提供此功能,但您可以根据需要定义它。例如,您可以使用此功能设置硬件。 __rt_entry 在调用 __rt_lib_init 之后和调用用户级 main() 函数之前调用此函数(如果您定义它)​​。

4.6 参见

C 库的入口点

为 ARM 处理器开发软件:

        • 复位和初始化

        • 应用程序启动

        • 堆栈指针初始化

        • 放置堆栈和堆。

ARM C 和 C++ 库和浮点支持参考:

        • __rt_entry

        • __user_setup_stackeheap()

        • __rt_stackheap_init()

        • __rt_lib_init()

        • __rt_lib_shutdown()

        • _sys_exit()

        • 旧函数__user_initial_stackheap()。

使用 ARM C 和 C++ 库和浮点支持:

        • 堆栈指针初始化和堆边界

        • 执行环境的初始化和应用程序的执行

        • 对 __user_initial_stackheap() 的传统支持。

5 __rt_lib_init 调用的函数

链接器包括来自内部目标文件的各种初始化代码部分,以创建自定义 __rt_lib_int 函数。仅当应用程序需要时,链接器才会在 __rt_lib_init 中放置一个函数。

这列出了 _rt_lib_init 可以调用的函数。这些函数按它们被调用的顺序列出:

        1. _fp_init

        2. _init_alloc

        3. _rand_init

        4. _get_lc_collate

        5. _get_lc_ctype

        6. _get_lc_monetary

        7. _get_lc_numeric

        8. _get_lc_time

        9. _atexit_init

        10. _signal_init

        11. _fp_trap_init

        12. _clock_init

        13. _getenv_init

        14. _initio

        15. _ARM_get_argv

        16. _alloca_initialize

        17. _ARM_exceptions_init

        18. __cpp_initialize__aeabi_

5.1 _fp_init

该函数通过设置 FP 状态字来初始化浮点环境。如果用户应用程序使用 VFP 硬件,该函数会初始化浮点状态和控制寄存器 (FPSCR)。如果应用程序使用软件 VFP,该函数会初始化内存中的 FP 状态字。 __rt_lib_init 总是在启动期间调用此函数。

如何调用 fp_init 取决于 ARM 编译器版本:

        • ARM 编译器 v4.1

                fp_init 始终在启动期间调用。

        • ARM Compiler 5

                _fp_init 在启动期间被调用,除非您同时使用softfp 和不带状态字的FP 模型(--fpmode={ieee_no_fenv,std,fast})。在这种情况下,完全省略了对 _fp_init 的调用。

5.2 _init_alloc

此函数设置 malloc、free 和其他相关函数使用的数据结构。该函数有 2 个参数。寄存器 r0 中的第一个参数是堆内存块(heapbase)的开始,寄存器 r1 中的第二个参数是堆内存块(heaptop)的结束。如果这些堆绑定参数未作为参数传递给 __rt_lib_init,则 __rt_lib_init 使用符号 __heap_base 和 __heap_limit 或特殊的分散加载区域加载它们,请参阅 __rt_entry 调用的函数。如果应用程序使用堆,__rt_lib_init 调用此函数。

5.3 _rand_init

此函数将随机数生成器初始化为其默认起始状态。如果应用程序使用 rand(),__rt_lib_init 调用此函数。

5.4 _get_lc_collate

此函数获取指向包含 LC_COLLATE 语言环境类别设置的默认数据块的指针。它将指针插入到 C 库存储的语言环境指针变量中。如果应用程序调用其行为取决于此区域设置的任何函数,__rt_lib_init 将调用此函数。

5.5 _get_lc_ctype

此函数获取指向包含 LC_CTYPE 语言环境类别设置的默认数据块的指针。它将指针插入到 C 库存储的语言环境指针变量中。如果应用程序调用其行为取决于此语言环境设置的任何函数,__rt_lib_init 将调用此函数。

5.6 _get_lc_monetary

此函数获取指向包含 LC_MONETARY 语言环境类别设置的默认数据块的指针。它将指针插入到 C 库存储的语言环境指针变量中。如果应用程序调用其行为取决于此区域设置的任何函数,__rt_lib_init 将调用此函数。

5.7 _get_lc_numeric

此函数获取指向包含 LC_NUMERIC 语言环境类别设置的默认数据块的指针。它将指针插入到 C 库存储的语言环境指针变量中。如果应用程序调用其行为取决于此语言环境设置的任何函数,__rt_lib_init 将调用此函数。

5.8 _get_lc_time

此函数获取指向包含 LC_TIME 语言环境类别设置的默认数据块的指针。它将指针插入到 C 库存储的语言环境指针变量中。如果应用程序调用其行为取决于此语言环境设置的任何函数,__rt_lib_init 将调用此函数。

5.9 _atexit_init

此函数为传递给 atexit() 的函数指针设置 C 库的存储。在多线程应用程序中,它还设置了互斥锁以保护存储免受并发访问。如果应用程序使用 atexit(),__rt_lib_init 调用此函数。

5.10 _signal_init

此函数设置包含每个信号编号的当前处理程序的存储。在多线程应用程序中,它还设置了互斥锁以保护此存储免受并发访问。如果应用程序使用 signal(),__rt_lib_init 调用此函数。

5.11 _fp_trap_init

此函数设置库的存储,其中包含每种浮点异常的当前处理程序。在多线程应用程序中,它还设置了互斥锁以保护此存储免受并发访问。如果应用程序使用捕获的浮点异常,__rt_lib_init 将调用此函数,例如,如果您使用以下任何一种:

        • --fpmode=ieee_full

        • --fpmpde=ieee_fixed。

5.12 _clock_ini

该函数读取clock() 使用的定时器的当前值。这被存储为程序的开始时间。随后对clock() 的调用返回自程序开始时间以来经过的时间。这些是clock() 和_clock_init 的默认实现。您可以以不同的方式重新实现它们。如果应用程序使用clock(),__rt_lib_init 调用这个函数。

5.13 _getenv_init

标准 C 库不提供此功能,但您可以根据需要定义它。此函数使 getenv() 能够检索任何需要的数据。 __rt_lib_init 如果您定义它,则调用此函数。

5.14 _initio

此函数设置 stdio 内部状态。这包括初始化打开文件列表,调用 _sys_open() 打开三个标准流。如果应用程序使用 stdio,__rt_lib_init 会调用此函数。

5.15 __ARM_get_argv

此函数获取传递给 main() 的 argc 和 argv 值。该函数分别在寄存器 r0 和 r1 中返回 argc 和 argv。该函数可能会在寄存器 r2 和 r3 中返回另外两个参数。如果 main() 用参数声明,__rt_lib_init 调用此函数。

__ARM_get_argv 调用 _sys_command_string 以获取作为单个字符串的参数列表。然后它将这个字符串分解为每个单词的单独字符串。

5.16 __alloca_initialize

此函数将 alloca 列表指针设置为 NULL。如果使用基于 RVCT 堆的分配,__rt_lib_init 将调用此函数。

5.17 __ARM_exceptions_init

此函数设置 C++ 异常处理状态。如果应用程序使用 C++ 异常,__rt_lib_init 会调用此函数。

5.18 __cpp_initialize__aeabi_

此函数调用顶级 C++ 对象的构造函数。如果应用程序具有顶级 C++ 对象,__rt_lib_init 将调用此函数。

5.19 参见

__rt_entry 调用的函数

使用 ARM C 和 C++ 库和浮点支持:

        • __rt_fp_status_addr()

        • 在利用 C 库时使用 malloc()

        • 从裸机 C 使用堆实现

        • C 库中语言环境数据块的定义

        • C++ 初始化、构造和销毁

        • 执行环境的初始化和应用程序的执行

        • 异常系统初始化

        • 在C 库中定制语言环境函数的汇编器宏。

ARM C 和 C++ 库和浮点支持参考:

        • _getenv_init()

        • getenv()

        • _clock_init()

        • _findlocale()

        • _sys_command_string()

        • __rt_lib_init。

链接器参考:

        • --ref_cpp_init, --no_ref_cpp_init。

6 附录

本附录提供了在应用程序启动期间可能调用的各种函数的摘要。它还显示该函数何时包含在启动代码中。

表1 启动功能汇总

符号名称

描述

包含在启动代码中

__alloca_initialize

将 alloca 列表指针设置为 NULL

使用基于堆的 alloca 时

__ARM_exceptions_init

设置异常处理状态

使用 C++ 异常时

__ARM_get_argv

获取 main() 的 argc 和 argv 值

如果 main() 使用参数定义

_atexit_init

为函数指针设置存储

使用 atexit() 时

_clock_init

读取由 clock() 使用的计时器的当前值

使用 clock() 时

__cpp_initialize__aeabi_

调用顶级 C++ 构造函数

当使用顶级 C++ 对象时

_fp_init

初始化浮点环境

始终

_fp_trap_init

为浮点异常处理程序设置存储

使用捕获的浮点异常时

_get_lc_collat​​e

存储指向包含 LC_COLLATE 设置的数据块的指针

当使用依赖于整理区域设置的函数时

_get_lc_ctype

存储指向包含 LC_CTYPE 的数据块的指针settings

使用依赖于 ctype 的功能时区域设置

_get_lc_monetary

存储指向包含 LC_MONETARY 设置的数据块的指针

当使用依赖于货币区域设置的函数时

_get_lc_numeric

存储指向包含 LC_NUMERIC 设置的数据块的指针

当使用依赖于数字区域设置的函数时

_get_lc_time

存储指向包含 LC_TIME 设置的数据块的指针

使用依赖于时间区域设置的函数时

_getenv_init

使 getenv() 能够自行初始化

如果定义 _getenv_init

_init_alloc

设置 malloc、free 和其他相关函数使用的数据结构

使用堆时

_initio

设置 stdio 内部状态

使用 stdio 时

_rand_init

初始化随机数生成器

使用 rand() 时

_platform_post_lib_init

在 __rt_lib_init 之后启用初始化

如果您定义 _platform_post_lib_init

_platform_post_stackheap_init

在堆栈初始化后启用初始化

如果您定义 _platform_post_stackheap_init

_platform_pre_stackheap_init

在堆栈初始化之前启用初始化

如果定义 _platform_pre_stackheap_init

__rt_entry

设置运行时环境,然后调用 main() 始终 __rt_lib_init 调用必要的 C 库初始化函数

始终

__scatterload

将代码和数据从加载区域复制到执行区域

始终

_signal_init

为信号处理程序设置存储

使用 signal() 时

_user_setup_stackheap

设置堆栈和堆

如果您定义 _user_setup_stackheap

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值