进入 .elf 图像的最少代码量是多少

What is the minimum amount of code that gets into the .elf image

首先介绍一下背景。

ESP32 使用最新版本的 esp-idf 框架,无法将我的代码安装在默认分区的 1MB 大小限制下。我注意到一个简单的 WiFi 客户端(我的意思是非常简单,没有任何 HTTP 请求或服务器)只占用了大约 40% 的可用资源 space。

我想知道在 linking 时间 linker 将为任何给定的依赖项放置在输出图像文件中的最小代码块是多少。

它是文件级别 (xxx.o) 还是能够以某种方式变得更细化?

是否还有任何 link 开关可以列出任何给定 .o/.a 文件的所有引用? (即:他们依赖它的地方)

提前致谢

亚历克斯

链接器在 function/method 和变量级别上是精细的。如果未引用函数或方法或变量,则无需将其包含在链接的二进制文件中。

您可以使用 objdump 程序自行检查(您可能需要在您的系统上安装它;还有其他工具可以读取可能已经安装的 .elf 二进制文件)。

这是一个简单的程序。这是在 C++ 中使用 Arduino 框架,但它使用与 ESP-IDF 相同的工具链,因为它是基于它构建的。我包括 WiFi 只是给它一点分量。

#include <Arduino.h>

#include <WiFi.h>

void ignore_me() {
  Serial.println("this will never happen");
  Serial.println("this code won't even be linked in");
}

void setup() {
  WiFi.begin("foo", "bar");
}

void loop() {
}

构建完成后您可以运行

objdump --syms firmware.elf

查看符号列表。例如,我们可以这样找到 setup() 函数:

objdump --syms firmware.elf | grep setup

其中 returns:

40120cbc l     F .flash.text    00000032 setup_tcp
4012cd18 l     F .flash.text    00000013 __esp_stack_guard_setup
4010a2c8 g     F .flash.text    0000007d hostapd_setup_wpa_psk
400f8cb4 g     F .flash.text    0000032f esf_buf_setup
400d8f18 g     F .flash.text    0000008e ieee80211_setup_basic_htrates
400d9580 g     F .flash.text    000000da ieee80211_setup_phy_mode
4012edd4 g     F .flash.text    00000037 esp_setup_syscall_table
4008bb58 g     F .iram0.text    00000000 _frxt_setup_switch
400e02cc g     F .flash.text    00000141 ieee80211_send_setup
4013c320 g     F .flash.text    00000084 ieee80211_setup_lr_rates
400d8e4c g     F .flash.text    000000c6 ieee80211_setup_htrates
400d965c g     F .flash.text    00000134 ieee80211_setup_rates
40058cc8 g       *ABS*  00000000 __swsetup_r
400e2324 g     F .flash.text    00000101 ieee80211_setup_ratetable
400d04a8 g     F .flash.text    0000001c _Z5setupv
4013c3a4 g     F .flash.text    00000007 ieee80211_setup_rateset
40026c84 g       *ABS*  00000000 lmp_setup_cmp_handler

符号_Z5setupv对应设置函数。额外的字符是由于 C++ 添加了类型信息(例如,尾随 v 表示其参数是 void)。

运行

objdump --syms firmware.elf | grep ignore

将 return 什么都没有,因为函数 ignore_me() 未包含在链接的二进制文件中。

如果我们返回并修改 setup() 以再次调用 ignore_me() 和 运行 objdump,我们将看到如下内容:

400d04ac g     F .flash.text    0000001a _Z9ignore_mev

表示现在 ignore_me() 包含在二进制文件中,因为它正在被调用。

0000001a 是与符号关联的代码的大小。您可以使用 objdump 的输出来查找二进制文件中最大的函数;您也许可以重写它以消除其中的一些。 (剧透:printf() 及其亲戚通常都很大。)

您还可以将 objdump.a.o 文件一起使用。

ESP-IDF 文档中有一篇文章如何 minimize the binary size of an ESP-IDF application. You can list the size per-component 甚至每个文件,如果需要的话。

也就是说,如果您使用的是 C++,请尽量避免 iostream,它往往会占用很多 space。您可以改用 printf()