当前位置: 首页 > news >正文

【ARM 嵌入式 编译系列 7.4 -- GCC 链接脚本中 ASSERT 函数】

文章目录

    • Overview
      • 使用场景
      • 示例说明
      • 如果超出怎么办?
      • 小贴士
    • 地址信息打印
      • 方法一:使用 `ASSERT` + `DEFINED` 机制手动制造错误
      • 方法二:创建多个 ASSERT 分段定位大小(伪“打印”)
      • 方法三:使用 `--defsym` 和脚本外部配合打印
      • 方法四:编译失败后使用 `size` 工具查看段大小
      • 总结

Overview

ASSERT() 是 GNU ld(linker)脚本中的一个非常有用的宏,用于在链接时进行条件检查。

它的语法如下:

ASSERT(expression, "error message")

如果 expression 的值为 false(即为 0),链接器会中断链接,并显示你定义的错误信息。

例子:

ASSERT(INST_LOAD_SIZE <= 0xe00, "inst load overflow")

这个语句的含义是:

  • INST_LOAD_SIZE 是一个在 linker script 中定义的符号,表示一段区域的大小,通常是 INST(指令段或代码段)加载部分的字节数。

  • 0xe00 是十六进制,等于十进制的 3584(3.5 KB)。

  • 如果 INST_LOAD_SIZE > 0xe00,说明代码段过大,链接器将报错,并输出 "inst load overflow"

使用场景

这种断言常用于嵌入式系统或资源受限系统(如 bootloader、裸机程序、microcontroller 上的应用)中,用来保证:

  • 某段代码不会超过特定区域(比如 Flash 起始地址+偏移)。

  • 某些段的对齐或边界满足要求。

  • 多个段之间不会互相覆盖。

示例说明

设想你有如下 linker script 段落:

INST_LOAD_START = ORIGIN(FLASH);
.inst_load :
{*(.text)       /* 所有 .text 段 */*(.text.*)     /* 所有 .text.子段 */*(.rodata)     /* 只读数据 */*(.rodata.*)
} > FLASHINST_LOAD_END = .;
INST_LOAD_SIZE = INST_LOAD_END - INST_LOAD_START;ASSERT(INST_LOAD_SIZE <= 0xe00, "inst load overflow")

这里做了几件事:

  1. 定义了一个段 .inst_load,把所有的代码和只读数据放到 FLASH 区域。

  2. 使用两个符号 INST_LOAD_STARTINST_LOAD_END 定位这个段的起止地址。

  3. 计算出大小 INST_LOAD_SIZE

  4. ASSERT 保证其大小不超过 0xe00

如果超出怎么办?

如果你的 .text + .rodata 实际大小超过了 0xe00,链接器报错:

ld: inst load overflow

你就需要:

  • 优化代码(裁剪函数、启用 LTO、精简库)。

  • 改变 layout,扩大 Flash 映射区域。

  • 或者容忍更大的大小,放宽 ASSERT 条件。

小贴士

  • 在复杂项目中,多用 ASSERT() 是很好的防御性编程习惯,防止 silent overflow。

  • 可以通过 nmobjdump -h 检查生成 elf 中段大小,辅助你验证实际使用情况。

  • 如果你想打印具体使用的大小,可以写:

    ASSERT(INST_LOAD_SIZE <= 0xe00, "inst load overflow: size too big")
    

    但如果你想更详细地打印值,则需要辅助脚本工具,如 objdump + awk 等。

地址信息打印

在 GNU ldlinker script 中,ASSERT() 报错时无法直接像 C 语言那样拼接字符串或打印数值(比如 INST_LOAD_SIZE 的值)。但有几种技巧可以在错误信息中间接打印 INST_LOAD_SIZE 的数值,以辅助定位问题。

方法一:使用 ASSERT + DEFINED 机制手动制造错误

INST_LOAD_START = ORIGIN(FLASH);
.inst_load :
{*(.text .text.*)*(.rodata .rodata.*)
} > FLASHINST_LOAD_END = .;
INST_LOAD_SIZE = INST_LOAD_END - INST_LOAD_START;ASSERT(INST_LOAD_SIZE <= 0xe00, "inst load overflow")/* 打印 INST_LOAD_SIZE 的数值(变相输出) */
inst_load_size_debug = INST_LOAD_SIZE;

