就使用C代码进行嵌入式开发而言,我了解到,在编译和链接程序时,会生成在目标计算机上执行的二进制或ELF文件。 ELF文件将包含(以及很多其他东西)全局变量的地址或地址偏移量。
现在,如果要在整个程序中进行修改,那么当C启动代码首次执行时,它可以将非常量数据/变量从闪存复制到RAM中。
然后将更改系统上变量的内存地址吗?这样是否会更新ELF文件以更改该数据的地址?
答案 0 :(得分:2)
注意: 以下内容描述了独立(或裸机)嵌入式环境的行为,并不一定在所有环境中都普遍适用。例如,从大容量存储动态加载代码的托管系统差异很大-但差别不大,即使在这种情况下,该系统也不是有效的答案(例如,“地址未更改”)...
没有地址被“改变”。在具有从ROM运行代码的“典型”独立环境中(并非所有嵌入式系统都以这种方式组织,但这可能是您的问题所建议的),链接程序将所有符号定位在特定位置。如果这些位置在RAM中且具有 initial 值,则初始化数据-不是变量,将存储在ROM中并复制到RAM中启动-变量位置未更改-它们始终位于RAM中,而初始化数据始终位于ROM中。
您可以在链接图中看到链接器几乎可以生成的链接。
目标代码中的地址数据-无论是ELF,Intel Hex还是其他类型,仅在加载代码 时才有影响-原始二进制文件不包含地址信息-所有对象均由地址定位链接器,并由加载器(可以是Flash编程器,调试器或引导加载器)使用其中的地址信息放置在所需的位置。原始二进制文件根本没有地址信息,并且在加载它时必须指定加载地址,并且任何“间隙”都必须用填充填充。如果代码不是position independent(即所有地址引用都是相对的),则必须将原始二进制文件加载到正确的特定地址,否则它将无法按预期运行。
当调试器加载ELF文件时,它将二进制文件加载到目标,并将地址和符号信息读取到主机中运行的调试器中。地址和符号信息隐含在代码中,而未明确存在于目标中(除非您可能具有基于目标的调试工具,这些工具可能会利用此类信息)。
C运行时启动至少执行以下步骤:
对于从RAM运行代码的系统,还需要执行一个步骤,将代码从ROM复制到RAM(在某些情况下包括图像解压缩),如果运行时支持C ++,则将有一个调用构造函数的步骤。所有静态对象。通常,此运行时启动代码是由您的工具链提供的,您可能永远不会注意到它,但是几乎可以肯定,它将作为源代码(在汇编器和/或C中)供您使用,以供您自定义或看看它是如何工作的。
数据段和BSS段通常通常是单个连续块,零初始化只是将块存储器写入零,而数据初始化只是从ROM到RAM的块的存储器副本(可能需要解压缩)。
代码段-在ROM或RAM中称为 text 段。
因此,您有两个内存映射-包含文本,常量数据,初始化数据和引导程序或启动代码的图像内存映射(可ROMable),以及包含文本,常量数据,数据的运行时内存映射,BSS,堆栈和堆段。数据,BSS,堆栈和堆必须位于RAM中,文本和常量数据可能位于ROM或RAM中,具体取决于系统的运行时体系结构。
答案 1 :(得分:1)
现在,当C启动代码首次执行时,它可以复制非常量 如果要从闪存将数据/变量存入RAM 在整个程序中进行了修改。
然后将更改变量上的变量的内存地址。 系统?然后这样做是否更新ELF文件以更改地址 这些数据?
在整个程序代码中对变量的所有引用将使用RAM中.bss
和.data
部分的地址。存储在.rodata
中的用于初始化的ROM常数将具有其他一些地址,只有启动代码(“ CRT”)才需要关注。无论是ELF还是最终的二进制文件,其工作原理都相同。
有关更多信息,请参见EE网站上的这篇文章:What resides in the different memory types of a microcontroller?