ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

深入解析 Rust 编译器错误 E0787:Naked 函数必须由单个 `naked_asm!` 内联汇编块构成

深入解析 Rust 编译器错误 E0787:Naked 函数必须由单个 `naked_asm!` 内联汇编块构成 深入解析 Rust 编译器错误 E0787Naked 函数必须由单个naked_asm!内联汇编块构成【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust本篇文章基于 rustc 编译器源码仓库compiler/rustc_error_codes中的错误说明文档系统讲解编译错误E0787不支持的裸函数定义An unsupported naked function definition的触发条件、修复方法以及其背后的类型检查与代码生成实现原理。读完本文你将掌握#[unsafe(naked)]函数的全部约束规则单个naked_asm!块、不允许asm!、只允许const/sym操作数、允许att_syntax与raw选项并能够从源码层面理解编译器是如何检查并诊断这些错误的。什么是 Naked 函数裸函数Naked 函数是 Rust 中一类特殊的函数函数体不产生编译器插入的任何序言prologue与尾声epilogue代码函数入口处完全由开发者通过内联汇编自行控制。这类函数常用于实现操作系统内核的启动入口、中断处理程序、上下文切换等对指令序列有绝对控制需求的底层场景。在当前的 Rust 版本中声明裸函数使用#[unsafe(naked)]属性早期版本为#[naked]并且函数体必须包含且仅能包含一个naked_asm!内联汇编块。编译器对裸函数施加了极其严格的限制因为它无法为函数生成任何辅助代码函数必须靠汇编块自给自足。E0787 的触发场景与错误示例当裸函数的定义不符合约束时编译器会抛出 E0787。官方错误文档E0787.md给出了最典型的错误示例#[unsafe(naked)] pub extern C fn f() - u32 { 42 }这段代码中函数体只是一条普通的返回值表达式42没有任何naked_asm!汇编块因此触发 E0787。错误的完整规则清单根据错误文档裸函数定义必须满足以下全部条件违反任意一条都会报 E0787函数体必须由且仅由一个naked_asm!汇编块构成。不能有多个naked_asm!块也不能混入其他普通 Rust 语句或表达式。执行流程不能越过汇编块末尾。即汇编代码必须以ret等返回指令结束或通过跳转等方式发散diverge绝不允许落空fall through到函数末尾。naked_asm!块中只允许使用att_syntax和raw这两个选项其余所有asm!选项如volatile、alignstack、intel等都不被允许。操作数限制naked_asm!块中不允许出现除const和sym以外的任何操作数。in、out、inout、label等操作数在裸函数上下文中都是非法的。从源码看 E0787 是如何被检查出来的E0787 的诊断逻辑位于类型检查阶段的专用检查模块 compiler/rustc_hir_typeck/src/naked_functions.rs。该模块通过typeck_naked_fn对每个#[unsafe(naked)]函数执行三步校验pub(crate) fn typeck_naked_fntcx( tcx: TyCtxttcx, def_id: LocalDefId, body: tcx hir::Bodytcx, ) { debug_assert!(find_attr!(tcx, def_id, Naked(..))); check_no_patterns(tcx, body.params); check_no_parameters_use(tcx, body); check_asm(tcx, def_id, body); }check_no_patterns函数参数只能使用平凡的绑定模式通配符_或简单标识符不允许解构模式check_no_parameters_use函数体内不允许使用任何函数参数因为裸函数没有编译器生成的参数加载代码参数实际并不存在check_asm函数体只能包含单个内联汇编块。其中check_asm是 E0787 的直接来源。它遍历函数体的 HIR将每个语句/表达式归类为四种ItemKindenum ItemKind { NakedAsm, // 合法的 naked_asm! 块 InlineAsm, // 非法的普通 asm! 块 NonAsm, // 非汇编语句 Err, // 解析错误节点 }归类的具体逻辑非常严格见 naked_functions.rs 中CheckInlineAssembly::check_expr任何普通表达式——字面量、二元运算、函数调用、if/match、循环、闭包、路径引用等——都会被标记为NonAsmDropTemps和块表达式Block会被递归遍历其内部语句而asm!宏展开产生的InlineAsm节点与naked_asm!产生的NakedAsm节点会被分别记录。global_asm!出现在函数体内则直接触发span_bug!它本就不允许出现在该位置。当检查结果不是恰好一个NakedAsm时编译器就会发射 E0787。诊断结构体定义在 compiler/rustc_hir_typeck/src/diagnostics.rs 中implG: EmissionGuarantee Diagnostic_, G for NakedFunctionsAsmBlock { fn into_diag(self, dcx: DiagCtxtHandle_, level: Level) - Diag_, G { let mut diag Diag::new( dcx, level, msg!(naked functions must contain a single naked_asm! invocation), ); diag.span(self.span); diag.code(E0787); for span in self.multiple_asms.iter() { diag.span_label( *span, msg!(multiple naked_asm! invocations are not allowed in naked functions), ); } for span in self.non_asms.iter() { diag.span_label(*span, msg!(not allowed in naked functions)); } diag } }可以看到E0787 的主错误消息是 naked functions must contain a singlenaked_asm!invocation并且会根据具体情况给出两类补充标注多个naked_asm!块时在每个多余块处标注 multiplenaked_asm!invocations are not allowed in naked functions存在非汇编语句时在每个违规语句处标注 not allowed in naked functions。此外若函数体内出现的是普通asm!而非naked_asm!编译器会单独发射NakedFunctionsMustNakedAsm诊断同样使用 E0787 错误码并给出 consider using thenaked_asm!macro instead 的修复建议#[derive(Diagnostic)] #[diag(the asm! macro is not allowed in naked functions, code E0787)] pub(crate) struct NakedFunctionsMustNakedAsm { #[primary_span] #[label(consider using the naked_asm! macro instead)] pub span: Span, }值得注意的一个细节是check_asm中有一个降噪逻辑——如果裸函数里只有一个汇编块且同时存在错误节点例如开发者临时添加了compile_error!(...)则不再叠加发射 E0787避免错误信息刷屏。正确写法合法的裸函数定义把错误示例修正为合法形式如下#![feature(unsafe_attributes)] use std::arch::naked_asm; #[unsafe(naked)] pub extern C fn f() - u32 { // 汇编块必须以 ret 结束不得越过末尾继续执行 naked_asm!(mov eax, 42, ret) }几点注意事项返回指令必须显式书写编译器不会帮你生成ret汇编块要么以返回指令结束要么以跳转/死循环等方式发散绝不能执行到汇编块结束就没了。这一点也体现在代码生成阶段rustc 期望裸函数的 MIR 起始块START_BLOCK以InlineAsm终止符结束否则会触发内部断言见下文。允许的选项naked_asm!内可以使用att_syntaxATT 语法风格与raw不对模板字符串做格式检查两个选项例如#[unsafe(naked)] pub extern C fn f() - u32 { naked_asm!( movl $42, %eax, ret, options(att_syntax), ) }操作数仅限const与sym你可以在汇编块中引用常量与符号但不能使用in/out/inout/label等寄存器或内存操作数。这与代码生成阶段只处理Const、SymFn、SymStatic三种操作数的实现是严格对应的。为什么操作数会被限制为const和sym答案藏在代码生成层。裸函数的汇编并不是作为普通函数体生成的而是被转换成类似global_asm!的全局汇编来发射。见 compiler/rustc_codegen_ssa/src/mir/naked_asm.rs 中的codegen_naked_asmlet operands: Vec_ operands.iter().map(|op| inline_to_global_operand::Cx(cx, instance, op)).collect();其中inline_to_global_operand的匹配分支很能说明问题它只实现了InlineAsmOperand::Const常量、InlineAsmOperand::SymFn函数符号、InlineAsmOperand::SymStatic静态变量符号三种情况的转换而对In、Out、InOut、Label操作数一律调用bug!(invalid operand type for naked_asm!)。也就是说寄存器输入输出类操作数在裸函数场景下从代码生成层面就是不被支持的类型检查阶段的限制与之一一对应。同时这段代码也展示了裸函数代码生成的另外两个关键行为符号与节section管理编译器会为裸函数手工发射.globl/.weak、.type、.balign等汇编指令并依据目标平台二进制格式ELF/Mach-O/COFF/Wasm/XCOFF选择不同的汇编模板。例如 ELF 下默认 4 字节对齐、COFF 下默认 16 字节对齐ARM 目标还会根据instruction_set属性发射.arm或.thumb_func指令。链接属性约束Linkage::Internal内部链接、Common、AvailableExternally、ExternalWeak等链接类型在裸函数中会导致编译失败emit_fatal因为内联汇编无法可靠表达这些链接语义——例如 LLVM 在外部弱符号上会触发 SIGILL。相关的其他裸函数错误E0787 之外裸函数还有一系列配套的错误码理解它们有助于完整掌握裸函数的限制体系E0736见 E0736.md#[naked]函数不允许同时使用某些属性包括#[inline]、#[track_caller]、#[test]/#[ignore]/#[should_panic]等。原因在于这些属性要求编译器插入额外代码内联决策、调用者位置跟踪、测试脚手架而裸函数刻意禁止编译器生成任何额外代码。其错误示例为#[inline] #[unsafe(naked)] fn foo() {}还有一组未使用 E0787 错误码、但由同一个naked_functions.rs模块发出的诊断包括参数解构模式不被允许NoPatterns、函数体内使用参数不被允许ParamsNotAllowed等。在仓库中进一步验证如果你想在真实编译器中验证这些行为可以关注以下几处仓库内容错误文档本体E0787.md、E0736.md类型检查实现compiler/rustc_hir_typeck/src/naked_functions.rs诊断结构定义compiler/rustc_hir_typeck/src/diagnostics.rs代码生成实现compiler/rustc_codegen_ssa/src/mir/naked_asm.rsnaked_asm!宏的展开入口位于 compiler/rustc_builtin_macros/src/asm.rsexpand_naked_asm函数其中的选项合法性检查is_supported_option与模板解析逻辑印证了仅允许att_syntax/raw的约束代码生成层的回归测试位于 tests/codegen-llvm/naked-fn/例如 naked-functions.rs 验证了不同目标平台下裸函数生成的汇编是否符合预期。总结E0787 是 Rust 编译器用来守护裸函数#[unsafe(naked)]定义合法性的核心错误码其背后是一整套严格限制的设计哲学裸函数没有编译器生成的序言/尾声函数体必须是一个自包含、以返回或跳转终结的naked_asm!块操作数仅限const与sym选项仅限att_syntax与raw。这些限制在类型检查阶段由 naked_functions.rs 强制实施在代码生成阶段由 naked_asm.rs 兜底验证形成了一条完整的检查—诊断—生成链路。理解 E0787就等于理解了 Rust 中所有底层汇编编程的边界。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表