深入解读 Rust 编译器错误码 E0776:cmse_nonsecure_entry 函数的 ABI 约束与演进

发布时间:2026/9/10 8:55:20
深入解读 Rust 编译器错误码 E0776:cmse_nonsecure_entry 函数的 ABI 约束与演进 深入解读 Rust 编译器错误码 E0776cmse_nonsecure_entry 函数的 ABI 约束与演进【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rustE0776 是 rustc 错误码体系中与 Armv8-M TrustZone-M 安全扩展相关的历史错误码用于约束 CMSE 非安全入口non-secure entry函数的调用约定。本文基于当前仓库中的 E0776 官方文档 展开并结合编译器中 ABI 校验、LLVM 代码生成等源码实现完整说明该错误码的产生背景、修复方式及其在现代 rustc 中的演进形态帮助读者理解 CMSE 入口函数的参数传递限制与正确写法。错误码现状E0776 已不再由编译器发出在深入探讨之前必须先澄清一个重要事实E0776 这个错误码在当前版本的 rustc 中已经不再被发出no longer emitted。这一点在 error_codes/lib.rs 中有明确的注释记录// E0776, // Removed; #[cmse_nonsecure_entry] is now extern cmse-nonsecure-entry也就是说旧的#[cmse_nonsecure_entry]属性写法已经被新的extern cmse-nonsecure-entry函数 ABI 语法所取代。错误码的删除并非因为功能被废弃而是因为触发该错误的旧语法本身被重构了。rustc 错误码文档体系中保留 E0776 的说明是为了让开发者尤其是检索历史错误码的开发者理解这条错误曾经的含义与修复方式。错误码原始含义入口函数需要 C ABI原始错误信息E0776 的原始错误信息为#[cmse_nonsecure_entry]functions require a C ABI#[cmse_nonsecure_entry]函数要求使用 C ABI在旧语法时代#[cmse_nonsecure_entry]是一个函数属性需要配合extern C使用。如果开发者忘记声明 C ABI就会触发 E0776。原始错误示例E0776 文档中给出的错误示例代码如下#![feature(cmse_nonsecure_entry)] #[no_mangle] #[cmse_nonsecure_entry] pub fn entry_function(input: Vecu32) {}这段代码包含两个问题缺少 C ABI 声明#[cmse_nonsecure_entry]要求函数使用extern C调用约定参数类型不当Vecu32是一个包含指针、长度、容量的复合类型体积远超寄存器容量无法通过寄存器传递。原始修复方式文档给出的修复方法是显式声明 C ABI#![feature(cmse_nonsecure_entry)] #[no_mangle] #[cmse_nonsecure_entry] pub extern C fn entry_function(input: Vecu32) {}需要说明的是即便补上extern CVecu32这类无法在寄存器中完整传递的类型仍会触发其他检查详见下文源码分析因此这只是针对 E0776 本身的修复示范。现代写法直接使用 cmse-nonsecure-entry ABIE0776 被删除的根本原因是 rustc 引入了专门的extern cmse-nonsecure-entry函数 ABI 语法取代了原先的属性 C ABI 组合。在 unstable-book 特性文档 中给出了当前的标准写法#![no_std] #![feature(cmse_nonsecure_entry)] #[no_mangle] pub extern cmse-nonsecure-entry fn entry_function(input: u32) - u32 { input 6 }关键点该特性名称为cmse_nonsecure_entry在 rustc_feature/src/unstable.rs 中登记为 unstable 特性自 Rust 1.48.0 起引入tracking issue 编号为 75835函数仍需搭配#[no_mangle]或类似导出手段因为该 ABI 的用途就是生成可被链接器识别的安全网关入口符号ABI 语法extern cmse-nonsecure-entry的定义位于 rustc_abi/src/extern_abi.rs属于ExternAbi::CmseNonSecureEntry变体。为什么参数必须通过寄存器传递源码级原理触发 E0776 的深层原因E0776 之所以要求 C ABI根因是 CMSE 非安全入口函数的参数和返回值不允许通过栈传递。这是因为安全世界与非安全世界之间共享栈如果参数落在栈上就可能泄露安全数据或成为攻击面。因此编译器强制要求所有参数都必须能装进寄存器。在 rustc_ast_lowering/src/stability.rs 中ExternAbi::CmseNonSecureEntry与CmseNonSecureCall一样被标记为需要 unstable feature gateExternAbi::CmseNonSecureEntry Err(UnstableAbi { abi, feature: sym::cmse_nonsecure_entry, explain: GateReason::Experimental, }),这解释了为什么示例代码开头必须写#![feature(cmse_nonsecure_entry)]。ABI 校验实现cmse.rsrustc 在类型检查阶段的 HIR 类型降级hir_ty_lowering模块中实现了专门的 CMSE ABI 校验逻辑文件位于 compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs。其中validate_cmse_abi函数负责检查两个核心约束1. 参数总量不能超过 4 个 32 位寄存器16 字节is_valid_cmse_inputs函数cmse.rs逐个累加参数类型的内存布局大小并做对齐处理accum size; accum accum.next_multiple_of(Ord::max(4, align)); // i.e. exceeds 4 32-bit registers if accum 16 { excess_argument_spans.push(hir_ty.span); }当累计大小超过 16 字节即 4 个 32 位寄存器时编译器会发出CmseInputsStackSpill诊断提示参数会溢出到栈上。这正解释了为何Vecu32无法作为入口函数参数——它的大小远超过寄存器容量。2. 返回值必须能完整放入寄存器is_valid_cmse_output_layout函数cmse.rs规定返回值要么不超过 4 字节要么是恰好 8 字节且剥离开透明包装后为i64/u64/f64之一if size 4 { return true; } else if size ! 8 { return false; } // Accept (transparently wrapped) scalar 64-bit primitives. matches!( layout.peel_transparent_wrappers(cx).ty.kind(), ty::Int(ty::IntTy::I64) | ty::Uint(ty::UintTy::U64) | ty::Float(ty::FloatTy::F64) )此外validate_cmse_abi还显式禁止两类情况C 可变参数c-variadicextern cmse-nonsecure-entry函数不能是可变参数函数cmse.rs返回值中的impl Traitopaque type在返回类型中使用impl Trait会引发布局计算查询环被显式禁止cmse.rs。这些校验由 rustc_codegen_ssa/src/mir/block.rs 中的相关逻辑共同配合确保从类型检查到 MIR 代码生成全程贯彻参数不得使用非安全栈这一安全约束。LLVM 端的属性生成在后端代码生成阶段rustc_codegen_llvm/src/abi.rs 会为 CMSE 非安全入口函数附加 LLVM 函数属性CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) { func_attrs.push(llvm::CreateAttrString(cx.llcx, cmse_nonsecure_entry)) }LLVM 收到该属性后会执行一系列平台相关的转换具体在 LLVM 的 ARM 后端实现为函数生成带__acle_se_前缀的导出符号、插入清除可能含安全信息的寄存器的代码、并以BXNS指令返回。这些行为在 unstable-book 特性文档 中有明确记载在函数上添加__acle_se_前缀加标准函数名的特殊符号约束参数数量以避免使用非安全栈函数返回前清除可能包含安全信息的寄存器使用BXNS指令返回。__acle_se_符号会被链接器用来生成安全网关 veneersecure gateway veneer这是 TrustZone-M 安全状态切换到非安全状态的必需机制。相关错误码E0775 与目标平台限制CMSE 入口函数还有一条密切相关的错误码 E0775两者同属 CMSE 特性家族可以对照阅读。E0775 的文档位于 compiler/rustc_error_codes/src/error_codes/E0775.md其含义为#[cmse_nonsecure_entry]is only valid for targets with the TrustZone-M extension.即 CMSE 入口函数只能在具备 TrustZone-M 扩展的目标平台上使用。当前支持的目标平台包括thumbv8m.main-none-eabithumbv8m.main-none-eabihfthumbv8m.base-none-eabi对应 Armv8-M 架构轮廓目标名中包含thumbv8m。E0776 与 E0775 的关系可以理解为E0776 关注调用约定是否符合要求是否使用 C ABI / 能否用寄存器传参E0775 关注目标平台是否支持是否具备 TrustZone-M 扩展。实战建议与总结结合当前仓库源码使用 CMSE 非安全入口函数时应遵循以下要点使用现代 ABI 语法优先使用extern cmse-nonsecure-entry而非旧的#[cmse_nonsecure_entry]属性 extern C组合这是 E0776 被移除后 rustc 推荐的写法保持 unstable 特性启用需要#![feature(cmse_nonsecure_entry)]rustc 1.48.0 起tracking issue 75835控制参数与返回值体积全部参数累计大小不得超过 16 字节4 个 32 位寄存器返回值只能是 ≤4 字节的类型或透明包装的 64 位标量i64/u64/f64否则会触发参数/返回值栈溢出的编译错误——Vecu32这类堆分配类型绝不能用作出入口函数参数选择正确的目标平台目标必须是thumbv8m系列如thumbv8m.main-none-eabi否则会触发 E0775避免可变参数与impl Trait入口函数不允许 c-variadic 和 opaque 返回类型配合导出符号使用函数应使用#[no_mangle]使编译器生成的__acle_se_前缀符号能正确导出供链接器生成安全网关 veneer。E0776 的消亡是 rustc 错误码体系随语言演进迭代的典型案例一条错误码所约束的约束条件并未消失而是随着更精确的 ABI 语法引入而转化为类型系统层面的直接检查。理解这条错误码的历史也就理解了 CMSE 入口函数参数必须走寄存器这一核心安全设计原则。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询