将 CRC 存储到 AXF/ELF 文件中

Posted

技术标签:

【中文标题】将 CRC 存储到 AXF/ELF 文件中【英文标题】:Storing CRC into an AXF/ELF file 【发布时间】:2014-07-31 17:59:21 【问题描述】:

我目前正在 Windows 7 上的 LPCXpresso (eclipse-based) 工具链中开发 C 程序,这是一个针对 NXP Cortex M3 微处理器的带有 gcc 的 IDE。它提供了一种通过 JTAG 对微处理器进行编译链接编程的简单方法。构建的结果是由调试配置加载的 AXF 文件(ELF 格式)。

加载的程序驻留在闪存中从 0x00000 到 0x3FFFB。我想在 0x3FFFC 包含一个 4 字节的 CRC-32,以在启动时验证程序。我添加了另一个部分并使用 gcc __attribute__ 指令访问该内存位置。

uint32_t crc32_build __attribute__ ((section(".text_MFlashCRC")));

为了计算和存储 CRC-32 值,我的计划是使用 SRecord 和以下构建后步骤:

arm-none-eabi-size "$BuildArtifactFileName"
arm-none-eabi-objcopy -O binary "$BuildArtifactFileName" "$BuildArtifactFileBaseName.bin"
checksum -p $TargetChip -d "$BuildArtifactFileBaseName.bin"
../util/srec_cat "$BuildArtifactFileBaseName.bin" -binary -crop 0 0x3FFFC -fill 0xFF 0x00000 0x3FFFC -crc32-b-e 0x3FFFC -o "$BuildArtifactFileBaseName.crc.bin" -binary
echo ""
echo "CRC32:"
../util/srec_cat "$BuildArtifactFileBaseName.crc.bin" -binary -crop 0x3FFFC 0x40000 -o - -hex-dump

这会创建一个带有校验和的二进制文件(引导加载程序所必需的),然后计算所用闪存的 CRC,将 CRC 值存储在 0x3FFFC。

但是,我认为我不能使用调试器加载二进制文件。有一个内置的 LPCXpresso 编程实用程序可以加载修改后的二进制文件,但是,它不允许我调试。我相信我可以尝试使用“仅附加”模式使用原始 AXF 文件启动调试会话,但是,这变得很麻烦。

我已经能够使用 readelf 检查 AXF 文件中的 crc32_build 变量。有没有办法编辑 AXF 文件中的变量?是否有行业标准的方法来插入 CRC 作为构建后步骤?

【问题讨论】:

【参考方案1】:

据我所知,没有行业标准。有多种技术可以做到这一点。我建议您在“C”中使用crc32_build 作为extern,并通过链接描述文件定义它。例如,

  $ cat ld.script
  .text : 
    _start_crc_region = .;
    *(.text);
    crc32_build = .;
    LONG(CALC_CRC);
    _end_crc_region = .;
  

您将值CALC_CRC 作为零传递给第一次调用,然后与设置的值重新链接。例如,

 $ ld --defsym=CALC_CRC=0 -T ld.script *.o -o phony.elf
 $ objcopy -j sections phony.elf -o phony.bin # sections means checksum 'areas'
 $ ld --defsym=CALC_CRC=`crc32 phony.bin` -T ld.script *.o -o target.elf

我使用这种技术为图像添加数字签名;它应该同样适用于 crc 值。链接描述文件允许您定位变量,这对于 CRC 等完整性检查通常很重要,但对于简单的校验和则无关紧要。链接描述文件还允许您为区域的开始和结束定义符号。没有脚本,你需要一些 elf 内省。

您当然可以扩展这个想法以包括 init data 和其他分配的部分。在某些时候,您需要使用objcopy 来提取部分并在构建时进行完整性检查。这些部分可能有各种对齐约束,您需要在进行构建时间 crc 计算时在主机上模拟这一点(在上面的 phony.bin 中)。

作为奖励,当您生成 srec 文件时,一切都已完成。

如果您对--defsym 有疑问,您可以使用 sedawk 预处理 ld.script >perlpython 等,并用 CALC_CRC 所在的十六进制值替换文本。

【讨论】:

这似乎是一个不错的方法。我必须弄清楚如何在对 LPCXpresso 工具链的破坏最小的情况下使用它,因为它已经自动生成链接器脚本。如果我理解这一点,我还需要修改 makefile 以将他们的ld 命令替换为定义 CALC_CRC 的命令? 是的,没错。你可能不得不改变 make 反正你需要做 两个 链接。第一个带有虚拟/零 CRC。第二个将实际填充 CRC。 ld -r(部分链接)也可能有用。但是,我使用了上面的方法,但我的问题是确定最终大小,因为它包含在证书中(实际上您与 _end_crc_region-_start_crc_region 相同。在计算构建时您可能需要减去最终的 crc 大小CRC;即不要在虚拟图像中包含零。 您可以将 __attribute section 用于 CRC 输入部分,但您几乎必须使用自定义链接器脚本并将 输入部分在某些输出部分(因为不存在标准)。因此,该方法将为您提供相同的最终解决方案(在我看来),您不妨在链接描述文件中定义空间。您的代码需要知道大小(在图像中编译);如果您对 CRC 调用代码进行 CRC 校验。或者,您可以填充到某个预先确定的最大尺寸,但这会浪费运行时间。 如果我也想将其扩展到 .data 部分,是否可以将 ld 脚本配置为将 CRC 值保留在 .data 的末尾?我在那里遇到了一个问题,因为它应该是一个 const,它通常是 .text 的一部分,所以在 .data 之前映射。是否有替代方法可以在所有 .text 上运行 CRC,跳过 CRC 位置,然后继续 .data? objcopy -j .text -j .data 将复制这两个部分。但是,运行时 CRC 将不得不 jump 指针,所以我只需存储两个 CRC 值并分别计算它们。我不认为你在一个价值中同时做这件事并没有多大收获? const 是一个“C”结构。对于链接器,.data 来自“initdata”,即初始数据值。此部分在启动时复制到最终的.data 地址。因此,您的数据 CRC 必须在任何更改任何“C”全局/静态数据之前运行;或者您可以为某些输入文件定义一些特殊的.data,而不是对其进行CRC。仅限 Asm 或小心 C。

以上是关于将 CRC 存储到 AXF/ELF 文件中的主要内容,如果未能解决你的问题,请参考以下文章

文件的 CRC 检查

CRC32+大小与 MD5/SHA1

校验和:CRC 还是哈希?

Protobuf 校验和 (crc)

redis集群

redis cluster 部署过程