跳转至

CDK 生成的 BIN 为何包含 ELF 头

阅读信息
阅读时间:9 分钟 字符数:4225 有效代码行数:94

本文说明 CDK 在非默认链接地址下生成的 .bin 文件为何可能包含 ELF 头和对齐区域,并给出提取、验证纯二进制文件(raw BIN)以及生成 PAT 的规范流程。

背景

CRC 小程序的设计流程如下:

  1. SDK 固件位于 OTP/MTP 映射窗口。
  2. CRC 小程序下载到 SRAM 的 0x20007800。
  3. CPU 跳转到 CRC 小程序入口。
  4. CRC 小程序读取 SDK 数据并校验 CRC。
  5. 校验结果写入 0x20007AF0,完成后拉高 GPIO2。

推荐内存布局:

SDK 映射窗口:0x00000000 - 0x000077FF
CRC 程序区域:0x20007800 - 0x20007AEF
CRC 结果地址:0x20007AF0 - 0x20007AF3
保留区域:    0x20007AF4 - 0x20007BFF
CRC RAM/栈:  0x20007C00 - 0x20007FFF

对应 CDK 配置:

ROM1 Start = 0x20007800
ROM1 Size  = 0x2F0
RAM1 Start = 0x20007C00
RAM1 Size  = 0x400

ROM1 使用 0x2F0,是为了保证链接器不会将代码或常量放到 CRC 结果地址 0x20007AF0。

问题现象

当 ROM1 起始地址为 0x20000000 时,CDK 生成的 .bin 看起来是普通裸二进制文件。

当 ROM1 起始地址改为 0x20007800 后,CDK 生成的 .bin 文件开头出现:

7F 45 4C 46

这是 ELF 固定魔数:

7F 'E' 'L' 'F'

因此,文件虽然使用 .bin 扩展名,但内容不是从程序第一字节开始的纯 raw BIN。文件格式必须根据内容判断,不能只看扩展名。

在实际文件中观察到:

CDK 文件偏移 0x000:ELF 头
CDK 文件偏移 0x800:CRC 程序实际内容
objcopy 输出偏移 0x000:CRC 程序实际内容

例如:

CDK 文件偏移 0x800:30 79 00 20 46 79 00 20 ...
raw BIN 偏移 0x000:30 79 00 20 46 79 00 20 ...

两段程序内容相同,只是 CDK 文件前面带有 ELF 头和对齐区域。

原因分析

链接器首先生成 ELF 文件。ELF 的加载段通常需要按固定边界对齐,并满足:

文件偏移 mod 段对齐值 = 运行地址 mod 段对齐值

本项目程序地址为:

0x20007800

其低 12 位为:

0x800

当加载段按 0x1000 边界组织时,程序内容在文件中的偏移也会成为 0x800:

0x20007800 mod 0x1000 = 0x800

CDK 输出文件名曾显示:

GC1377_0x20007000_0x095c.bin

对应关系为:

文件映像基地址:0x20007000
程序文件偏移:  0x800
程序实际地址:  0x20007000 + 0x800 = 0x20007800

当程序链接到 0x20000000 时:

0x20000000 mod 0x1000 = 0

不需要 0x800 前置偏移,所以生成结果看起来像普通 raw BIN。

可使用 C-SKY readelf 查看实际段布局:

csky-elfabiv2-readelf.exe -l input.elf
csky-elfabiv2-readelf.exe -S input.elf

重点查看:

LOAD
Offset
VirtAddr
PhysAddr
Align

推荐方案

保持正确链接地址 0x20007800,使用 C-SKY objcopy 显式生成纯 raw BIN:

csky-elfabiv2-objcopy.exe -O binary input.elf output_raw.bin

工具通常位于:

<CDK 安装目录>\CSKY\MinGW\csky-abiv2-elf-toolchain\bin\csky-elfabiv2-objcopy.exe

完整示例:

"<CDK 安装目录>\CSKY\MinGW\csky-abiv2-elf-toolchain\bin\csky-elfabiv2-objcopy.exe" -O binary "Obj\SN_Prog.elf" "Obj\SN_Prog_raw.bin"

如果 CDK 生成的 .bin 实际包含 ELF 魔数,也可以将该文件作为 objcopy 输入:

csky-elfabiv2-objcopy.exe -O binary SN_Prog.bin SN_Prog_raw.bin

优先使用链接器生成的 .elf,因为它是权威输入文件。

自动转换脚本

项目提供配套脚本:下载 generate_raw_bin.bat

下载后,将脚本放到 CDK 工程根目录,即与 Obj 目录同级的位置。

双击运行时,脚本按以下顺序查找输入:

1. BAT 所在目录\Obj
2. BAT 所在目录

只检查上述两个目录,不递归检查其他目录。

候选文件优先级:

1. .elf
2. 内容为 C-SKY ELF 的 .bin

只要存在有效 .elf,无论 .bin 的时间是否更新,都优先使用 .elf。同一种扩展名存在多个文件时,选择最新文件。

脚本检查输入必须满足:

ELF magic: 7F 45 4C 46
ELF class: ELF32
字节序:    Little-endian
e_machine: 0x00FC,C-SKY

输出名称:

<输入文件名>_raw.bin

例如:

