尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

第 5 章 构建与固件:代码如何变成可执行的二进制

发布时间:2026/9/24 18:01:47

资讯中心
01
ARTICLE

第 5 章 构建与固件:代码如何变成可执行的二进制

第 5 章 构建与固件:代码如何变成可执行的二进制
本章从底层讲清编译四阶段的每一步到底做什么、优化等级在改什么、.elf 的各段text/data/bss/rodata如何装载、固件体积是怎么算出来的。最后用本书项目的真实数字做一次完整核对。5.1 四阶段流水线每个阶段做什么① 预处理preprocess: 处理 #include、#define 宏 → 展开后的 .i 文件 ② 编译compile: C → 汇编芯片指令的可读版 ③ 汇编assemble: 汇编 → 机器码.o 目标文件 ④ 链接link: 多个 .o → 分配地址 → .elf① 预处理文本替换#include x.h把 x.h 全文贴进来#define FOO 10把代码里所有 FOO 换成 10条件编译#ifdef按条件保留/删除代码段。宏macro正式定义用#define 名字 替换文本给一段文本起的“别名”预处理阶段做纯文本替换2.1 表格里展开宏说的就是它编译器真正开工前替换已经完成所以宏不做类型检查。头文件找不到 这一阶段报错No such file。② 编译人话 → 芯片指令编译器把 C 翻译成汇编指令如movi a2, 100 把 100 放进寄存器。语法错误在这一步报error: expected ...。③ 汇编汇编 → 机器码每条汇编对应一个二进制指令字如0x221F22。产物是.o文件里面有代码段、数据段、以及符号表记录了这个文件定义了哪些名字、需要哪些名字。符号 每个函数/全局变量在编译器和链接器眼里的名字声明 vs 定义这对词的分工5.6 专门讲。④ 链接对符号 排地址链接器读所有 .o 的符号表把需要的名字undefined和定义的名字对上把各段拼到一起按链接脚本分配到内存地址生成.elf含符号和调试信息。undefined reference 需要某个名字但所有 .o 里都没定义第 3 章。5.2 优化等级编译器在动什么手脚优化 编译器改写你的代码让结果更好更小/更快但语义不变。常用手段理解即可常量折叠x 2 * 100;直接算成 200死代码删除if (false) {...}整个删掉内联小函数直接展开调用处省掉压栈跳转公共子表达式重复计算只算一次循环展开减少循环开销。选项编译器动脑程度效果调试友好度-O0不优化固件最大、执行最直白最好-Og只为调试优化兼顾好-Os目标体积最小固件最小本书用一般-O2目标速度最快快但大差⚠️ 优化会重排/删除代码导致调试时变量看不到、断点对不上、崩溃回溯不准。排查 bug 用 -Og发布再换 -Os第 11 章展开。在哪切换v6.0.1 实测路径idf.py menuconfig→ 主菜单最上层Compiler options→Optimization Level→ 选Optimize for size (-Os with GCC, -Oz with Clang)按 S 保存。本书工程的 sdkconfig 里那行CONFIG_COMPILER_OPTIMIZATION_SIZEy就是这么来的。⚠️ 改完优化等级请idf.py fullclean再 build——增量编译可能让一部分文件按旧等级、一部分按新等级混合编译体积和现象都对不上账。5.3 C 标准用哪年的词典-stdgnu26告诉编译器用最新的 C26 标准GNU 扩展。本书代码依赖的新词写法最低标准作用std::clampC17数值钳位指定初始化DCMotor motor({.in1 MOTOR_IN1, .freq_hz 20000});C20按名字挑字段填没写的字段自动用默认值constexprC11编译期常量结构化绑定等C17解包编译错clamp is not a member of std→ 标准太低调高。去哪调不是 menuconfig它管优化等级不管 C 标准而是在出错那个组件的CMakeLists.txt 里加本节末尾那句target_compile_options(${COMPONENT_LIB} PRIVATE -stdgnu20)3.3 逐字块的注释里就有这句模板。本书工程不用调——IDF 已自动探测到 gnu26够新。回收 3.2 埋的伏笔这个-stdgnu26不是你写的——ESP-IDF 的project.cmake会按gnu26 gnu2b gnu20 …的顺序挨个问工具链这个你支持吗用第一个支持的并追加到全局编译选项里。所以根 CMakeLists 里设CMAKE_CXX_STANDARD才会无效全局选项里后出现的-std覆盖它。真要给某一个组件钉死标准用官方注释里那句话target_compile_options(${COMPONENT_LIB} PRIVATE -stdgnu20)。5.4 .elf 的各段程序和数据怎么摆放链接脚本把程序分成若干段section各自去向段装什么都是全局/静态变量局部变量在栈上去向运行时占 Flash.text指令代码直接从 Flash 执行是.rodata只读常量字符串、查表Flash是.data**有初值非 0**的变量初值存 Flash启动时复制到 SRAM是初值.bss无初值或初值为 0的变量启动时清零否启动时发生了什么crt0 初始化芯片复位后、app_main前启动代码crt0要做把 .data 的初值从 Flash 复制到 SRAM否则变量没有正确初值把 .bss 区清零否则变量是垃圾值设置栈指针、堆的边界才调用app_main()。这里说的有无初值指的是全局变量和静态变量全局int x 5;的 5 会占 Flash初值要存在 .data 里启动时搬去 SRAM而全局int y;隐含初值 0不占 Flash、只在 SRAM 占位.bss启动清零。本书真实例子motor_control.cpp顶部的static bool s_ledc_fade_installed false;——静态变量、初值就是 0进的是 .bss。⚠️ 写在函数里面的int x 5;是局部变量住在栈上1.7 节运行时每次进函数现场压栈跟 .data/.bss 都没关系。中断服务函数ISR为什么常用IRAM_ATTR放进指令 RAM、不在 Flash里执行也和指令从哪来有关——见第 15 章。5.5 固件体积怎么算、真实验证烧录报告怎么读l298n.bin binary size 0x2a150 bytes. Smallest app partition is 0x100000 bytes. 0xd5eb0 bytes (84%) free.0x2a150172368 字节bin 文件大小。换算是笨办法但最可靠0x2a150 2×16⁴ 10×16³ 1×16² 5×16 0 131072 40960 256 80 0 1723680xd5eb0 factory 分区剩余 876208 字节 → 剩余84%自检172368 876208 1048576 0x1000001MB正是第 3 章的factory 分区总大小。三个数互相咬合谁也别想记错。bin 大小 ≈ text rodata data 的初值bin 里装的几乎全是要在 Flash 里待着的东西指令、常量、初值不含 bssbss 运行时在 SRAM 清零。所以bin ≈ .text .rodata .data各段合计本书当前发布版的真实值构建后从.elf的分段统计得到idf.py size输出的 Memory Type 归堆表见 5.7 末尾动手框两套口径的关系那里讲text data bss dec hex 118639 53598 362939 535176 82a88text 118639 data 53598 172237与 172368 差的 131 字节去了哪bin 文件开头还有一段镜像头、各段之间也有对齐填充——≈就是这点零头的意思第 3 章 factory 分区那段讲的镜像校验同理✅bss 362939 ≈ 354KB 很吓人别怕里面含系统给 malloc 预留的“heap 池”声明了但不一定用不是真的都占满了第 10 章。我们的体积变化记录真数据版本大小改动初始-Og176336 B基线改 -Os162288 B省 14KB优化等级影响最大PSRAM 配置170768 B启动代码增加当前含 fade 修复172368 B最终版结论优化等级是体积的第一大因素想小先查 -Os 和日志等级。发布前还要确认固件塞得进分区OTA 是双分区、日志等级调到 INFO——第 18 章的发布检查清单。5.6 声明 vs 定义链接器的账本视角声明在 .h 里说有这么个东西类型/签名——编译器记账定义在 .cpp 里给出真实内容——链接器才能付款找到实现。常见病带机制解释声明了但没实现 → undefined reference账上有需求没人付款 实现放进了 .h 且被多次包含 → multiple definition一份账被付了两次款5.7 实用命令idf.py build# 增量编译只重编改过的idf.py fullclean# 清空 build 全量重编idf.py size# 总大小idf.py size-components# 每个组件占多少找大块头idf.py size-files# 每个 .o 占多少编译日志在build/log/idf_py_stdout_output_*找不到报错时去翻。【动手框】亲手跑一次 idf.py size对账① 在哪执行第 2 章建的blink 工程根目录就是已经idf.py build成功过的那个blink/的 IDF 终端里。如果你已经跟到第 12 章在 l298n 工程里也跑一遍两个对照看才有意思。② 敲什么idf.py size③ 预期看到一张分段表加一行总计。本书环境v6.0.1 esp32s3 默认配置blink 空骨架的实测输出| Memory Type/Section | Used [bytes] | Used [%] | Remain [bytes] | Total [bytes] | |----------------------------------------------------------------------------| | Flash Code | 65174 | | | | | .text | 65174 | | | | | DIRAM | 47629 | 13.94 | 294131 | 341760 | | .text | 32739 | 9.58 | | | | .data | 12426 | 3.64 | | | | .bss | 2464 | 0.72 | | | | Flash Data | 37132 | | | | | .rodata | 36860 | | | | | .appdesc | 256 | | | | | .tdata | 16 | | | | | IRAM | 16384 | 100.0 | 0 | 16384 | | .text | 15356 | 93.73 | | | | .vectors | 1028 | 6.27 | | | | RTC SLOW | 36 | 0.44 | 8156 | 8192 | | .force_slow | 36 | 0.44 | | | | RTC FAST | 24 | 0.29 | 8168 | 8192 | | .rtc_reserved | 24 | 0.29 | | | Total image size: 163891 bytes (.bin may be padded larger)同环境下l298n 工程实测是Total image size: 172257 bytes5.5 的 bin 报告 0x2a150 172368 字节比它略大——就是表末那句“.bin may be padded larger” 说的对齐填充零头。一个实测反直觉功能完整的 l298n 只比空骨架大不到 6%——固件的绝对大头是 IDF 系统本身不是你的代码2.8 第 2 步已埋过这句。数字对不上正常优化等级、日志等级、PSRAM 配置、工具链版本都会挪动这些数——你这个数值比书上的小一截或大一截都别慌要慌的是表结构不一样或干脆没跑出来见 ④。④ 没看到a) 报类似project has not been built、找不到 .elf/.map →刚 fullclean 过或从没编过先idf.py build再敲一次b) 报错提到路径不对 → 你不在工程根目录有 CMakeLists.txt 的那层cd 过去c)idf.py不认 → 回 2.2b/2.3 激活终端。和 5.5 那张text data bss表是什么关系两套统计口径那张是直接从.elf问每段一共多大这张idf.py size按住在哪种内存里Flash Code/Flash Data/DIRAM/IRAM重新归堆。同一堆字节、两个角度数字互相咬合但不会逐字节相等——拿它们对到个位数的账本身就是外行行为。5.8 想一想 小测验想一想bss 很大为什么不用紧张为什么.data的初值要占 Flash-Os和-O2你怎么选为什么调试时用 -Og小测验编译四阶段的顺序是、、、。bin 文件大约等于哪几段之和A. textdata B. bss C. 全部烧录报告里的0x2a150等于多少十进制字节把按位换算过程写在旁边参考 5.5 的算法2×16⁴ 10×16³ …-Os优化的是什么A. 速度 B. 体积 C. 功耗5.9 本章总结四阶段预处理贴头文件 → 编译成汇编 → 汇编成机器码 → 链接排地址优化 编译器改写但语义不变-Os 最小、-Og 好调试段text/rodata/data 占 Flashbss 只占 RAM启动清零bin ≈ text data用 size 系列命令核对账本两种统计口径见 5.7 末动手框。下一章让芯片开口说话——日志与错误处理。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。