(bpf*-unknown-none)完全指南:從目標(biāo)構(gòu)建到程序編寫與調(diào)試)
Rust 編譯器 BPF 目標(biāo)bpf*-unknown-none完全指南從目標(biāo)構(gòu)建到程序編寫與調(diào)試【免費(fèi)下載鏈接】rustEmpowering everyone to build reliable and efficient software.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ru/rust本文以 Rust 官方編譯器倉庫當(dāng)前倉庫即 rust-lang/rust 源碼樹中src/doc/rustc/src/platform-support/bpf-unknown-none.md為核心系統(tǒng)講解在 Rust 生態(tài)中為 64 位 BPF 虛擬機(jī)編寫no_std程序的全部環(huán)節(jié)目標(biāo)規(guī)格與要求、Rust 編譯器自身的構(gòu)建方式、基于build-std的程序編譯流程、panic 與錯(cuò)誤處理、宿主側(cè)單元測(cè)試策略、跨平臺(tái)交叉編譯注意事項(xiàng)以及鏈接由 clang 編譯的 C 位碼的build.rs方案。讀完本文你將掌握bpfel-unknown-none/bpfeb-unknown-none這兩個(gè) Tier 3 目標(biāo)的完整使用鏈路并能基于當(dāng)前倉庫源碼理解每個(gè)環(huán)節(jié)背后的編譯期決策。BPF 目標(biāo)概覽兩個(gè) triple 與它們的定位BPFBerkeley Packet Filter是一種運(yùn)行在 Linux 內(nèi)核中的虛擬機(jī)字節(jié)碼。Rust 為 64 位 BPF 虛擬機(jī)提供了兩個(gè) Tier 3 目標(biāo)Target triple字節(jié)序說明bpfeb-unknown-nonebig endian大端llvm_target為bpfebbpfel-unknown-nonelittle endian小端llvm_target為bpfel兩者的目標(biāo)定義位于compiler/rustc_target/src/spec/targets/bpfeb_unknown_none.rs與compiler/rustc_target/src/spec/targets/bpfel_unknown_none.rs。從源碼可以確認(rèn)幾個(gè)關(guān)鍵事實(shí)兩者均為Tier 3、std: false不提供標(biāo)準(zhǔn)庫、host_tools: falsepointer_width均為 64屬于 64 位目標(biāo)arch均為Arch::Bpfdata layout 分別以E-大端和e-小端開頭公共的底層選項(xiàng)由compiler/rustc_target/src/spec/base/bpf.rs中的opts(endian)統(tǒng)一生成。這兩個(gè) triple 在 rustc 的目標(biāo)注冊(cè)表中通過compiler/rustc_target/src/spec/mod.rs的(bpfeb-unknown-none, bpfeb_unknown_none)與(bpfel-unknown-none, bpfel_unknown_none)兩條記錄生效。目標(biāo)維護(hù)者BPF 目標(biāo)的維護(hù)者為 nagisa 與 vadorovsky對(duì)應(yīng)平臺(tái)支持文檔中列出的維護(hù)者名單。作為 Tier 3 目標(biāo)它不包含自動(dòng)構(gòu)建、測(cè)試或發(fā)布保證相關(guān)變更通常在 rustc 倉庫中以 PR 形式經(jīng)維護(hù)者 review 后合入。目標(biāo)的核心技術(shù)約束bpf*-unknown-none的目標(biāo)選項(xiàng)在compiler/rustc_target/src/spec/base/bpf.rs中集中定義以下特性直接決定了你在該目標(biāo)上能寫什么、不能寫什么選項(xiàng)值含義allow_asmtrue允許使用內(nèi)聯(lián)匯編linker_flavorLinkerFlavor::Bpf使用 BPF 專用鏈接器bpf-linkeratomic_casfalse不支持 CAS 原子操作min_atomic_width/max_atomic_widthSome(64)僅支持 64 位原子操作寬度panic_strategyPanicStrategy::Abortpanic 直接中止不展開棧obj_is_bitcodetrue產(chǎn)物為 LLVM 位碼bitcode而非原生機(jī)器碼singlethreadtrue單線程模型no_builtinstrue不鏈接編譯器內(nèi)建函數(shù)merge_functionsMergeFunctions::Disabled禁用函數(shù)合并原因見下關(guān)于merge_functions的禁用源碼注釋給出了明確理由舊內(nèi)核不支持 bpf-to-bpf 調(diào)用而在新內(nèi)核上用戶態(tài)程序在BPF_PROG_LOAD之前仍需自行完成重定位并非所有 BPF 庫都實(shí)現(xiàn)了這一步。關(guān)于原子操作注釋還提醒v3及以上 CPU 在 LLVM 中支持 32 位原子操作但 rustc 選擇統(tǒng)一將最小原子寬度設(shè)為 64以避免選項(xiàng)隨 target cpu 變化帶來困惑。運(yùn)行環(huán)境的版本要求文檔給出的宿主內(nèi)核版本門檻如下絕大多數(shù)宿主架構(gòu)Linux 內(nèi)核4.18及以上該版本引入了 BTFRISC-V 宿主5.7及以上PowerPC32 宿主5.13及以上LoongArch 宿主6.1及以上。這些版本的差別源于各架構(gòu)對(duì) BTF 與 BPF 基礎(chǔ)設(shè)施的支持先后不同。BPF 程序由虛擬機(jī)內(nèi)的 JIT 編譯器翻譯為宿主原生指令而運(yùn)行前的合法性檢查由內(nèi)核的 BPF verifier 完成。基礎(chǔ)工具鏈要求需要帶rust-src組件的 Rust 工具鏈用于build-std構(gòu)建core必須額外安裝bpf-linker一個(gè)基于 LLVM 位碼的鏈接器是 aya-rs 組織維護(hù)的項(xiàng)目extern C遵循 BPF ABI 調(diào)用約定產(chǎn)物為 ELF 格式準(zhǔn)確說是包含 ELF 元數(shù)據(jù)的 LLVM 位碼對(duì)象最終由 bpf-linker 產(chǎn)出 ELF。從 bootstrap 源碼看src/bootstrap/src/utils/helpers.rs的use_host_linker明確將bpf列為不使用宿主鏈接器的目標(biāo)與 wasm32、nvptx 等并列印證了 BPF 目標(biāo)必須走專用鏈接器這一設(shè)計(jì)。構(gòu)建支持 BPF 的 Rust 編譯器Rust 官方暫不發(fā)布 BPF 目標(biāo)的預(yù)編譯產(chǎn)物因此如果你希望 rustc 自帶該目標(biāo)的core需要在構(gòu)建 rustc 時(shí)把目標(biāo)加入config.toml[build] target [bpfeb-unknown-none, bpfel-unknown-none]對(duì)應(yīng)到倉庫的構(gòu)建體系bootstrap 在編譯no_std目標(biāo)的core/alloc時(shí)走src/bootstrap/src/core/build_steps/compile.rs的no_std分支并且有一個(gè)針對(duì) BPF 的細(xì)節(jié)compiler-builtins-mem特性始終開啟但compiler-builtins-c基于 compiler-rt 的 C 實(shí)現(xiàn)對(duì)bpf目標(biāo)會(huì)跳過if !target.starts_with(bpf)才追加該特性。這避免了為 BPF 鏈接 C 語言編寫的 compiler-rt 目標(biāo)文件——BPF 目標(biāo)的全部?jī)?nèi)建函數(shù)都要求是純 Rust 實(shí)現(xiàn)。編寫并編譯 BPF 程序方式一在 config.toml 中聲明默認(rèn)目標(biāo)[build] target [bpfel-unknown-none]之后cargo build無需再指定--target。方式二命令行直接指定推薦無需自建編譯器cargo nightly build -Z build-stdcore --target bpfel-unknown-none-Z build-stdcore因?yàn)樵撃繕?biāo)沒有預(yù)編譯的core此標(biāo)志讓 cargo 用本機(jī)的rust-src組件現(xiàn)場(chǎng)構(gòu)建core--target bpfel-unknown-none顯式指定 BPF 目標(biāo)需要 nightly 工具鏈以啟用build-std這類不穩(wěn)定特性。一個(gè)典型的 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與目標(biāo)的PanicStrategy::Abort一致BPF 不允許棧展開任何基于 unwind 的 panic 方案都不適用。鏈接器與調(diào)試信息BPF 目標(biāo)使用 bpf-linkerLLVM 位碼鏈接器完成鏈接這與前文LinkerFlavor::Bpf、obj_is_bitcode: true的底層設(shè)置一一對(duì)應(yīng)未來可能遷移到 GNU 風(fēng)格的鏈接器進(jìn)展見 rust-lang/rust 倉庫的 issue 135175bpf object linkingBPF 有自己的調(diào)試信息格式BTFBPF Type Format-g編譯選項(xiàng)產(chǎn)生的調(diào)試信息將以 BTF 呈現(xiàn)供內(nèi)核 CO-RECompile Once, Run Everywhere與 bpftool 等工具消費(fèi)。錯(cuò)誤處理在無棧展開的世界里處理 panicBPF 中沒有棧展開stack unwinding的概念因此程序必須以可恢復(fù)的方式處理錯(cuò)誤。文檔給出的標(biāo)準(zhǔn) panic 處理函數(shù)如下#[cfg(not(test))] #[panic_handler] fn panic(_info: core::panic::PanicInfo) - ! { loop {} }要點(diǎn)#[cfg(not(test))]確保測(cè)試構(gòu)建運(yùn)行在宿主上不受影響loop {}是故意的掛起B(yǎng)PF verifier 禁止無限循環(huán)一旦程序包含任何可能 panic 的代碼虛擬機(jī)將拒絕加載該程序。因此這個(gè)處理器本質(zhì)上是一個(gè)永不觸發(fā)的哨兵——它只保證編譯通過真正的錯(cuò)誤路徑必須通過返回碼等可恢復(fù)機(jī)制處理。測(cè)試策略在宿主機(jī)上跑單元測(cè)試BPF 字節(jié)碼必須運(yùn)行在 BPF 虛擬機(jī)中——無論是 Linux 內(nèi)核自帶的還是用戶態(tài)實(shí)現(xiàn)如 rbpf。而這類虛擬機(jī)都不支持運(yùn)行 Rust 的#[test]函數(shù)原因之一就是不支持 panic 機(jī)制。因此文檔推薦的方案是單元測(cè)試在宿主系統(tǒng)上運(yùn)行并用條件編譯保證測(cè)試模塊只出現(xiàn)在宿主構(gòu)建中#[cfg(all(not(target_arch bpf), test))] mod test {}即當(dāng)目標(biāo)是 BPFtarget_arch bpf時(shí)不編譯測(cè)試模塊其余情況下宿主架構(gòu) test profile才編譯。這樣cargo test在宿主上正常執(zhí)行全部斷言而cargo build --target bpf*-unknown-none產(chǎn)出的 BPF 程序不含任何測(cè)試代碼。交叉編譯與端到端字節(jié)序匹配BPF 程序永遠(yuǎn)是從宿主例如x86_64-unknown-linux-*交叉編譯到 BPF 目標(biāo)的x86_64-unknown-linux-gnu --(cross compile)-- bpfel-unknown-none兩個(gè)必須遵守的匹配規(guī)則字節(jié)序匹配所選 BPF 目標(biāo)的端序必須與目標(biāo) BPF 虛擬機(jī)宿主的端序一致大端宿主選bpfeb-unknown-none小端宿主選bpfel-unknown-none架構(gòu)相關(guān)類型由開發(fā)者處理宿主架構(gòu)會(huì)影響 BPF 程序應(yīng)使用的類型。例如 kprobe、fprobe、uprobe 等動(dòng)態(tài)跟蹤機(jī)制可以通過pt_regs結(jié)構(gòu)體訪問宿主寄存器而該結(jié)構(gòu)體隨架構(gòu)而不同。這種差異不是編譯器關(guān)心的范疇而應(yīng)由開發(fā)者處理。文檔特別指出AyaRust 生態(tài)中編寫 Linux BPF 程序的主要庫也是 BPF 目標(biāo)最主要的消費(fèi)者通過提供aya-ebpf-ctycrate 來解決它提供與core::ffi類似的類型別名并允許通過CARGO_CFG_BPF_TARGET_ARCH環(huán)境變量指定虛擬機(jī)目標(biāo)例如CARGO_CFG_BPF_TARGET_ARCHaarch64 cargo nightly build -Z build-stdcore --target bpfel-unknown-noneCARGO_CFG_*環(huán)境變量會(huì)注入為--cfg標(biāo)志從而讓aya-ebpf-cty中的cfg門控類型定義按目標(biāo)架構(gòu)生效。鏈接 C 代碼build.rs 中的 clang 集成BPF 程序允許鏈接由 clang 從 C 源碼編譯出的位碼或目標(biāo)文件。做法是在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 命令的關(guān)鍵參數(shù)參數(shù)作用-I src/添加 C 頭文件搜索路徑-O2優(yōu)化級(jí)別-emit-llvm輸出 LLVM 位碼與 BPF 目標(biāo)obj_is_bitcode的鏈接要求一致-target bpf指定 BPF 目標(biāo)三態(tài)clang 側(cè)對(duì)應(yīng)bpfel/bpfeb未指明端序時(shí)按宿主默認(rèn)-c只編譯不鏈接-g生成調(diào)試信息BPF 下即 BTF-o {out_dir}/my_module.bpf.o輸出到 cargo 的 OUT_DIR保證構(gòu)建可重現(xiàn)隨后兩條println!向 cargo 暴露鏈接配置cargo:rustc-link-searchnative{out_dir}告訴 rustc 在OUT_DIR中查找原生庫cargo:rustc-link-liblink-arg{out_dir}/my_module.bpf.o把該目標(biāo)文件作為鏈接參數(shù)直接傳給 bpf-linker。assert!(s.success())保證 clang 失敗時(shí)構(gòu)建立即報(bào)錯(cuò)build.rs也會(huì)在 clang 或源文件變化時(shí)由 cargo 自動(dòng)重跑。結(jié)語一條完整的 BPF 開發(fā)鏈路綜合當(dāng)前倉庫的源碼與平臺(tái)支持文檔一條可落地的 BPF 開發(fā)鏈路可以總結(jié)為準(zhǔn)備 nightly 工具鏈 rust-src組件安裝 bpf-linker選擇與部署端序匹配的 triplebpfel-unknown-none/bpfeb-unknown-none用cargo nightly build -Z build-stdcore --target bpf*-unknown-none編譯或把目標(biāo)寫進(jìn)config.toml后自建帶目標(biāo)支持的 rustc通過#[panic_handler]掛起式處理器滿足 verifier 約束用返回碼承載錯(cuò)誤用#[cfg(all(not(target_arch bpf), test))]把單元測(cè)試留在宿主機(jī)需要 C 依賴時(shí)在build.rs中調(diào)用 clang 產(chǎn)出位碼并以link-arg方式鏈接。上述每一步都能在當(dāng)前倉庫中找到對(duì)應(yīng)的源碼證據(jù)目標(biāo)定義在compiler/rustc_target/src/spec/targets/公共選項(xiàng)在compiler/rustc_target/src/spec/base/bpf.rs目標(biāo)注冊(cè)在compiler/rustc_target/src/spec/mod.rsbootstrap 對(duì) BPF 的 no_std 編譯路徑則在src/bootstrap/src/core/build_steps/compile.rs。如需查閱 BPF 目標(biāo)在 rustc 測(cè)試套件中的表現(xiàn)可繼續(xù)瀏覽倉庫的tests/目錄關(guān)于平臺(tái)支持列表的完整說明可回到本文主文檔src/doc/rustc/src/platform-support/bpf-unknown-none.md查看。【免費(fèi)下載鏈接】rustEmpowering everyone to build reliable and efficient software.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ru/rust創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考