CDK 生成的 BIN 为何包含 ELF 头¶
本文说明 CDK 在非默认链接地址下生成的 .bin 文件为何可能包含 ELF 头和对齐区域,并给出提取、验证纯二进制文件(raw BIN)以及生成 PAT 的规范流程。
背景¶
CRC 小程序的设计流程如下:
- SDK 固件位于 OTP/MTP 映射窗口。
- CRC 小程序下载到 SRAM 的
0x20007800。 - CPU 跳转到 CRC 小程序入口。
- CRC 小程序读取 SDK 数据并校验 CRC。
- 校验结果写入
0x20007AF0,完成后拉高 GPIO2。
推荐内存布局:
SDK 映射窗口:0x00000000 - 0x000077FF
CRC 程序区域:0x20007800 - 0x20007AEF
CRC 结果地址:0x20007AF0 - 0x20007AF3
保留区域: 0x20007AF4 - 0x20007BFF
CRC RAM/栈: 0x20007C00 - 0x20007FFF
对应 CDK 配置:
ROM1 使用 0x2F0,是为了保证链接器不会将代码或常量放到 CRC 结果地址 0x20007AF0。
问题现象¶
当 ROM1 起始地址为 0x20000000 时,CDK 生成的 .bin 看起来是普通裸二进制文件。
当 ROM1 起始地址改为 0x20007800 后,CDK 生成的 .bin 文件开头出现:
这是 ELF 固定魔数:
因此,文件虽然使用 .bin 扩展名,但内容不是从程序第一字节开始的纯 raw BIN。文件格式必须根据内容判断,不能只看扩展名。
在实际文件中观察到:
例如:
两段程序内容相同,只是 CDK 文件前面带有 ELF 头和对齐区域。
原因分析¶
链接器首先生成 ELF 文件。ELF 的加载段通常需要按固定边界对齐,并满足:
本项目程序地址为:
其低 12 位为:
当加载段按 0x1000 边界组织时,程序内容在文件中的偏移也会成为 0x800:
CDK 输出文件名曾显示:
对应关系为:
当程序链接到 0x20000000 时:
不需要 0x800 前置偏移,所以生成结果看起来像普通 raw BIN。
可使用 C-SKY readelf 查看实际段布局:
重点查看:
推荐方案¶
保持正确链接地址 0x20007800,使用 C-SKY objcopy 显式生成纯 raw BIN:
工具通常位于:
完整示例:
"<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 输入:
优先使用链接器生成的 .elf,因为它是权威输入文件。
自动转换脚本¶
项目提供配套脚本:下载 generate_raw_bin.bat
下载后,将脚本放到 CDK 工程根目录,即与 Obj 目录同级的位置。
双击运行时,脚本按以下顺序查找输入:
只检查上述两个目录,不递归检查其他目录。
候选文件优先级:
只要存在有效 .elf,无论 .bin 的时间是否更新,都优先使用 .elf。同一种扩展名存在多个文件时,选择最新文件。
脚本检查输入必须满足:
输出名称:
例如:
脚本先生成临时文件并验证,验证通过后再生成正式输出。转换失败时会删除临时文件和无效输出,避免后续误用旧 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 不能以以下字节开头:
读取首 word¶
本项目平台约定 raw BIN 第一个 word 为运行入口。入口必须位于 CRC 程序区域:
一个实际示例:
入口相对程序基址的偏移:
因此:
PAT 生成要求¶
推荐只使用 objcopy 生成的 raw BIN 制作 PAT。
三项必须一致:
例如 raw BIN 第一个 word 为 0x20007930 时:
不要再次给首 word 加 0x7800,因为该 word 已经是绝对地址。
下载后可以读取 SRAM 验证:
直接使用带 ELF 头的文件¶
理论上可以,但必须按整个文件映像的基地址下载。
以前述文件为例:
这样会额外写入:
该区域包含 ELF 头和填充数据,可能覆盖其他内容并增加下载时间,因此不推荐。
错误组合
不要将带 ELF 头的 CDK 文件按 0x20007800 作为起始地址直接下载。
以下组合是错误的:
因为实际程序会被放到:
但 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)
比较结果:
或:
只有两者完全一致时,才能确认手工裁剪正确。
不建议将手工删除固定 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。