为什么87%的嵌入式团队误判静态分析结果?揭秘GCC+PC-lint+Clinic三工具链的5个隐性误报根源

启动代码中关于变量的初始化 对于一个标准C程序所用到的全局和静态变量都是定位在固定的内存空间,在程序启动时必须赋予确定的数值,对于程序中没有赋初值的变量,编译器默认赋值为0。对于__no_init限定的变量则不会被初始化。 对于一个标准的C程序中用到的变量类别如下 Categories of declared data 所示, L(1) 初始化过程 需要初始化为0的section由ILINK中放置在 阅读详情

第一章:静态分析误判现象的系统性归因

静态分析工具在提升代码质量与安全性的过程中,常因语义建模不完备、上下文感知能力受限或规则配置失当,导致大量误报(False Positive)与漏报(False Negative)。这些误判并非孤立缺陷,而是由多层耦合因素共同作用形成的系统性现象。

常见误判根源类型

  • 控制流抽象过度:工具将分支条件简化为不可达路径,忽略运行时动态特征
  • 数据流建模缺失:未跟踪跨函数指针解引用、反射调用或序列化反序列化过程
  • 配置与规则偏差:默认规则集面向通用场景,未适配项目特有的内存管理约定或框架生命周期语义
  • 第三方库符号缺失:未提供准确的stub或summary模型,导致对库函数副作用误判

典型误判案例:空指针解引用误报

以下Go代码片段中,静态分析器可能误报p.Name存在空指针风险,但实际逻辑确保p非nil:
// 示例:结构体指针校验后使用
func printName(p *Person) {
    if p == nil {
        return // 提前返回,后续p必不为nil
    }
    fmt.Println(p.Name) // 某些工具仍标记此处为潜在nil dereference
}
该误判源于分析器未完整建模“提前返回”对后续控制流中变量可达性的约束传播。

误判影响维度对比

影响维度低误判率优势高误判率代价
开发者信任度问题报告被快速响应与修复频繁忽略告警,形成“狼来了”效应
CI/CD集成效率可作为强制门禁环节需人工复核,拖慢流水线

第二章:GCC编译器内建检查机制的隐性缺陷

2.1 GCC -Wall/-Wextra 未覆盖的嵌入式边界场景建模

硬件寄存器位域访问的静默截断
typedef struct { uint8_t ctrl : 3; } reg_t;
reg_t r = {.ctrl = 8}; // 无警告:8 → 0(3-bit 溢出)
GCC 默认不检查位域赋值溢出,-Wall/-Wextra 均不触发告警。该行为在驱动初始化中易引发不可预测的硬件状态。
中断上下文中的非原子变量访问
  • 全局标志位被 ISR 与主循环同时读写
  • 编译器可能优化掉重复读取(如 while(!flag);)
  • -Wextra 不检测 volatile 缺失风险
常见未覆盖场景对比
场景-Wall 覆盖-Wextra 覆盖需手动建模
位域溢出❌❌✅
volatile 缺失❌❌✅

2.2 未初始化变量检测在中断上下文中的失效路径分析

失效根源:编译器优化与上下文隔离
中断处理函数常被标记为 __attribute__((interrupt)),触发编译器启用寄存器保存/恢复优化,导致静态分析工具无法追踪栈帧中变量的初始化状态。
典型失效代码示例
void irq_handler(void) {
    int buf[32];           // 未显式初始化
    memcpy(buf, src, sizeof(buf)); // 静态分析可能跳过此路径
}
该代码在非中断上下文中易被 Clang SA 捕获;但在 IRQ 上下文中,因函数调用约定变更及内联展开抑制,初始化检查被绕过。
检测覆盖对比
上下文类型GCC -WuninitializedClang SA
普通函数✓ 触发警告✓ 报告缺陷
IRQ handler✗ 静默通过✗ 路径不可达

2.3 volatile 语义与优化级联导致的指针别名误报验证

volatile 的内存屏障语义
volatile 告知编译器该变量可能被外部(如硬件、中断、其他线程)异步修改,禁止对其读写进行重排序或缓存优化。
误报触发场景
当多个指针指向同一内存区域,且其中仅部分被声明为 volatile 时,编译器因别名分析不充分,可能错误保留非 volatile 访问路径:
int data = 0;
int *p = &data;
volatile int *vp = &data;  // 显式 volatile 视角

