ARTICLE DETAIL

资讯详情

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

Rust 编译器 BPF 目标(bpf*-unknown-none)完全指南:从目标构建到程序编写与调试

Rust 编译器 BPF 目标(bpf*-unknown-none)完全指南:从目标构建到程序编写与调试 Rust 编译器 BPF 目标bpf*-unknown-none完全指南从目标构建到程序编写与调试【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust本文以 Rust 官方编译器仓库当前仓库即 rust-lang/rust 源码树中src/doc/rustc/src/platform-support/bpf-unknown-none.md为核心系统讲解在 Rust 生态中为 64 位 BPF 虚拟机编写no_std程序的全部环节目标规格与要求、Rust 编译器自身的构建方式、基于build-std的程序编译流程、panic 与错误处理、宿主侧单元测试策略、跨平台交叉编译注意事项以及链接由 clang 编译的 C 位码的build.rs方案。读完本文你将掌握bpfel-unknown-none/bpfeb-unknown-none这两个 Tier 3 目标的完整使用链路并能基于当前仓库源码理解每个环节背后的编译期决策。BPF 目标概览两个 triple 与它们的定位BPFBerkeley Packet Filter是一种运行在 Linux 内核中的虚拟机字节码。Rust 为 64 位 BPF 虚拟机提供了两个 Tier 3 目标Target triple字节序说明bpfeb-unknown-nonebig endian大端llvm_target为bpfebbpfel-unknown-nonelittle endian小端llvm_target为bpfel两者的目标定义位于compiler/rustc_target/src/spec/targets/bpfeb_unknown_none.rs与compiler/rustc_target/src/spec/targets/bpfel_unknown_none.rs。从源码可以确认几个关键事实两者均为Tier 3、std: false不提供标准库、host_tools: falsepointer_width均为 64属于 64 位目标arch均为Arch::Bpfdata layout 分别以E-大端和e-小端开头公共的底层选项由compiler/rustc_target/src/spec/base/bpf.rs中的opts(endian)统一生成。这两个 triple 在 rustc 的目标注册表中通过compiler/rustc_target/src/spec/mod.rs的(bpfeb-unknown-none, bpfeb_unknown_none)与(bpfel-unknown-none, bpfel_unknown_none)两条记录生效。目标维护者BPF 目标的维护者为 nagisa 与 vadorovsky对应平台支持文档中列出的维护者名单。作为 Tier 3 目标它不包含自动构建、测试或发布保证相关变更通常在 rustc 仓库中以 PR 形式经维护者 review 后合入。目标的核心技术约束bpf*-unknown-none的目标选项在compiler/rustc_target/src/spec/base/bpf.rs中集中定义以下特性直接决定了你在该目标上能写什么、不能写什么选项值含义allow_asmtrue允许使用内联汇编linker_flavorLinkerFlavor::Bpf使用 BPF 专用链接器bpf-linkeratomic_casfalse不支持 CAS 原子操作min_atomic_width/max_atomic_widthSome(64)仅支持 64 位原子操作宽度panic_strategyPanicStrategy::Abortpanic 直接中止不展开栈obj_is_bitcodetrue产物为 LLVM 位码bitcode而非原生机器码singlethreadtrue单线程模型no_builtinstrue不链接编译器内建函数merge_functionsMergeFunctions::Disabled禁用函数合并原因见下关于merge_functions的禁用源码注释给出了明确理由旧内核不支持 bpf-to-bpf 调用而在新内核上用户态程序在BPF_PROG_LOAD之前仍需自行完成重定位并非所有 BPF 库都实现了这一步。关于原子操作注释还提醒v3及以上 CPU 在 LLVM 中支持 32 位原子操作但 rustc 选择统一将最小原子宽度设为 64以避免选项随 target cpu 变化带来困惑。运行环境的版本要求文档给出的宿主内核版本门槛如下绝大多数宿主架构Linux 内核4.18及以上该版本引入了 BTFRISC-V 宿主5.7及以上PowerPC32 宿主5.13及以上LoongArch 宿主6.1及以上。这些版本的差别源于各架构对 BTF 与 BPF 基础设施的支持先后不同。BPF 程序由虚拟机内的 JIT 编译器翻译为宿主原生指令而运行前的合法性检查由内核的 BPF verifier 完成。基础工具链要求需要带rust-src组件的 Rust 工具链用于build-std构建core必须额外安装bpf-linker一个基于 LLVM 位码的链接器是 aya-rs 组织维护的项目extern C遵循 BPF ABI 调用约定产物为 ELF 格式准确说是包含 ELF 元数据的 LLVM 位码对象最终由 bpf-linker 产出 ELF。从 bootstrap 源码看src/bootstrap/src/utils/helpers.rs的use_host_linker明确将bpf列为不使用宿主链接器的目标与 wasm32、nvptx 等并列印证了 BPF 目标必须走专用链接器这一设计。构建支持 BPF 的 Rust 编译器Rust 官方暂不发布 BPF 目标的预编译产物因此如果你希望 rustc 自带该目标的core需要在构建 rustc 时把目标加入config.toml[build] target [bpfeb-unknown-none, bpfel-unknown-none]对应到仓库的构建体系bootstrap 在编译no_std目标的core/alloc时走src/bootstrap/src/core/build_steps/compile.rs的no_std分支并且有一个针对 BPF 的细节compiler-builtins-mem特性始终开启但compiler-builtins-c基于 compiler-rt 的 C 实现对bpf目标会跳过if !target.starts_with(bpf)才追加该特性。这避免了为 BPF 链接 C 语言编写的 compiler-rt 目标文件——BPF 目标的全部内建函数都要求是纯 Rust 实现。编写并编译 BPF 程序方式一在 config.toml 中声明默认目标[build] target [bpfel-unknown-none]之后cargo build无需再指定--target。方式二命令行直接指定推荐无需自建编译器cargo nightly build -Z build-stdcore --target bpfel-unknown-none-Z build-stdcore因为该目标没有预编译的core此标志让 cargo 用本机的rust-src组件现场构建core--target bpfel-unknown-none显式指定 BPF 目标需要 nightly 工具链以启用build-std这类不稳定特性。一个典型的 Cargo 工程中还需注意# Cargo.toml示意 [package] name my-bpf-program version 0.1.0 edition 2021 [profile.release] # BPF 程序体积敏感通常配合 LTO 与较小的 codegen-units lto true codegen-units 1 panic abort说明panic abort与目标的PanicStrategy::Abort一致BPF 不允许栈展开任何基于 unwind 的 panic 方案都不适用。链接器与调试信息BPF 目标使用 bpf-linkerLLVM 位码链接器完成链接这与前文LinkerFlavor::Bpf、obj_is_bitcode: true的底层设置一一对应未来可能迁移到 GNU 风格的链接器进展见 rust-lang/rust 仓库的 issue 135175bpf object linkingBPF 有自己的调试信息格式BTFBPF Type Format-g编译选项产生的调试信息将以 BTF 呈现供内核 CO-RECompile Once, Run Everywhere与 bpftool 等工具消费。错误处理在无栈展开的世界里处理 panicBPF 中没有栈展开stack unwinding的概念因此程序必须以可恢复的方式处理错误。文档给出的标准 panic 处理函数如下#[cfg(not(test))] #[panic_handler] fn panic(_info: core::panic::PanicInfo) - ! { loop {} }要点#[cfg(not(test))]确保测试构建运行在宿主上不受影响loop {}是故意的挂起BPF verifier 禁止无限循环一旦程序包含任何可能 panic 的代码虚拟机将拒绝加载该程序。因此这个处理器本质上是一个永不触发的哨兵——它只保证编译通过真正的错误路径必须通过返回码等可恢复机制处理。测试策略在宿主机上跑单元测试BPF 字节码必须运行在 BPF 虚拟机中——无论是 Linux 内核自带的还是用户态实现如 rbpf。而这类虚拟机都不支持运行 Rust 的#[test]函数原因之一就是不支持 panic 机制。因此文档推荐的方案是单元测试在宿主系统上运行并用条件编译保证测试模块只出现在宿主构建中#[cfg(all(not(target_arch bpf), test))] mod test {}即当目标是 BPFtarget_arch bpf时不编译测试模块其余情况下宿主架构 test profile才编译。这样cargo test在宿主上正常执行全部断言而cargo build --target bpf*-unknown-none产出的 BPF 程序不含任何测试代码。交叉编译与端到端字节序匹配BPF 程序永远是从宿主例如x86_64-unknown-linux-*交叉编译到 BPF 目标的x86_64-unknown-linux-gnu --(cross compile)-- bpfel-unknown-none两个必须遵守的匹配规则字节序匹配所选 BPF 目标的端序必须与目标 BPF 虚拟机宿主的端序一致大端宿主选bpfeb-unknown-none小端宿主选bpfel-unknown-none架构相关类型由开发者处理宿主架构会影响 BPF 程序应使用的类型。例如 kprobe、fprobe、uprobe 等动态跟踪机制可以通过pt_regs结构体访问宿主寄存器而该结构体随架构而不同。这种差异不是编译器关心的范畴而应由开发者处理。文档特别指出AyaRust 生态中编写 Linux BPF 程序的主要库也是 BPF 目标最主要的消费者通过提供aya-ebpf-ctycrate 来解决它提供与core::ffi类似的类型别名并允许通过CARGO_CFG_BPF_TARGET_ARCH环境变量指定虚拟机目标例如CARGO_CFG_BPF_TARGET_ARCHaarch64 cargo nightly build -Z build-stdcore --target bpfel-unknown-noneCARGO_CFG_*环境变量会注入为--cfg标志从而让aya-ebpf-cty中的cfg门控类型定义按目标架构生效。链接 C 代码build.rs 中的 clang 集成BPF 程序允许链接由 clang 从 C 源码编译出的位码或目标文件。做法是在build.rs中通过rustc-link-lib指令完成链接。文档给出的完整示例use std::{env, process::Command}; let out_dir env::var(OUT_DIR).unwrap(); let c_module my_module.bpf.c; let s Command::new(clang) .arg(-I) .arg(src/) .arg(-O2) .arg(-emit-llvm) .arg(-target) .arg(bpf) .arg(-c) .arg(-g) .arg(c_module) .arg(-o) .arg(format!({out_dir}/my_module.bpf.o)) .status() .unwrap(); assert!(s.success()); println!(cargo:rustc-link-searchnative{out_dir}); println!(cargo:rustc-link-liblink-arg{out_dir}/my_module.bpf.o);逐步拆解这条 clang 命令的关键参数参数作用-I src/添加 C 头文件搜索路径-O2优化级别-emit-llvm输出 LLVM 位码与 BPF 目标obj_is_bitcode的链接要求一致-target bpf指定 BPF 目标三态clang 侧对应bpfel/bpfeb未指明端序时按宿主默认-c只编译不链接-g生成调试信息BPF 下即 BTF-o {out_dir}/my_module.bpf.o输出到 cargo 的 OUT_DIR保证构建可重现随后两条println!向 cargo 暴露链接配置cargo:rustc-link-searchnative{out_dir}告诉 rustc 在OUT_DIR中查找原生库cargo:rustc-link-liblink-arg{out_dir}/my_module.bpf.o把该目标文件作为链接参数直接传给 bpf-linker。assert!(s.success())保证 clang 失败时构建立即报错build.rs也会在 clang 或源文件变化时由 cargo 自动重跑。结语一条完整的 BPF 开发链路综合当前仓库的源码与平台支持文档一条可落地的 BPF 开发链路可以总结为准备 nightly 工具链 rust-src组件安装 bpf-linker选择与部署端序匹配的 triplebpfel-unknown-none/bpfeb-unknown-none用cargo nightly build -Z build-stdcore --target bpf*-unknown-none编译或把目标写进config.toml后自建带目标支持的 rustc通过#[panic_handler]挂起式处理器满足 verifier 约束用返回码承载错误用#[cfg(all(not(target_arch bpf), test))]把单元测试留在宿主机需要 C 依赖时在build.rs中调用 clang 产出位码并以link-arg方式链接。上述每一步都能在当前仓库中找到对应的源码证据目标定义在compiler/rustc_target/src/spec/targets/公共选项在compiler/rustc_target/src/spec/base/bpf.rs目标注册在compiler/rustc_target/src/spec/mod.rsbootstrap 对 BPF 的 no_std 编译路径则在src/bootstrap/src/core/build_steps/compile.rs。如需查阅 BPF 目标在 rustc 测试套件中的表现可继续浏览仓库的tests/目录关于平台支持列表的完整说明可回到本文主文档src/doc/rustc/src/platform-support/bpf-unknown-none.md查看。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表