进入 .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()
。
首先介绍一下背景。
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()
。