输入:Obj\SN_Prog.elf
输出:Obj\SN_Prog_raw.bin

脚本先生成临时文件并验证,验证通过后再生成正式输出。转换失败时会删除临时文件和无效输出,避免后续误用旧 BIN。

验证 raw BIN

检查文件头

$path = "Obj\SN_Prog_raw.bin"
$b = [System.IO.File]::ReadAllBytes((Resolve-Path $path))

"Header = " + (
    ($b[0..15] | ForEach-Object { $_.ToString("X2") }) -join " "
)

raw BIN 不能以以下字节开头:

7F 45 4C 46

读取首 word

"Word0 = 0x{0:X8}" -f [BitConverter]::ToUInt32($b, 0)

本项目平台约定 raw BIN 第一个 word 为运行入口。入口必须位于 CRC 程序区域:

0x20007800 <= PC < 0x20007AF0

一个实际示例:

raw BIN 文件头:30 79 00 20
首 word:       0x20007930

入口相对程序基址的偏移:

0x20007930 - 0x20007800 = 0x130

因此:

raw BIN 偏移 0x130 -> SRAM 地址 0x20007930

PAT 生成要求

推荐只使用 objcopy 生成的 raw BIN 制作 PAT。

三项必须一致:

CRC 程序链接地址:      0x20007800
CRC PAT start_addr:    0x20007800
CRC run PAT pc_addr:   raw BIN 第一个 word

例如 raw BIN 第一个 word 为 0x20007930 时:

GC1379_CRC.pat start_addr = 0x20007800
GC1379_CRC_run.pat PC     = 0x20007930

不要再次给首 word 加 0x7800,因为该 word 已经是绝对地址。

下载后可以读取 SRAM 验证:

*(uint32_t *)0x20007800 == raw BIN 第一个 word
*(uint32_t *)PC         == raw BIN 中对应入口偏移处的指令 word

直接使用带 ELF 头的文件

理论上可以,但必须按整个文件映像的基地址下载。

以前述文件为例:

CDK 文件下载地址:0x20007000
程序文件偏移:    0x800
程序实际地址:    0x20007800
运行 PC:         0x20007930

这样会额外写入:

0x20007000 - 0x200077FF

该区域包含 ELF 头和填充数据,可能覆盖其他内容并增加下载时间,因此不推荐。

错误组合

不要将带 ELF 头的 CDK 文件按 0x20007800 作为起始地址直接下载。

以下组合是错误的:

输入文件:带 ELF 头的 CDK 文件
PAT start_addr:0x20007800

因为实际程序会被放到:

0x20007800 + 0x800 = 0x20008000

但 CPU 仍跳转到 0x20007930,程序无法运行。

手工裁剪的限制

对已经确认布局固定的特定文件,可以删除前 0x800 字节,并与 objcopy 输出比较。

$inputPath = "input_with_elf.bin"
$outputPath = "input_trimmed.bin"
$b = [System.IO.File]::ReadAllBytes((Resolve-Path $inputPath))
$trimmed = [byte[]]$b[0x800..($b.Length - 1)]
[System.IO.File]::WriteAllBytes($outputPath, $trimmed)

比较结果:

Get-FileHash "input_trimmed.bin", "input_raw.bin"

或:

fc.exe /b input_trimmed.bin input_raw.bin

只有两者完全一致时,才能确认手工裁剪正确。

不建议将手工删除固定 0x800 作为正式流程,因为:

  • 链接地址改变后偏移可能变化。
  • 链接器对齐参数可能变化。
  • 文件可能包含多个加载段。
  • 文件尾部可能包含符号表或调试信息。
  • .data 加载映像可能不与 .text 连续。

objcopy 会根据 ELF 元数据正确提取加载内容,应作为正式方案。

故障排查清单

BIN 格式

  • 输入 ELF 以 7F 45 4C 46 开头。
  • raw BIN 不以 7F 45 4C 46 开头。
  • raw BIN 首 word 位于 0x20007800 - 0x20007AEF。

链接与下载

  • ROM1 Start = 0x20007800。
  • ROM1 Size = 0x2F0。
  • RAM1 Start = 0x20007C00。
  • RAM1 Size = 0x400。
  • PAT start_addr = 0x20007800。
  • run PAT 的 PC 等于 raw BIN 首 word。

下载后验证

  • 0x20007800 的内容等于 raw BIN 首 word。
  • PC 对应地址的指令与 raw BIN 对应偏移一致。
  • SDK 零地址映射窗口内容正确。
  • CRC 成功后,0x20007AF0 等于 0x00005A5A。
  • CRC 程序结束后 GPIO2 拉高。

结论

CDK 在非默认、非整页对齐的 ROM 地址下,可能生成带 ELF 头和对齐区域的 .bin 文件。该文件包含正确程序内容,但程序不一定从文件偏移零开始。

本项目统一采用以下流程:

CDK 链接生成 ELF
        |
        v
csky-elfabiv2-objcopy -O binary
        |
        v
生成纯 raw BIN
        |
        v
使用 raw BIN 生成下载 PAT
        |
        v
PAT 从 0x20007800 下载,run PAT 跳转到 raw BIN 首 word

不要根据 .bin 扩展名判断文件一定是 raw binary;始终检查文件头和首 word。

相关内容