【问题标题】:Where do addresses in S-Record files come from?S-Record 文件中的地址来自哪里?
【发布时间】:2021-11-11 21:28:40
【问题描述】:

我正在为 ARM Cortex-M 微控制器开发一个独立的应用程序,在研究 S-Record 文件的结构时,我发现我对 S-Record 格式中地址的表示方式存在某种误解。

我在源代码中定义了一个变量,如下所示:

uint32_t g_ip_address = IP_ADDRESS(10, 1, 0, 56); // in LE: 0x3800010A

当我运行 objdump 时,我看到变量最终出现在地址 0x1ffe01c4 的 .data 部分中:

$ arm-none-eabi-objdump -t application.elf | grep g_ip_address
1ffe01c4 g     O .data  00000004 g_ip_address

这是有道理的,因为我的链接描述文件的内存部分看起来像这样并且 .data 将进入 RAM:

MEMORY
{
  FLASH (rx)         : ORIGIN = 0x00000000, LENGTH = 0x0200000  /*   2M */
  RAM (rwx)          : ORIGIN = 0x1FFE0000, LENGTH = 0x00A0000  /* 640K */   
}

但是,当我检查 srec 文件时,我发现记录的地址不是 0x1FFE0000。是 0x0005F570,好像放在了 FLASH 部分(为了清楚起见,加了空格)。

S315 0005F570 00000000 3800010A 000010180000000014

是否在不同的记录条目中编码了隐式偏移量? objcopy 是如何得到这个新地址的呢?如果这个值以某种方式被编码到一个函数中(可能是一些变量的预主初始化)?

最终,我的目标是能够解析 srec 文件并修补 IP 地址值以创建新的 srec 文件。做这样的事情的惯用方式是简单地创建一个结构来硬编码一些可以在文件中检测到的前导幻数序列吗?

【问题讨论】:

  • srecord 格式记录在维基百科中。使用 s3 记录完整地址在每一行中
  • 对于一个 mcu,二进制文件中不应该定义 ram,这将是您构建中的一个非常严重的错误。任何非零 ram 初始化都将在闪存空间中,然后在引导程序中复制到 ram。写一个简单的asm程序,不到10行就可以了,然后看看它是怎么落到图像中的。
  • 还可以查看链接描述文件的部分,答案就在那里
  • 你也可以使用 readelf 查看二进制文件以了解链接的结果
  • 哦,当然,我的思路已经走得太远了,以至于我忘了质疑我的基本假设。假设我有 elf 文件(没有反汇编 elf 文件并找到在 RAM 中设置相关地址的指令),有什么方法可以具体知道在 srec 文件中查找值的位置?

标签: linker arm gnu objcopy srecord


【解决方案1】:

flash.s

.cpu cortex-m0
.thumb

.word 0x00002000
.word reset

.thumb_func
reset:
    b reset
    
.data
.word 0x11223344

.bss
.word 0x00000000
.word 0x00000000

flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > rom
    .bss    : { *(.bss*)    } > ram AT > rom
    .data   : { *(.data*)   } > ram AT > rom
}

构建它

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy --srec-forceS3 so.elf -O srec so.srec
arm-none-eabi-objcopy -O binary so.elf so.bin

cat so.list

08000000 <reset-0x8>:
 8000000:   00002000    andeq   r2, r0, r0
 8000004:   08000009    stmdaeq r0, {r0, r3}

08000008 <reset>:
 8000008:   e7fe        b.n 8000008 <reset>

Disassembly of section .bss:

20000000 <.bss>:
    ...

Disassembly of section .data:

20000008 <.data>:
20000008:   11223344            ; <UNDEFINED> instruction: 0x11223344

cat so.srec

S00A0000736F2E7372656338
S30F080000000020000009000008FEE7D2
S3090800000A443322113A
S70508000000F2

arm-none-eabi-readelf -l so.elf

Elf file type is EXEC (Executable file)
Entry point 0x8000000
There are 3 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000094 0x08000000 0x08000000 0x0000a 0x0000a R E 0x2
  LOAD           0x000000 0x20000000 0x0800000a 0x00000 0x00008 RW  0x1
  LOAD           0x00009e 0x20000008 0x0800000a 0x00004 0x00004 RW  0x1

 Section to Segment mapping:
  Segment Sections...
   00     .text 
   01     .bss 
   02     .data 

hexdump -C so.bin

00000000  00 20 00 00 09 00 00 08  fe e7 44 33 22 11        |. ........D3".|
0000000e

bss 通常不会按原样公开,您可以使链接描述文件复杂化以添加起点和终点,以便您可以在引导程序中将该范围归零。对于 .data,您可以清楚地看到标准 binutils 工具发生了什么。

您没有提供足够的代码(和链接器脚本),也没有提供演示问题的最小示例,所以这大概是可以做到的。

【讨论】:

  • 好的,谢谢!这是我的理解:readelf 给出了 .data 段的物理地址和虚拟地址。通过从我的符号 (g_ip_address) 中减去虚拟地址,我得到一个偏移量,然后我可以将其应用于物理地址。物理地址是 srec 文件中显示的内容,因此我可以使用它来搜索正确的记录条目。
  • 在我的例子中,.data 的 VirtAddr 和 PhysAddr 分别是 0x1ffe01c0 和 0x0005f570。 g_ip_address 的 VirtAddr 是 0x1ffe01c4,偏移量为 4。这意味着 g_ip_address 的 PhysAddr 是 0x0005f574。然后正确的条目具有我显示的地址 0x0005f570 并且在我在该记录中找到的字节模式之前确实有 4 个字节预置。
  • 我不会对从另一个偏移量中减去一个偏移量做任何假设。从系统级别您了解为什么二进制文件不能在基于 ram 的可加载部分中包含 .data 是吗?单片机?
  • 如果您想知道链接器将某些东西放在哪里并且您可以访问精灵,那么在链接器脚本中创建一个变量并使用该变量(您可以使用 nm 等工具查看)。或者您可以将该变量以已知偏移量(在 .text 中)放在闪存中,然后使用硬编码偏移量,然后如果您想在编译后更改它,您可以以任何形式查找二进制文件。或在运行时控制其在 ram 中的偏移量/地址以进行更改
  • 如果你想做裸机,那么你需要了解 IMO 工具和启动过程的基础知识。 gnus 编译器可能是普通的,但 binutils 提供了许多工具,使生活更容易理解工具链的情况。
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 2018-12-22
  • 2010-09-26
  • 2023-03-28
  • 1970-01-01
  • 2015-01-17
  • 1970-01-01
相关资源
最近更新 更多