Eclipse CDT & STM32:强制预定义程序内存
Eclipse CDT & STM32: force predefined program memory
我有一个不常见但在我看来合理的用例:
我必须构建两个 STM32 固件映像:一个引导加载程序和一个应用程序(通过使用来自 ST Microelectronics 的基于 IDE 的最新 Eclipse CDT,称为:"STM32CubeIDE")。
因为我的约束主要是低功耗而不是安全性,所以我只要求 DFU(设备固件升级)场景的数据完整性,为此我对完整的固件映像实施了 CRC32 检查。棘手的部分是,固件本身在代码存储器中的固定偏移地址 0x200 处的 C-struct 内包含其实际大小(此设计的好处是,不必传输完整的代码存储器,但FW 始终受 CRC32 保护):
固件的布局是这样的:
<ISR Table> <FW-Header@FixedAddress0x200> <RestFWCode> " + CRC32
- FW Header 包含 FW 大小
- booloader 用来刷新应用程序的完整固件大小是存储的固件大小(参见 1.)+ 附加的 CRC32 的 4 个字节
对于我的实现,我需要将 "FW Header" 区域内的内存区域替换为实际固件大小(仅在构建过程后可用)。
为此,我制作了一个 python 脚本来修补二进制“*.bin”文件,但似乎 Eclipse/GDB 用于调试 ELF-file,它寻找与二进制图像相比,自定义补丁要复杂得多,因为我发现没有简单的方法来做到这一点(替换实际固件大小并附加 CRC32 的 4 个字节)。
因此,我认为最简单的方法是在固件从调试器加载程序后立即修补代码内存。
我成功地测试了来自 ST 的 command-line 工具,它甚至可以在代码内存中操作任意内存(我的代码内存从 0x08000000 开始 + 应用程序在偏移量 0x4000 和 FW header 在 öffset 0x200 -> 0x08004200):
ST-LINK_CLI.exe -c SWD -w32 0x08004200 0xAABBCCDD
(参见:https://www.st.com/resource/en/user_manual/cd00262073.pdf)
我的问题是,我不知道如何在调试器连接到 MCU 之前启动这个简单的 EXE 调用...我尝试了 "Debug Configuration"-> "Startup" -> "Run Commands",但没有成功...
有人知道如何实现这个吗?
运行在启动调试会话之前启动程序可以使用位于调试配置下的 Eclipse "Launch group" 来完成,例如顶部菜单 -> 运行 -> 调试配置。
然而,在这样做之前,您应该转到项目属性 -> 构建器并在那里添加您的程序调用 - 可执行文件的路径及其参数。确保未选中它,以便在构建项目时它不会 运行。然后您可以转到上述启动组并创建一个组,其中包含您在项目构建器部分中定义的程序,然后是您应该已经在列表中可用的常规调试会话。
我将推荐不同的工作流程来实现相同的图像格式:
<ISR Table> <FW-Header@FixedAddress0x200> <RestFWCode> " + CRC32
一旦到位,您的构建过程将是:
- 构建固件映像以生成原始二进制文件,不带 CRC-32 值
- 在原始二进制图像上计算 CRC-32,第三方工具
- 将计算出的 CRC-32 插入链接描述文件,重建 FW 映像
注意:对于 STM32CubeIDE,您需要拥有自己的 *.elf
项目,其中包括作为静态库的 STM32CubeMX 项目。否则,STM32CubeMX 每次生成新代码时都会覆盖您的链接描述文件。
由于每个项目的链接描述文件往往略有不同,我将使用 .test_crc
扇区进行演示。将以下内容插入您的 *.ld
链接描述文件:
.test_crc :
{
_image_start = .;
BYTE( 0x31)
BYTE( 0x32)
BYTE( 0x33)
BYTE( 0x34)
BYTE( 0x35)
BYTE( 0x36)
BYTE( 0x37)
BYTE( 0x38)
BYTE( 0x39)
/* FW Image Header */
LONG( _image_end - _image_start ) /* Size of FW image */
LONG( _image_start ) /* Base address to load FW image */
/* Place this at the end of the FW image */
_image_end = .;
_IMAGE_CRC = ABSOLUTE(0x0); /* Using CRC-32C (aka zip checksum) */
/*LONG( (-_IMAGE_CRC) - 1 ) /* Uncomment to append value, comment to calculate new value */
} >FLASH
在 STM32CubeIDE 中添加以下内容作为 post-构建步骤(生成原始二进制图像):
arm-none-eabi-objcopy -S -O binary -j .test_crc ${ProjName}.elf ${ProjName}.bin
现在您准备好 test/evaluate 过程:
- 重建项目以生成
*.bin
文件。
- 使用第三方工具计算 CRC-32 校验和。我使用 7-Zip 命令行界面为
*.bin
文件 生成 CRC-32C 值
- 附加计算出的 CRC-32C。在链接描述文件中,设置以下
_IMAGE_CRC = ABSOLUTE(0x0)
中的 0x0
以匹配您的计算值。取消注释以下内容:
LONG( (-_IMAGE_CRC) - 1 ) /* Uncomment to append value, comment to calculate new value */
- 重建您的映像和 运行 第三方 CRC 实用程序,它现在应该报告
0xFFFFFFFF
的 CRC-32C 值。
当您准备好将此应用于您的实际固件映像时,请执行以下操作:
- 更改 post-构建步骤以转储完整的二进制文件:
arm-none-eabi-objcopy -S -O binary ${ProjName}.elf ${ProjName}.bin
- 将
_image_start = .;
移到矢量 table 前面。
- 将以下内容移到您的矢量后 table:
/* FW Image Header */
LONG( _image_end - _image_start ) /* Size of FW image */
LONG( _image_start ) /* Base address to load FW image */
- 将以下内容移至最后一个扇区的末尾:
/* 把它放在固件镜像的末尾 */
_image_end = .;
_IMAGE_CRC = 绝对(0x0); /* 使用 CRC-32C(又名 zip 校验和)*/<br>
/*LONG( (-_IMAGE_CRC) - 1 ) /* 取消注释附加值,注释计算新值 */
您可能会发现其实并不需要图像内置的CRC值,只需将CRC值附加到*.bin
即可。然后将 *.bin
提供给您的引导加载程序。 *.bin
仍将包含 FW 映像的加载地址和大小,+4 字节用于附加的 CRC 值。
我有一个不常见但在我看来合理的用例: 我必须构建两个 STM32 固件映像:一个引导加载程序和一个应用程序(通过使用来自 ST Microelectronics 的基于 IDE 的最新 Eclipse CDT,称为:"STM32CubeIDE")。
因为我的约束主要是低功耗而不是安全性,所以我只要求 DFU(设备固件升级)场景的数据完整性,为此我对完整的固件映像实施了 CRC32 检查。棘手的部分是,固件本身在代码存储器中的固定偏移地址 0x200 处的 C-struct 内包含其实际大小(此设计的好处是,不必传输完整的代码存储器,但FW 始终受 CRC32 保护):
固件的布局是这样的:
<ISR Table> <FW-Header@FixedAddress0x200> <RestFWCode> " + CRC32
- FW Header 包含 FW 大小
- booloader 用来刷新应用程序的完整固件大小是存储的固件大小(参见 1.)+ 附加的 CRC32 的 4 个字节
对于我的实现,我需要将 "FW Header" 区域内的内存区域替换为实际固件大小(仅在构建过程后可用)。
为此,我制作了一个 python 脚本来修补二进制“*.bin”文件,但似乎 Eclipse/GDB 用于调试 ELF-file,它寻找与二进制图像相比,自定义补丁要复杂得多,因为我发现没有简单的方法来做到这一点(替换实际固件大小并附加 CRC32 的 4 个字节)。
因此,我认为最简单的方法是在固件从调试器加载程序后立即修补代码内存。 我成功地测试了来自 ST 的 command-line 工具,它甚至可以在代码内存中操作任意内存(我的代码内存从 0x08000000 开始 + 应用程序在偏移量 0x4000 和 FW header 在 öffset 0x200 -> 0x08004200):
ST-LINK_CLI.exe -c SWD -w32 0x08004200 0xAABBCCDD
(参见:https://www.st.com/resource/en/user_manual/cd00262073.pdf)
我的问题是,我不知道如何在调试器连接到 MCU 之前启动这个简单的 EXE 调用...我尝试了 "Debug Configuration"-> "Startup" -> "Run Commands",但没有成功...
有人知道如何实现这个吗?
运行在启动调试会话之前启动程序可以使用位于调试配置下的 Eclipse "Launch group" 来完成,例如顶部菜单 -> 运行 -> 调试配置。
然而,在这样做之前,您应该转到项目属性 -> 构建器并在那里添加您的程序调用 - 可执行文件的路径及其参数。确保未选中它,以便在构建项目时它不会 运行。然后您可以转到上述启动组并创建一个组,其中包含您在项目构建器部分中定义的程序,然后是您应该已经在列表中可用的常规调试会话。
我将推荐不同的工作流程来实现相同的图像格式:
<ISR Table> <FW-Header@FixedAddress0x200> <RestFWCode> " + CRC32
一旦到位,您的构建过程将是:
- 构建固件映像以生成原始二进制文件,不带 CRC-32 值
- 在原始二进制图像上计算 CRC-32,第三方工具
- 将计算出的 CRC-32 插入链接描述文件,重建 FW 映像
注意:对于 STM32CubeIDE,您需要拥有自己的 *.elf
项目,其中包括作为静态库的 STM32CubeMX 项目。否则,STM32CubeMX 每次生成新代码时都会覆盖您的链接描述文件。
由于每个项目的链接描述文件往往略有不同,我将使用 .test_crc
扇区进行演示。将以下内容插入您的 *.ld
链接描述文件:
.test_crc :
{
_image_start = .;
BYTE( 0x31)
BYTE( 0x32)
BYTE( 0x33)
BYTE( 0x34)
BYTE( 0x35)
BYTE( 0x36)
BYTE( 0x37)
BYTE( 0x38)
BYTE( 0x39)
/* FW Image Header */
LONG( _image_end - _image_start ) /* Size of FW image */
LONG( _image_start ) /* Base address to load FW image */
/* Place this at the end of the FW image */
_image_end = .;
_IMAGE_CRC = ABSOLUTE(0x0); /* Using CRC-32C (aka zip checksum) */
/*LONG( (-_IMAGE_CRC) - 1 ) /* Uncomment to append value, comment to calculate new value */
} >FLASH
在 STM32CubeIDE 中添加以下内容作为 post-构建步骤(生成原始二进制图像):
arm-none-eabi-objcopy -S -O binary -j .test_crc ${ProjName}.elf ${ProjName}.bin
现在您准备好 test/evaluate 过程:
- 重建项目以生成
*.bin
文件。 - 使用第三方工具计算 CRC-32 校验和。我使用 7-Zip 命令行界面为
*.bin
文件 生成 CRC-32C 值
- 附加计算出的 CRC-32C。在链接描述文件中,设置以下
_IMAGE_CRC = ABSOLUTE(0x0)
中的0x0
以匹配您的计算值。取消注释以下内容:
LONG( (-_IMAGE_CRC) - 1 ) /* Uncomment to append value, comment to calculate new value */
- 重建您的映像和 运行 第三方 CRC 实用程序,它现在应该报告
0xFFFFFFFF
的 CRC-32C 值。
当您准备好将此应用于您的实际固件映像时,请执行以下操作:
- 更改 post-构建步骤以转储完整的二进制文件:
arm-none-eabi-objcopy -S -O binary ${ProjName}.elf ${ProjName}.bin
- 将
_image_start = .;
移到矢量 table 前面。 - 将以下内容移到您的矢量后 table:
/* FW Image Header */
LONG( _image_end - _image_start ) /* Size of FW image */
LONG( _image_start ) /* Base address to load FW image */
- 将以下内容移至最后一个扇区的末尾:
/* 把它放在固件镜像的末尾 */
_image_end = .;
_IMAGE_CRC = 绝对(0x0); /* 使用 CRC-32C(又名 zip 校验和)*/<br>
/*LONG( (-_IMAGE_CRC) - 1 ) /* 取消注释附加值,注释计算新值 */
您可能会发现其实并不需要图像内置的CRC值,只需将CRC值附加到*.bin
即可。然后将 *.bin
提供给您的引导加载程序。 *.bin
仍将包含 FW 映像的加载地址和大小,+4 字节用于附加的 CRC 值。