2

我正在使用 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 分支到正确的地址,一切都会正常工作?我是不是忘记了什么?

4

3 回答 3

4

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

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

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 的代码也很容易。解决所有这些问题。

于 2012-03-18T21:34:29.200 回答
3

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

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

于 2012-03-18T21:28:00.570 回答
2

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

许多引导加载程序将数据从只读存储器(例如闪存)复制到可写存储器(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.

此外,在堆栈溢出中搜索“嵌入式 C 写入内存”

于 2012-03-19T01:05:36.643 回答