*p = 42;        // 非 volatile 写 → 可能被优化或延迟
*vp = 42;        // volatile 写 → 强制刷新到内存
此处 *p 写入可能被编译器合并、省略或重排,导致与 *vp 的同步语义不一致,静态分析工具常将此判定为“潜在别名冲突”。
验证结果对比
工具误报率原因
Clang SA68%未建模 volatile 对别名分析的约束传播
LLVM AliasAnalysis41%忽略 memory operand 的 volatile 标记传递

2.4 位域(bit-field)跨平台对齐假设引发的结构体越界误警

问题根源:编译器对齐策略差异
不同平台(x86_64 vs ARM64)及编译器(GCC vs Clang)对位域的打包方式和填充规则存在隐式假设,导致 sizeof(struct) 不一致,静态分析工具据此误判内存访问越界。
典型误报示例
struct pkt_hdr {
    uint8_t version : 4;
    uint8_t type    : 4;
    uint16_t len    : 12;  // 跨字节边界
    uint8_t flags   : 4;
}; // GCC x86_64: 4B;Clang ARM64: 5B → 工具按4B校验时触发越界告警
该结构在 GCC 中将 len 和 flags 压入同一 32 位单元,而 Clang 可能因对齐要求插入填充字节,造成布局偏移不一致。
验证对齐行为差异
平台/编译器sizeof(struct pkt_hdr)flags 偏移
GCC 12 (x86_64)43
Clang 16 (aarch64)54

2.5 内联汇编约束符缺失时寄存器污染传播的静态推演盲区

约束符缺失引发的隐式寄存器依赖
当 GCC 内联汇编省略输入/输出约束符(如 "r" 或 "=r"),编译器无法识别寄存器读写意图,导致寄存器生命周期分析失效。
asm volatile("mov %0, %%rax" : : "r"(x)); // ❌ 缺失输出约束,rax 脏态未声明
此处 %%rax 被修改但未通过输出约束告知编译器,后续指令若复用 %rax 将触发未定义行为;编译器静态分析器因无约束元数据,无法建立该寄存器污染传播路径。
静态分析盲区表现
  • 寄存器别名关系不可达:如 %rax 与 %eax 的子寄存器重叠无法建模
  • 跨基本块污染链断裂:缺少约束导致 live-out 集合为空,中断传播推演
场景约束存在约束缺失
rax 污染传播深度3 层(含调用链)0 层(推演终止)

第三章:PC-lint Plus规则引擎在裸机环境下的适配断层

3.1 中断服务函数(ISR)执行模型与函数调用图构建偏差

ISR执行的非标准控制流特性
传统函数调用图(Call Graph)假设所有调用均通过显式call指令触发,但ISR由硬件异步触发,无调用栈上下文。静态分析工具常将其误判为“不可达”或错误归入主函数调用链。
典型偏差示例
void __attribute__((interrupt)) timer_isr(void) {
    update_counter();     // 静态分析可能忽略此调用
    clear_irq_flag(IRQ_TIMER);
}
该ISR由定时器硬件直接跳转执行,不经过任何C语言级调用点;update_counter()在调用图中易被遗漏,导致依赖分析断裂。
偏差影响对比
分析维度正确建模常见偏差
调用源硬件中断向量表误标为main()间接调用
栈帧生成由CPU自动压栈被当作普通函数分配栈空间

3.2 启动代码(startup.s/crt0)符号可见性缺失导致的全局变量生命周期误判

问题根源:启动阶段符号未导出
在多数嵌入式裸机项目中,crt0.s 或 startup.s 通常以 .hidden 或未声明 .globl 方式定义 _start 及初始化段符号,导致链接器无法识别其与 C 运行时数据段的依赖关系。
/* startup.s — 符号未显式导出 */
.section .text
_start:
    ldr r0, =__stack_top
    mov sp, r0
    bl main
    b .
/* __data_start、__data_end 等未声明 .globl → 链接时不可见 */
该写法使链接脚本中基于 __data_start 的 .data 拷贝逻辑失效,全局变量初始化被跳过,表现为未定义行为而非编译错误。
影响链与验证方式
  • 编译器假定全局变量在 _start 后已就绪,但实际未完成 .data 复制
  • 调试器读取变量值为零或随机内存内容,而反汇编显示其地址有效
