C启动代码是否更改数据地址 [英] Does the C startup code change addresses of data

查看:45
本文介绍了C启动代码是否更改数据地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

就使用C代码进行嵌入式开发而言,我了解到,在编译和链接程序时,会生成在目标计算机上执行的二进制或ELF文件.ELF文件将包含(以及许多其他东西)全局变量的地址或地址偏移量.

现在,当要在整个启动程序中修改C启动代码时,它可以将非常量数据/变量从闪存复制到RAM中.

然后将更改系统上变量的内存地址吗?这样是否会更新ELF文件以更改此数据的地址?

解决方案

注意:以下内容描述了独立(或裸机)嵌入式环境的行为,并且不一定在所有环境中都普遍适用.例如,从大容量存储动态加载代码的托管系统差异很大-但差异不大,即使在那种情况下(也就是未更改地址"),它仍然不是有效的答案...


没有地址被改变".在具有从ROM运行代码的典型"独立环境中(并非所有嵌入式系统都以这种方式组织,但这可能是您的问题所建议的),链接程序将所有符号定位在特定位置.如果这些位置在RAM中且具有 initial 值,则初始化数据-不是变量,将存储在ROM中并复制到RAM处,启动-变量位置未更改-它们始终位于RAM中,而初始化数据始终位于ROM中.

您可以在链接图中看到链接器几乎可以生成的链接.

目标代码中的地址数据-无论是ELF,Intel Hex还是其他类型,仅在加载代码 时才有影响-原始二进制文件不包含地址信息-所有对象均由地址定位链接器,并由加载器(可以是Flash编程器,调试器或引导加载器)使用其中的地址信息放置在所需的位置.原始二进制文件根本没有地址信息,并且在加载它时必须指定加载地址,并且任何间隙"都必须用填充填充.如果代码不是 位置独立 (即所有地址)相对引用),则必须将原始二进制文件加载到正确的特定地址,否则它将无法按预期运行.

当调试器加载ELF文件时,它将二进制文件加载到目标,并将地址和符号信息读入在主机中运行的调试器中.地址和符号信息隐含在代码中,而没有明确地存在于目标中(除非您拥有可能使用此类信息的基于目标的调试工具).

C运行时启动至少执行以下步骤:

  • 堆栈指针初始化
  • 静态数据的零初始化,而无需显式的非零初始化(数据段)
  • 初始化显式初始化的非零静态数据(BSS段)
  • 运行时库初始化(例如堆初始化)
  • 跳到主要.

对于从RAM运行代码的系统,还需要执行一个步骤,将代码从ROM复制到RAM(在某些情况下包括图像解压缩),如果运行时支持C ++,则将有一个调用构造函数的步骤.所有静态对象.该运行时启动代码通常由您的工具链提供,您可能永远不会注意到它,但是几乎可以肯定,它将作为源代码(在汇编器和/或C语言中)供您使用,以供您自定义或看看它是如何工作的.

数据段和BSS段通常通常是单个连续块,零初始化只是将块存储器写入零,而数据初始化仅仅是从ROM到RAM的块的存储器副本(可能需要解压缩).

该代码段-在ROM或RAM中称为 text 段.

因此,您有两个内存映射-包含文本,常量数据,初始化数据和引导程序或启动代码的图像内存映射(可ROMable),以及包含文本,常量数据,数据的运行时内存映射,BSS,堆栈和堆段.数据,BSS,堆栈和堆必须位于RAM中,文本和常量数据可能位于ROM或RAM中,具体取决于系统的运行时体系结构.

引用: https://en.wikipedia.org/wiki/Data_segment

In terms of embedded development using C code, I understand that when a program is compiled and linked, a binary or ELF file is produced which executes on the target. The ELF file will contain (along with alot of other stuff ) the addresses or address offsets of global variables.

Now, when the C startup code executes first, it can copy non-const data / variables from flash memory into RAM if this data is to be modified throughout the program.

This will then change the memory addresses of the variables on the system? Does this then update the ELF file to change the address of this data?

解决方案

Note: the following describes the behaviour of a standalone (or bare metal) embedded environment, and is not necessarily universally true of all environments. Hosted systems that load code dynamically from mass storage for example differ significantly - but not so much that this does not remain a valid answer even in that case (i.e. No addresses are "changed") ...


No addresses are "changed". In a "typical" standalone environment with code running from ROM (not all embedded systems are organised that way, but that is perhaps suggested by your question), the linker locates all symbols at specific locations. If those locations are in RAM and have initial values, the initialisation data - not the variables, are stored in ROM and are copied to RAM at start up - the variable locations have not changed - they were always in RAM, and the initialisation data always in ROM.

You can see this in the link map that your linker is almost certainly capable of producing.

Address data in the object code - be it ELF, Intel Hex or whatever, only has a bearing when the code is loaded - the raw binary contains no address information - all objects are located by the linker and placed at the required location by the loader (be that a flash programmer, debugger or a bootloader) using the address information therein. A raw binary file has no address information at all, and you must specify the load address when you load it, and any "gaps" must be filled with padding. If the code is not position independent (i.e. all address references relative), then a raw binary must be loaded to the correct specific address or it will not run as intended.

When a debugger loads an ELF file, it loads the binary to the target and reads the address and symbolic information into the debugger running in the host. The address and symbol information is implicit in the code and not explicitly present in the target (unless you have target based debug facilities that make use of such information perhaps).

The C runtime start-up performs the following steps (at least):

  • Stack pointer initialisation
  • Zero initialisation of static data without an explicit non-zero initialisation (Data segment)
  • Initialisation of explicitly initialised non-zero static data (BSS segment)
  • Runtime library initialisation (such as heap initialisation)
  • Jump to main.

For systems running code from RAM, there would also be a step where that code is copied from ROM to RAM (in some cases including image decompression), and if the runtime supports C++, there will be a step that calls the constructors of all static objects. Often this run-time start-up code is provided by your toolchain, and you may never take any notice of it, but it will almost certainly be available to you as source code (in assembler and/or C) for you to customise or just see how it works.

The data and BSS segments are each normally a single contiguous block, the zero initialisation is simply a block memory write to zero, and the data initialisation is simply a memory copy (possibly with decompression) of a block from ROM to RAM.

The code segment - whether in ROM or RAM is called the text segment.

So you have two memory maps - the image memory map (which is ROMable) containing the text, constant data, initialisation data and bootstrap or start up code, and a run-time memory map containing the text, constant data, data, BSS, stack and heap segments. The data, BSS, stack and heap are necessarily in RAM, the text and constant data may be in ROM or RAM depending on the runtime architecture of the system.

Ref: https://en.wikipedia.org/wiki/Data_segment

这篇关于C启动代码是否更改数据地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