这个 inst_load_size_debug 会在最终 ELF 文件中作为一个符号出现,可以使用如下命令在出错后查看:

nm -n your_program.elf | grep inst_load_size_debug

方法二:创建多个 ASSERT 分段定位大小(伪“打印”)

这种方法不优雅,但实用:将大小范围分成几段,分别报不同错,从而间接推测数值范围

ASSERT(INST_LOAD_SIZE <= 0x1000, "INST_LOAD_SIZE > 0x1000")
ASSERT(INST_LOAD_SIZE <= 0x0e00, "INST_LOAD_SIZE > 0x0e00")
ASSERT(INST_LOAD_SIZE <= 0x0c00, "INST_LOAD_SIZE > 0x0c00")
ASSERT(INST_LOAD_SIZE <= 0x0a00, "INST_LOAD_SIZE > 0x0a00")
  • 如果只有最后一个断言失败:INST_LOAD_SIZE > 0xa00,表示大小在 0xa00~0xc00 之间。

方法三:使用 --defsym 和脚本外部配合打印

在编译后脚本中增加:

echo "INST_LOAD_SIZE: $(nm -S your_program.elf | grep inst_load_size_debug)"

或者使用 objdump:

arm-none-eabi-objdump -t your_program.elf | grep inst_load_size_debug

方法四:编译失败后使用 size 工具查看段大小

size your_program.elf

会打印 .text.rodata 的大小,总计是否超过限制。


总结

由于 ASSERT() 的限制,不能直接在错误信息中打印数值,但可以通过:

  • 将数值保存为符号供 nm/objdump 查看

  • 用多个 ASSERT 模拟区间“打印”

  • 编译失败后用 size/nm 等工具配合查看

http://www.lqws.cn/news/495937.html

相关文章:

  • 如何利用Charles抓包工具提升API调试与性能优化
  • QT6(46)5.2 QStringListModel 和 QListView :列表的模型与视图的界面搭建与源代码实现
  • Netty内存池分层设计架构
  • 本地文件深度交互新玩法:Obsidian Copilot的深度开发
  • 【streamlit 组件样式定位与修改】
  • 数字孪生:为UI前端设计带来沉浸式交互新体验
  • 【2025CVPR】基于脉冲神经网络的能效目标检测模型:Multi-scale Spiking Detector(MSD)深度解析
  • PillarNet: Real-Time and High-PerformancePillar-based 3D Object Detection
  • 稳定币走向监管:新秩序下的数字货币新局
  • Redis集群性能优化实战指南
  • 96道Docker 容器高频题整理(附答案背诵版)
  • 设计模式 - 工厂方法
  • 在AI时代看清糖网:糖尿病视网膜病变筛查的转型之路
  • CARLsim开源程序 是一个高效、易用、GPU 加速的软件框架,用于模拟具有高度生物细节的大规模脉冲神经网络 (SNN) 模型。
  • Word2Vec 原理是什么
  • mysql一张表,其中一个字段设置了唯一索引,又设置了普通索引,查询的时候很慢,没有走普通索引,是const
  • 如何在 Vue 应用中嵌入 ONLYOFFICE 编辑器
  • OpenLayers:台风轨迹动画
  • AI智能体——MCP 模型上下文协议
  • TestCafe 全解析:免费开源的 E2E 测试解决方案实战指南
  • Python datetime模块详解
  • SpringBoot中使用表单数据有效性检验
  • C#串口通讯实战指南
  • 前端跨域解决方案(7):Node中间件
  • C语言数组介绍 -- 一维数组和二维数组的创建、初始化、下标、遍历、存储,C99 变长数组
  • Linux笔记---线程控制
  • 容器技术入门与Docker环境部署指南
  • js逻辑:【增量更新机制】
  • 【LeetCode 热题 100】42. 接雨水——(解法一)前后缀分解
  • Profibus DP主站转EtherNet/IP从站总线协议转换网关