符号状态链接可见性运行时表现
__data_start(未 .globl)不可见.data 段未初始化
counter(全局 int)可见值恒为 0,即使源码赋初值 = 42

3.3 自定义内存段(如 .bss_noinit)未注册引起的零初始化漏检与误报交织

问题根源
当链接脚本中定义了自定义段(如 .bss_noinit),但未在静态分析工具的段注册表中声明,工具会默认将其归入标准 .bss 段处理,导致零初始化行为误判。
典型链接脚本片段
SECTIONS {
  .bss_noinit (NOLOAD) : {
    *(.bss.noinit)
  } > RAM
}
该段标记为 NOLOAD,运行时不加载初始值,也不清零——但若分析器未识别该段名,会错误执行 memset(ptr, 0, size) 插桩。
段注册缺失影响对比
场景漏检(应清零却未清)误报(不应清却清)
未注册 .bss_noinit✓(.bss_noinit 中全局变量残留栈旧值)✗(工具跳过该段,无误清)
错误注册为 .bss✗✓(对 .bss_noinit 区域执行零初始化)

第四章:Clinic工具链集成时的语义解析失真

4.1 CMSIS头文件宏展开深度不足引发的外设寄存器访问误标

问题根源:宏嵌套层级截断
CMSIS标准头文件中,`__IO` 类型修饰符经多层宏展开后,在部分预处理器(如早期ARM GCC 6.3)中因宏展开深度限制(默认200)被提前终止,导致 `volatile` 语义丢失。
#define __IO volatile
#define USART_CR1   ((USART_TypeDef *) USART1_BASE)->CR1
// 展开后本应为: volatile uint32_t CR1;
// 实际可能退化为: uint32_t CR1;
该退化使编译器对寄存器读写执行非法优化,例如将两次独立读取合并为一次缓存值访问。
影响验证对比表
场景正确展开深度不足展开
寄存器读取每次生成LDR指令可能复用前次结果
状态轮询真实硬件采样陷入死循环
典型修复策略
  • 显式提升宏展开深度:-fmacro-expansion-depth=300
  • 在关键外设结构体定义处强制重声明 __IO

4.2 静态断言(_Static_assert)与配置宏耦合导致的条件编译分支误剪枝

问题根源
当 _Static_assert 依赖未定义或弱定义的配置宏时,预处理器可能在断言求值前完成分支裁剪,导致本应触发的编译期检查被跳过。
典型误用示例
#define CONFIG_FEATURE_X 0
#if CONFIG_FEATURE_X
  #define FEATURE_ENABLED 1
  _Static_assert(sizeof(int) == 4, "int must be 4 bytes");
#endif
此处预处理器因 CONFIG_FEATURE_X 为 0 直接剔除整个 #if 块,_Static_assert 永不执行——即便该断言本应校验跨平台基础假设。
修复策略
  1. 将静态断言移至独立头文件,脱离条件编译上下文;
  2. 改用宏展开强制求值:#define ASSERT_INT_SIZE() _Static_assert(sizeof(int) == 4, "");

4.3 弱符号(__attribute__((weak)))解析丢失引发的函数覆盖逻辑误报

弱符号语义与静态分析断层
当静态分析工具未识别 __attribute__((weak)) 修饰符时,会将弱定义函数误判为强定义冲突源,导致错误报告“重复定义”或“不可达覆盖”。
void __attribute__((weak)) log_init() {
    printf("default init\n");
}
// 若分析器忽略 weak 属性,将视其为普通强定义
该声明允许链接时被同名强定义覆盖;但解析丢失后,工具无法建模此“可覆盖性”,进而将后续强定义判定为非法覆盖。
典型误报场景对比
分析行为正确识别 weakweak 解析丢失
log_init 出现两次接受(弱+强)报错:duplicate symbol
调用链可达性仅标记强定义路径错误标记弱定义为死代码
  • 根本原因:符号属性解析模块未注入 GNU_ATTR_WEAK 语义节点
  • 修复路径:Clang AST Consumer 中扩展 VisitAttr 对 weak 属性的捕获与传播

4.4 编译器内置函数(如 __builtin_clz)抽象语义未建模造成的整数溢出误警

