就使用 C 代码的嵌入式开发而言,我了解当编译和链接程序时,会生成在目标上执行的二进制或 ELF 文件。 ELF 文件将包含(以及许多其他内容)全局变量的地址或地址偏移量。
现在,当 C 启动代码首先执行时,如果要在整个程序中修改该数据,它可以将非常量数据/变量从闪存复制到 RAM 中。
这会改变系统上变量的内存地址吗?那么这是否会更新 ELF 文件以更改该数据的地址?
最佳答案
注意:以下内容描述了独立(或裸机)嵌入式环境的行为,并不一定适用于所有环境。例如,从大容量存储动态加载代码的托管系统差异很大 - 但即使在这种情况下,这也不是有效的答案(即没有“更改”地址)......
没有地址被“更改”。在从 ROM 运行代码的“典型”独立环境中(并非所有嵌入式系统都以这种方式组织,但这可能是您的问题所建议的),链接器将所有符号定位在特定位置。如果这些位置位于 RAM 中并且具有初始值,则初始化数据 - 不是变量将存储在 ROM 中并复制到 RAM启动 - 变量位置没有改变 - 它们总是在 RAM 中,初始化数据总是在 ROM 中。
您可以在您的链接器几乎肯定能够生成的链接映射中看到这一点。
目标代码中的地址数据 - 无论是 ELF、Intel Hex 还是其他什么,仅在代码加载时才起作用 - 原始二进制文件不包含地址信息 - 所有对象均由链接器并由加载器(闪存编程器、调试器或引导加载器)使用其中的地址信息将其放置在所需位置。原始二进制文件根本没有地址信息,加载时必须指定加载地址,任何“间隙”都必须用填充填充。如果代码不是 position independent (即所有相对的地址引用),那么原始二进制文件必须加载到正确的特定地址,否则它将无法按预期运行。
当调试器加载 ELF 文件时,它将二进制文件加载到目标并将地址和符号信息读取到主机中运行的调试器中。地址和符号信息隐含在代码中,并且不显式存在于目标中(除非您有基于目标的调试工具,可能会使用此类信息)。
C 运行时启动执行以下步骤(至少):
- 堆栈指针初始化
- 静态数据的零初始化,无需显式非零初始化(数据段)
- 显式初始化的非零静态数据(BSS 段)的初始化
- 运行时库初始化(例如堆初始化)
- 跳转到主程序。
对于从 RAM 运行代码的系统,还会有一个将代码从 ROM 复制到 RAM 的步骤(在某些情况下包括图像解压缩),如果运行时支持 C++,还会有一个调用构造函数的步骤所有静态对象。通常,此运行时启动代码是由您的工具链提供的,您可能永远不会注意到它,但几乎可以肯定它会作为源代码(汇编程序和/或 C 语言)提供给您,供您自定义或只需看看它是如何工作的。
数据和 BSS 段通常都是单个连续 block ,零初始化只是 block 存储器写入零,数据初始化只是 block 从 ROM 到 RAM 的存储器复制(可能需要解压缩)。
代码段 - 无论是在 ROM 还是 RAM 中都称为文本段。
因此,您有两个内存映射 - 包含文本、常量数据、初始化数据和 Bootstrap 或启动代码的镜像内存映射(可 ROMable),以及包含文本、常量数据、数据的运行时内存映射、BSS、堆栈和堆段。数据、BSS、堆栈和堆必然位于 RAM 中,文本和常量数据可能位于 ROM 或 RAM 中,具体取决于系统的运行时架构。
关于c - C启动代码是否改变数据地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58241883/