【问题标题】:Loading HEX data into memory将 HEX 数据加载到内存中
【发布时间】:2012-04-03 11:28:10
【问题描述】:

我正在使用 Codesourcerys GCC arm EABI 编译器为 Beagleboard (ARM Cortex A8) 编译裸机软件(无操作系统)。现在它编译成一个二进制或图像文件,我可以使用 U-Boot 引导加载程序加载。

问题是,我可以在运行时动态地将 hexdata 加载到内存中(这样我就可以将其他图像文件加载到内存中)吗?我可以使用 gcc objcopy 生成软件的 hexdump。我可以使用这些信息并将其加载到适当的地址吗? .text .data .bss 部分的所有地址是否会按照链接描述文件中的说明正确加载?

生成的hexdata输出
$(OBJCOPY) build/$(EXE).elf -O binary build/$(EXE).bin
od -t x4 build/$(EXE).bin > build/$(EXE).bin.hex

看起来像这样:

0000000 e321f0d3 e3a00000 e59f1078 e59f2078
0000020 e4810004 e1510002 3afffffc e59f006c
0000040 e3c0001f e321f0d2 e1a0d000 e2400a01
0000060 e321f0d1 e1a0d000 e2400a01 e321f0d7

...等等。

是否像将每行的 20 个字节加载到所需的内存地址一样简单,只需将 PC 分支到正确的地址就可以正常工作吗?我是不是忘记了什么?

【问题讨论】:

    标签: c embedded beagleboard


    【解决方案1】:

    当您使用 -O 二进制文件时,您几乎放弃了您的 .text、.data。 .bss 控制。例如,如果您在地址 0x10000000 处有一个字 0x12345678 调用该 .text,在 0x20000000、0xAABBCCDD 处有一个 .data 字,并且您使用 -O 二进制文件,您将获得一个 0x10000004 字节长度的文件,该文件以 0x12345678 开头并以 0xAABBCCDD 结尾并且有 0x0FFFFFFC 字节的零。尝试将其转储到芯片中,您可能会清除引导加载程序(uboot 等)或丢弃一堆寄存器等这样做。

    如果使用基于 rom 的引导加载程序,典型的做法是使用 gcc 工具

    MEMORY
    {
       bob : ORIGIN = 0x10000000, LENGTH = 16K
       ted : ORIGIN = 0x20000000, LENGTH = 16K
    }
    
    SECTIONS
    {
       .text : { *(.text*) } > bob
       .bss  : { *(.bss*) } > ted AT > bob
       .data : { *(.data*) } > ted AT > bob
    }
    

    代码 (.text) 将被链接,就好像 .bss 和 .data 位于内存中的正确位置 0x20000000,但字节由可执行文件(elf 加载程序或 -O 二进制文件等)加载到 .text 的末尾。通常,您使用更多的链接脚本魔术来确定链接器在哪里执行此操作。在启动时,您的 .text 代码应首先将 .bss 归零并将 .data 从 .text 空间复制到 .data 空间,然后您就可以正常运行了。

    uboot 可能可以处理 .bin 以外的格式,是吗?编写一个 elf 工具来提取二进制文件的不同部分并制作自己的 .bin 也很容易,而不是使用 objcopy。编写从不依赖 .bss 为零也没有 .data 的代码也很容易。解决所有这些问题。

    【讨论】:

    • 我有点期待,通过链接器脚本,我可以完全控制所有部分的位置。 Uboot 使用 load 命令为我加载这个文件。但是,我不知道如何在加载软件后在运行时加载另一个二进制文件的内容。正如我从您的回答中所理解的那样,仅读取二进制文件的所有内容并将其加载到特定地址是行不通的。猜猜我必须阅读一些精灵文档才能知道如何解析它! THx
    • 如果你想让你的程序加载其他程序,是的,你需要做一个加载器。我建议先intel hex 格式或者motorola srec,用objcopy -O ihex 或者-O srec,ascii 格式,容易解析,没有-O 二进制问题。 github.com/dwelch67 在模拟器中我可能有不止一个英特尔十六进制解析器(同样超级简单)。请注意,对于每种目标类型,长度和地址定义以及 ihex 的字节序变化,我想知道我为 amber_samples 做了什么,它基本上是 arm,可能是 ihex。
    • 谢谢,intel hex 格式解决了这个问题!只需要转换数据的字节序并将其解析到内存中!
    【解决方案2】:

    如果您可以在没有操作系统的情况下写入随机地址,那么使用一些随机的十六进制转储格式是没有意义的。只需将二进制数据直接加载到所需地址即可。即时从十六进制转换为二进制以存储在内存中不会为您带来任何好处。当然,您可以使用纯 read()fread() 将二进制数据加载到任何地址。

    如果您要加载完整的 ELF 文件(或类似文件),您当然需要实现特定格式期望从对象加载器中执行的任何任务,例如为 BSS 数据分配内存,可能解决任何未解析的地址代码(跳转等)等等。

    【讨论】:

      【解决方案3】:

      是的,可以在运行时写入内存(在嵌入式系统上)。

      许多引导加载程序将数据从只读存储器(例如闪存)复制到可写存储器 (SRAM),然后将执行转移到该地址。

      我曾在其他系统上工作过,这些系统可以将程序从端口(USB、SD 卡)下载到可写内存中,然后将执行转移到该位置。

      我编写了从串行端口下载数据并将其编程到闪存(和 EEPROM)设备中的函数。

      对于内存到内存的副本,使用memcpy 或自己编写,使用分配了物理地址的指针。

      要将数据从端口复制到内存,请弄清楚如何从设备(例如 UART)获取数据,然后通过指针将数据从其寄存器复制到您想要的位置。

      例子:

      #define UART_RECEIVE_REGISTER_ADDR (0x2000)
      //...
      volatile uint8_t * p_uart_receive_reg = (uint8_t*) UART_RECEIVE_REGISTER_ADDR;
      *my_memory_location = *p_uart_receive_reg; // Read device and put into memory.
      

      另外,在 Stack Overflow 中搜索“嵌入式 C 写入内存”

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多