sBPF 暫存器與記憶體模型
當你的 sBPF 程式執行時,它會使用兩種主要的儲存機制:處理器內建的 11 個高速暫存器,以及組織化的記憶體區域來存放其他所有內容。
Registers
暫存器就像處理器內建的 11 個編號儲存槽(r0 到 r10)。每個暫存器可存放一個 64 位元的值,並能即時存取,毫無延遲。當你執行 add64 r1, r2 時,處理器會立即從兩個暫存器中檢索值並執行計算。
權衡很簡單:你總共只有 11 個儲存槽,因此需要策略性地使用它們來存放你正在處理的值。
sBPF 為每個暫存器分配了特定的角色:
| 暫存器 | 角色 | 是否由被呼叫方保存? | 使用說明 |
r0 | 返回值 | 否 | 函數結果、系統呼叫返回值 |
r1 | 輸入緩衝區指標 | 否 | 進入時指向 0x400000000 |
r2-r5 | 暫存/參數 | 否 | 輔助函數參數、臨時值 |
r6-r9 | 通用用途 | 是 | 必須在呼叫間保留 |
r10 | 幀指標 | 是,只讀 | 堆疊基底,不能直接修改 |
暫存器 r6 到 r9 是 由被呼叫方保存 的,這意味著它們的值在函數呼叫期間會保持不變。使用這些暫存器來存放需要在函數調用中保留的重要數據。
其餘的暫存器(r0-r5)在系統呼叫和函數呼叫期間會被覆蓋。
暫存器 r10 是「特殊的」,因為它作為你的幀指標,指向你的堆疊空間基底並保持只讀。你可以使用負偏移量(例如 [r10 - 8] 和 [r10 - 16])來訪問堆疊變數。
記憶體
雖然暫存器保存活躍使用的值,但記憶體則存儲其他所有內容。sBPF 將記憶體組織成固定區域,並在所有程式中具有相同的佈局:
| 區域 | 起始地址 | 用途 | 大小/備註 |
| Text | 0x100000000 | 程式碼和唯讀數據 | 程式二進制文件 |
| Stack | 0x200000000 | 局部變量 | 每個堆疊框架 4 KiB,最大呼叫深度為 64 |
| Heap | 0x300000000 | 動態分配 | 32 KiB |
| Input | 0x400000000 | 程式參數 | 序列化的帳戶和指令數據 |
Text 區域 包含可執行程式碼和唯讀數據,例如字串常量。使用
.quad指令定義的數據通常位於此處。Stack 區域 存放局部變量。
r10指向堆疊基址,您可以使用負偏移量訪問局部變量,例如[r10 - 16]、[r10 - 24]等。Input 區域 包含程式的參數。在進入時,
r1指向此區域 (0x400000000),允許您讀取序列化的帳戶數據和傳遞給程式的指令參數。
這種固定佈局提供了三個主要優勢:通過程式間記憶體隔離實現安全性,通過在所有驗證器中使用相同地址實現確定性,以及通過編譯器針對已知位置的優化提升性能。
使用暫存器和記憶體
以下是暫存器和記憶體如何在實踐中協同工作的方式:
.globl entrypoint
entrypoint:
// On entry: r1 points to input data at 0x400000000
ldxdw r0, [r1 + 0] // Load first 8 bytes from input into r0
mov64 r2, 42 // Put 42 in register r2
add64 r0, r2 // Add them: r0 = r0 + r2
stxdw [r10 - 8], r0 // Store result on stack
mov64 r0, 0 // Return success
exit此程式展示了典型模式:
使用暫存器
r1(輸入指針)從記憶體中讀取使用暫存器
r0和r2進行計算使用暫存器
r10(框架指針)訪問堆疊記憶體在暫存器
r0中返回結果
工作流程遵循一致的模式:將數據從記憶體加載到暫存器,使用暫存器進行計算,然後在需要時將結果存回記憶體。
堆疊使用
堆疊使用 r10 作為框架指標,指向當前堆疊框架的基址(最高地址)。局部變量使用負偏移量:
// Store a value on the stack
mov64 r0, 42
stxdw [r10 - 8], r0 // Store at first stack slot
// Load it back
ldxdw r1, [r10 - 8] // Load from first stack slot堆疊槽通常為 8 字節寬,因此連續的變量使用類似 [r10 - 8]、[r10 - 16]、[r10 - 24] 等偏移量。
程式的進入與退出
程式從標記為 .globl(通常是 entrypoint)的符號開始執行。初始暫存器狀態是最小且可預測的:
.globl entrypoint
entrypoint:
// On entry:
// r1 = 0x400000000 (input buffer pointer)
// r10 = 0x200001000 (0x200000000 stack start + 0x1000 4KiB frame size)
// r0, r2-r9 = 0 (all other registers zeroed)
// Your program logic here
mov64 r0, 0 // Success code (0 = SUCCESS)
exit // Return to runtime退出行為取決於呼叫深度:
在呼叫深度為 0 時,退出會以
r0作為結果代碼終止程式。在更深的呼叫層次,退出的行為類似於 return 語句,恢復呼叫者保存的暫存器(
r6-r9)和呼叫者的框架指標(r10),然後在返回地址繼續執行。
運行時通過隱式的序言和尾聲代碼自動處理設置和拆卸,包括初始暫存器狀態配置和最終返回處理。