问题根源
静态分析工具常将 __builtin_clz(x) 视为普通函数调用,忽略其要求 x ≠ 0 的前置条件。当输入为 0 时,该函数行为未定义,但分析器可能错误推导出“x 左移 clz 结果”导致溢出。
典型误报场景
int safe_shift(uint32_t x) {
    if (x == 0) return 0;
    int leading_zeros = __builtin_clz(x); // x > 0 ⇒ leading_zeros ∈ [0,31]
    return (int)x << (32 - leading_zeros); // 静态工具可能未约束位移量范围
}
此处位移量 32 - leading_zeros 恒在 [1,32] 区间;当为 32 时,对 int 左移触发未定义行为,但工具因未建模 __builtin_clz 的值域约束而发出冗余溢出警告。
关键约束缺失对比
元素实际语义静态分析建模现状
__builtin_clz(1)返回 31(确定值)视为未知整数,区间 [0,32)
__builtin_clz(x)仅对 x≠0 有定义;输出 ∈ [0,31]未编码非零前提与输出上界

第五章:构建高置信度嵌入式静态分析工作流

工具链协同设计
在 ARM Cortex-M4 项目中,将 SonarQube 与 GCC 的 `-fanalyzer` 和 `cppcheck --enable=warning,style,performance` 深度集成,通过自定义 CI 脚本统一输出 SARIF 格式报告,实现跨工具缺陷归一化。
误报抑制策略
  • 为 FreeRTOS 任务函数添加 `__attribute__((no_instrument_function))` 避免覆盖检测误报
  • 在 `.cppcheck.cfg` 中定义 `` 规则屏蔽 `xQueueReceive()` 超时参数的空指针警告
内存安全强化实践
/* 启用 MISRA-C:2012 Rule 21.3 禁止 malloc/free */
#define malloc(x) _Static_assert(0, "Dynamic allocation forbidden on safety-critical MCU")
#define free(x)   _Static_assert(0, "Free not allowed in static memory model")
分析结果可信度量化
指标阈值实测值(STM32F767)
FP Rate< 8%5.2%
Defect Density< 0.3/kLOC0.18/kLOC
增量分析流水线
git diff --name-only HEAD~1 | grep '\.\(c\|h\)$' | xargs -r cppcheck --quiet --suppress=missingInclude --enable=information
B001-Atmega16-带电复位后不清0的.noinit变量 AVR中、保存在.bss段的变量在芯片启动的.init4阶段会被清0,但是定义在.bss的.noinit段中的变量在.init4阶段不会被清0。 单片机不掉电复位后,.noinit段的这些变量的值依然存在,可以继续使用。 主要内容: 利用这个特性可以测得复位次数。 进而可以测试WDT复位和BOD复位。 阅读详情

相关推荐

产业园区如何高效对接企业技术需求与高校科研成果?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

Keil中如何将变量放置在NOINIT段中

1、参考一下文章,在keil中生成sct文件。2、根据编译器选择使用哪种语句。

yuleitao的博客 1086

数字IC后端实战:OCC电路时钟树综合的5个关键技巧(附Synopsys工具配置)

本文深入探讨了数字IC后端设计中OCC电路时钟树综合的实战技巧。针对工具自动平衡导致的绕线拥塞和时序问题,文章提出了分段处理的核心思路,并详细阐述了通过物理隔离、分段长树策略以及Synopsys工具链(如ICC2、PrimeTime)的精准配置来优化function clk与occ clk路径的关键方法,旨在提升时序收敛性和可制造性。

jwt8token的博客 856

MDK 非0初始化

Article ID:KA003046Applies To:Arm Compiler 5, Arm Compiler 6, Keil MDKConfidentiality:Customer Non-confidential Information in this knowledgebase article applies to: KeilMDKv.4.02and later KeilµVision IDEv. 4.00d and later CMSIS-Pack...

明天乌儿会歌唱 969

[数字电路]用与非门实现BCD译码器驱动数码管

Hello,大家好。这里是xdd_core,好久不见。今天来和大家聊聊大一数字电路课程的一个课设。# Key word:BCD Decoder,Proteus8.x,NAND Gate,Digital Circuit# 流程:Proteus8.x仿真电路并绘制PCB导出Gerber文件打样,最终实物验证。# 以下为我对这个课设的report(里面对整个设计做了详细介绍,并设计了完整的验证电路) 感谢观看,仅供参考,未经作者允许,禁止转载/抄袭,本文已上传至数据库,查重必挂QAQ BCD to 7-Segme

