c++ - 如何分析固件镜像的大小?

标签 c++ g++ embedded firmware

我们目前正在使用 ESP-IDF 框架基于流行的 ESP32 芯片为物联网产品开发固件,该框架使用名为 xtensa ( https://github.com/icamgo/xtensa-toolchain ) 的 GCC/G++ 工具链构建二进制文件.

最近,我注意到二进制文件的大小变得相当大(不到 1 MB),因此决定查看一下并尝试减小它。 NDEBUG 已定义,-Os 已启用,输出正在被stripped。

基本上,工具链会生成一个 .elf 文件,所以我看了一下它的内容:

nm -S -C --size-sort <my-app>.elf

六个最大的函数(大小为 6 kB-12 kB)是:

4011b24c 0000187b T __ssvfscanf_r
400f9f38 00001ffa T _svfiprintf_r
400f2aa4 000020fe T _vfiprintf_r
4012005c 000030d2 T _svfwprintf_r
400ef4d8 000030de T _svfprintf_r
400f50dc 000031e6 T _vfprintf_r

因此,我的固件镜像中最大的函数是 vfprintf 和它的 friend ,仅二进制文件大小就达 60 kB 左右。他们为什么这么大?我怎样才能减小它们的大小或摆脱它们(我根本不需要 vfprintf,因为我在微 Controller 上没有文件系统)?

是否有进一步的技术来减少二进制文件的大小?我将如何继续我的任务?

编辑(澄清优化原因):

ESP32 有不同版本,最高 16 MB 闪存。我们使用的那个有 4 MB。其中 1 MB 保留用于存储固定服务器证书、受信任的 URL 配置选项。而且,由于我们需要 OTA 更新功能,因此我们需要为新版本的应用程序镜像免费占用相同数量的闪存。这为我们的应用程序镜像留下了 1.5 MB 的闪存,这与我们当前的 1 MB 相差不远。因此,我认为在该问题完全阻止我们引入新功能之前考虑减小尺寸是合理的。

我确实意识到 60 kB 不需要的 vfprintf() 函数只是 1 MB 的一小部分,但我们确实需要很多实际有用的库(用于加密的 mbedtls、完整的 IP 堆栈、瘦网络服务器、. ..).我无法摆脱这些,所以我想通过删除我没有任何用处的函数来尽可能地和可行地减小大小

最佳答案

考虑单个函数的大小并不是一个好的方法。一个“微小”的函数在其调用图中可能有数百个同样微小的依赖关系,这些依赖关系合起来构成了一个巨大的 block 。例如以下内容:

int main()
{
    for(;;)
    {
        do_statemachine() ;
    }

    return 0 ;
}

main() 会很小,但最终会因为 do_statemachine() 所做的事情而导致 所有 应用程序的其余部分被链接,这可以是任何尺寸。您需要考虑函数的总大小及其所有依赖项。

同样存储在 ROM 中的静态或常量数据初始化器的总大小也需要考虑。

您应该使用链接器生成映射文件并调用图形 - 这可能比事后使用 nm 更有用。

关于您问题中的特定符号,您必须问自己在stdio中调用什么?例如 printf 需要流访问(对于 stdout)、格式说明符解析和可变参数遍历 - 所有vfprintf< 提供。如果不是这样,您就会有重复的代码,虽然您可能会链接更少的函数,但它们都将非常大并且可能表现出不同的行为。您在链接中具有面向"file"的功能这一事实并不是一个特别的问题; stdio 对流而不是文件进行操作 - "file"是概念性的,而不是物理的。如果您尚未将您的库连接到文件系统(或者如果工具中尚未提供),则不会包含任何文件系统代码。低级流访问由可能支持也可能不支持文件访问的低级 I/O 函数执行。

另一种可能性是库缺乏粒度——如果所有这些函数都定义在同一个目标模块中,链接器将别无选择,只能链接它们,即使它们没有被引用。这或许可以解释为什么链接中有整数、 float 和宽字符版本。

关于c++ - 如何分析固件镜像的大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46409467/

相关文章:

c++ - 将 Child 对象数组传递给接受 Parent* 的函数

c++ - 用其他预处理器命令包装#include 指令

c++ - 如何将矩阵的索引映射到一维数组(C++)?

c++ - 当警告为错误时使用 [[deprecated]] 属性 (-Werror)

c++ - 将 C++ 编译成 "real"程序

c++ - 读取不同数据类型的二进制数据

c - 如何强制IAR保持链接中断相关函数?

c++ - 打开多个文件,替代 fopen()

c++ - 对静态 constexpr char[] 的 undefined reference

c++ - 在使用 yacc 和 lex 的 CodeBlocks/Eclipse 中构建 C++ 项目