xddcore的博客 6012

【机器人】线性方程最小二乘解法对机械臂DH参数进行标定

1.前言 看了一篇文章,觉得里面的DH参数标定法讲的很清晰,便使用Matlab复现了一下,效果还可以,特写此文记录。 2.原理 2.1.误差最佳值的最小二乘计算 有研究结果表明:几乎95%的机器人位置误差都是由机器人内部运动学的不准确描述引起的,所以对DH模型参数进行标定是很有必要的。 DH参数物理含义:选自《机器人学、机器视觉与控制-Matlab基础》 当DH参数确定后,我们...

要恰饭得嘛 1万+

在keil MDK中定义非初始化(noini)变量

具体 可以参考ARM官方资料:ARM: Uninialized Variables Get Initialized 这里是对上述资料的总结, 该方法已在项目中得到验证。 方法: 分散加载文件如下: 定义了一个 UNINIT 段,并指定它不允许被初始化。 LR_IROM1 0x08000000 0x00080000 { ; load region size_region ER_IROM...

qq_20163075的博客 2250

恭喜,恭喜,iMouse爱鼠XP版全面发布测试,苹果手机自动化测试必须

电脑控制苹果手机,免越狱,免签名,自动化测试

qq_41057894的博客 1063

嵌入式内存深究(5):记忆的保留 —— No-Init 与黑匣子

通常我们被教导:复位(Reset)等于一切重来。但在工业级产品的设计中,我们往往需要“带着记忆重生”。这不仅是为了调试(知道刚才为什么死机),更是为了业务连续性(热启动快速恢复)。

一路往蓝-Anbo 的博客 1176

RK3588开发板RKNPU2环境部署实战:更新rknn_server与librknnrt.so详解

本文详细介绍了在RK3588开发板上更新rknn_server与librknnrt.so核心组件的完整实战流程。通过adb工具推送文件、设置权限并执行同步操作,确保RKNPU2环境部署成功,从而解决推理速度慢或初始化失败等问题,提升AI模型运行效率与稳定性。

weixin_29194817的博客 342

Fortran基础编程(3)——格式化输出

本篇推文将进一步讲解Fortran基础语法中格式化输出与声明部分,让我们的代码更加美观起来吧~

qq_45715554的博客 3827

MDK下99%用户都不知道的万能printf方法

先别着急半路开香槟——该文件是系统自动生成的,如果我们不移动它的位置,那么只要哪次手抖勾选了“值得强调的是:如果你的MDK版本太老,为了确保最佳的用户体验,还是推荐尽快升级吧。”,它的内容就会立即被覆盖掉——意味着我们在后续步骤中所做的修改就会付诸东流。是一个可以编辑的状态,但实践中,我们基本不用去碰他——使用默认配置即可。理论上,到这里,我们就已经完成了部署,可以在进入调试模式后,通过。函数——用它来发送字符到具体的外设吧,比如:​​​​​​​。目录下生成了一个与工程同名的链接脚本(比如图中的工程名叫。

自由的天空 2130

UG二次开发GRIP创建drill工序

UG二次开发GRIP创建drill工序

qq285503851的博客 1696

Keil中变量不被初始化方法

有些时候在我们的应用过程中要求变量有连续性,或者现场保留,例如 Bootloader 跳转,某种原因的复位过程中我们有些关键变量不能被初始化,在不同的编译环境下有不同的设置,本文就这个操作做总结,分别介绍使用 Keil,IAR 和 CubeIDE 的操作方法,本文中所用芯片为:STM32G431RBT6。Keil 中没有这个关键字,而且会有版本的区别,下面分别介绍:为了防止未初始化的变量被初始化为...

撞上电子 885

179、运动控制中的行业标准:机器人安全标准ISO 10218

今天这篇笔记,就把我踩过的坑和标准里的硬骨头掰开揉碎讲清楚。

m0_50546716的博客 169

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Keil MDK下如何设置非零初始化变量 及变量指定位置- Arm CompilerV5 和 V6区别

在分散加载文件中,定义名为NO_INIT的段。注意该段所在的可执行域要具有UNINIT属性。load region size_region 从0扇区开始.ANY (+RO)RW dataload region size_region 从0扇区开始.ANY (+RO)RW data。

原初引力 1668

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

计算机系统: .bss(Block Started by Symbol)详解

bss是程序内存布局中的一个特定段,用于存储未初始化的全局变量和静态变量。它通常位于数据段(.data段)之后,堆(heap)之前。.bss段的名称源自“Block Started by Symbol”的缩写,意味着通过符号开始的块。

m0_72410588的博客 3609

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

嵌入式 C++ 教程——静态存储与栈上分配策略

嵌入式 C++ 内存分配策略指南 本文探讨嵌入式系统中静态存储与栈上分配的关键差异与适用场景。静态存储(包括常量、全局/静态变量)提供编译期确定性但永久占用内存,适合长期存在的只读数据和大缓冲区;栈分配则适合临时对象和局部变量,但需警惕溢出风险。文章详细介绍了如何通过链接脚本控制数据布局、避免常见陷阱(如静态初始化顺序问题),并提供了无动态内存分配时的替代方案(如对象池和placement new)。在资源受限的嵌入式环境中,合理选择内存分配策略对保证可靠性、实时性和内存效率至关重要。

charlie114514191的博客 703

stm32cubeide中实现 __no_init: 只分配空间,不初始化

__no_init; 只分配空间,不初始化; 复位原因变量;

zazabar的专栏 1675

AVR 单片机与GCC 编程之存储器操作

AVR 系列单片机内部有三种类型的被独立编址的存储器,它们分别为:Flash 程序存储器、内部SRAM 数据存储器和EEPROM 数据存储器。Flash 存储器为1K~128K 字节,支持并行编程和串行下载,下载寿命通常可达10,000 次。由于AVR 指令都为16 位或32 位,程序计数器对它按字进行寻址,因此FLASH 存储器按字组织的,但在程序中访问FLASH 存储区时专用指令LPM 可分别

Persistence Produces Miracles 6296

arm-2d库详细介绍

本文档解释了 ARM-2D 的基本概念。在我们开始体验Arm-2D时,你可能会有很多问题,比如:Arm-2D 是什么?它能解决什么问题?目标受众是谁?Arm-2D 由什么组成?发展环境如何?接下来的内容,将一一作答。如果您想在 Linux 中设计 GUI 应用程序,则不必直接面对硬件。Linux 生态系统为您提供了所有必要的软件组件,包括 GPU 驱动程序、 GUI 栈和许多方便的参考设计。不幸的是,如果您是使用 Cortex-M 设备的嵌入式开发人员,则必须直接面对各种显示硬件。

xuxuluo的博客 3717

通过未初始化全局变量,研究BSS段和COMMON段的不同

本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。 作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net 最近正在重温《程序员的自我修养》一书,由于水平比以前有所提升,所以读书的收获也不一样。

Chris_Magic的学习笔记 1724

__no_init

__no_init用于禁止系统启动时的变量初始化,什么情况下需要用这个关键字使系统禁止变量的初始化,禁止变量初始化用在什么场合,为什么要这样做,有什么意义吗? 1、看门狗复位的现场恢复,如果初始化了就完全不可恢复了 2、使用nvram保存数据,需要连续记录的。    我有个变量,需要在系统意外复位时,这个变量值能保留,所以采用__no_init来实现,只是上电的时候这个值不

hhy_0612的专栏 1210

AVR的-section

avr-gcc段section与在定位relocation     粗略的讲,一个代表一无缝隙的数据快即地址范围,一个段里存储的数据都为同一性质, 如只读数据。as汇编器在编译局部程序时总假设从0地址开始,并生成目标文件。 最后LD连接器在连接多个目标文件时为每一个段分配运行时统一地址。这虽然是个简单的解 释,却足以说明我门为什么用段。  LD将这些数据快正确移动到他们运行时的地址。

851
上一篇: MCP 2.0 2026新版加密要求详解:TLS 1.3+国密SM4双栈部署实操指南(含OpenSSL 3.2适配脚本)
下一篇: 【Dify企业级私有化部署黄金架构】:5大核心组件+3层安全隔离+2小时极速接入实战指南
CodePulse
博客等级 码龄2年 187粉丝 2233原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值