Instruksi
Sekarang setelah Anda memahami register dan region memori sBPF, mari kita periksa instruksi yang memanipulasinya.
Instruksi adalah operasi fundamental yang dilakukan program Anda—menambahkan angka, memuat dari memori, atau melompat ke lokasi yang berbeda.
Apa itu Instruksi?
Instruksi adalah blok dasar pembangun program Anda. Anggap saja sebagai perintah yang memberi tahu prosesor persis apa yang harus dilakukan:
add64 r1, r2: "Tambahkan nilai dalam registerr1danr2, simpan hasilnya dir1"ldxdw r0, [r10 - 8]: "Muat 8 byte dari memori stack ke dalam registerr0"jeq r1, 42, +3: "Jikar1sama dengan 42, lompat maju 3 instruksi"
Setiap instruksi melakukan tepat satu operasi dan dikodekan sebagai data 8 byte yang tepat untuk dekoding VM secara instan.
Instruksi sBPF bekerja dengan ukuran data yang berbeda:
byte = 8 bit (1 byte)
halfword = 16 bit (2 byte)
word = 32 bit (4 byte)
doubleword = 64 bit (8 byte)
Sebagian besar operasi sBPF menggunakan nilai 64-bit (doubleword) karena register berukuran 64 bit, tetapi Anda dapat memuat dan menyimpan ukuran yang lebih kecil saat diperlukan untuk efisiensi.
Kategori dan Format Instruksi
Ketika Anda mengompilasi kode Rust, C, atau assembly, toolchain menghasilkan aliran instruksi lebar-tetap 8-byte yang dikemas ke dalam bagian .text ELF Anda.
Setiap instruksi mengikuti struktur yang konsisten yang dapat didekode VM dalam satu kali proses:
1 byte 4 bits 4 bits 2 bytes 4 bytes
┌──────────┬────────┬────────┬──────────────┬──────────────────┐
│ opcode │ dst │ src │ offset │ imm │
└──────────┴────────┴────────┴──────────────┴──────────────────┘opcode: Mendefinisikan jenis operasi. 3 bit teratas memilih kelas instruksi (aritmatika, memori, lompat, panggil, keluar), sementara 5 bit bawah menentukan varian yang tepat (tambah, kalikan, muat, lompat-jika-sama).dst: Nomor register tujuan (r0–r10) tempat hasil disimpan—hasil aritmatika, nilai yang dimuat, atau pengembalian fungsi pembantu.src: Register sumber yang menyediakan input. Untuk aritmatika dua-operand (add r1, r2), ini menyediakan nilai kedua. Untuk operasi memori, ini dapat menyediakan alamat dasar. Untuk varian langsung (add r1, 10), 4 bit ini digabungkan ke dalam opcode.offset: Integer kecil yang memodifikasi perilaku instruksi. Untuk load/store, ini ditambahkan ke alamat sumber untuk mencapai[src + offset]. Untuk lompatan, ini adalah target cabang relatif yang diukur dalam instruksi.imm: Bidang nilai langsung. Operasi aritmatika menggunakannya untuk konstanta (add r1, 42),CALLmenggunakannya untuk nomor syscall (sol_log = 16), dan operasi memori mungkin memperlakukannya sebagai pointer absolut.
Kategori Instruksi
Jenis instruksi yang berbeda menggunakan bidang-bidang ini dengan cara spesifik:
Pergerakan Data: Memindahkan nilai antara register dan memori:
mov64 r1, 42 // Put immediate value 42 into r1
// opcode=move_imm, dst=1, src=unused, imm=42
ldxdw r0, [r10 - 8] // Load 8 bytes from stack into r0
// opcode=load64, dst=0, src=10, offset=-8, imm=unused
stxdw [r1 + 16], r0 // Store r0 to memory at [r1 + 16]
// opcode=store64, dst=1, src=0, offset=16, imm=unusedAritmatika: Melakukan operasi matematika:
add64 r1, r2 // r1 = r1 + r2
// opcode=add_reg, dst=1, src=2, offset=unused, imm=unused
add64 r1, 100 // r1 = r1 + 100
// opcode=add_imm, dst=1, src=unused, offset=unused, imm=100Alur Kontrol: Mengubah urutan eksekusi:
ja +5 // Jump forward 5 instructions unconditionally
// opcode=jump, dst=unused, src=unused, offset=5, imm=unused
jeq r1, r2, +3 // If r1 == r2, jump forward 3 instructions
// opcode=jump_eq_reg, dst=1, src=2, offset=3, imm=unused
jeq r1, 42, +3 // If r1 == 42, jump forward 3 instructions
// opcode=jump_eq_imm, dst=1, src=unused, offset=3, imm=42Pengkodean Opcode
Pengkodean opcode menangkap beberapa informasi di luar jenis operasi:
Kelas instruksi: Aritmatika, memori, lompatan, panggilan, dll.
Ukuran operasi: Operasi 32-bit vs 64-bit
Jenis sumber: Register vs nilai langsung
Operasi spesifik: Tambah vs kurang, muat vs simpan, dll.
Ini menciptakan opcode yang berbeda untuk varian instruksi. Misalnya, add64 r1, r2 (sumber register) menggunakan opcode yang berbeda dari add64 r1, 42 (sumber langsung). Demikian juga, add64 dan add32 memiliki opcode berbeda untuk ukuran operasi yang berbeda.
Operasi aritmatika lebih lanjut membedakan antara varian bertanda dan tanpa tanda. udiv64 memperlakukan nilai sebagai tanpa tanda (0 hingga 18 kuintiliun), sementara sdiv64 menangani nilai bertanda (-9 kuintiliun hingga +9 kuintiliun).
Eksekusi Instruksi
Opcode menentukan bagaimana VM menafsirkan bidang-bidang yang tersisa.
Ketika VM menemukan add64 r1, r2, ia membaca opcode dan mengenalinya sebagai operasi aritmatika 64-bit menggunakan dua register:
Bidang dst menunjukkan hasilnya masuk ke r1, bidang src menentukan r2 sebagai operan kedua, dan bidang offset dan immediate diabaikan.
Untuk add64 r1, 42, opcode berubah untuk menunjukkan operasi langsung. Sekarang dst masih menunjuk ke r1, tetapi src menjadi tidak berarti, dan bidang immediate menyediakan operan kedua (42).
Operasi memori menggabungkan beberapa bidang secara bermakna:
Untuk ldxdw r1, [r2+8], opcode menunjukkan pemuatan memori 64-bit, dst menerima nilai yang dimuat, src menyediakan alamat dasar, dan offset (8) ditambahkan untuk membuat alamat akhir r2 + 8.
Instruksi aliran kontrol mengikuti pola yang sama:
Ketika Anda menulis jeq r1, r2, +5, opcode mengenkode lompatan bersyarat yang membandingkan dua register. Jika r1 sama dengan r2, VM akan menambahkan offset (5) ke program counter, melompat maju 5 instruksi.
Pemanggilan Fungsi dan Syscall
Mekanisme pemanggilan sBPF berkembang di berbagai versi untuk kejelasan dan keamanan yang lebih baik. Hingga sBPF v3, call imm memiliki dua tujuan: nilai immediate menentukan apakah Anda memanggil fungsi internal atau menggunakan syscall.
Runtime membedakan antara keduanya berdasarkan rentang nilai immediate, dengan nomor syscall biasanya berupa bilangan bulat positif kecil seperti 16 untuk sol_log.
Dari sBPF v3 dan seterusnya, instruksi dipisahkan untuk perilaku yang eksplisit. call sekarang menangani pemanggilan fungsi internal menggunakan offset relatif, sementara syscall imm secara eksplisit memanggil fungsi runtime. Pemisahan ini membuat intensi bytecode jelas dan memungkinkan verifikasi yang lebih baik.
Pemanggilan tidak langsung melalui callx juga berkembang. Versi sebelumnya mengenkode register target di bidang immediate, tetapi dari v2 dan seterusnya, register target dienkode di bidang register sumber untuk konsistensi dengan format instruksi umum.
Tabel Referensi Opcode
Operasi Load Memory
| opcode | Mnemonic | Deskripsi |
| lddw | lddw dst, imm | Load immediate 64-bit (slot pertama) |
| lddw | lddw dst, imm | Load immediate 64-bit (slot kedua) |
| ldxw | ldxw dst, [src + off] | Load word dari memory |
| ldxh | ldxh dst, [src + off] | Load halfword dari memory |
| ldxb | ldxb dst, [src + off] | Load byte dari memory |
| ldxdw | ldxdw dst, [src + off] | Load doubleword dari memory |
Operasi Penyimpanan Memori
| opcode | Mnemonic | Deskripsi |
| stw | stw [dst + off], imm | Simpan word immediate |
| sth | sth [dst + off], imm | Simpan halfword immediate |
| stb | stb [dst + off], imm | Simpan byte immediate |
| stdw | stdw [dst + off], imm | Simpan doubleword immediate |
| stxw | stxw [dst + off], src | Simpan word dari register |
| stxh | stxh [dst + off], src | Simpan halfword dari register |
| stxb | stxb [dst + off], src | Simpan byte dari register |
| stxdw | stxdw [dst + off], src | Simpan doubleword dari register |
Operasi Aritmatika (64-bit)
| opcode | Mnemonic | Deskripsi |
| add64 | add64 dst, imm | Tambah immediate |
| add64 | add64 dst, src | Tambah register |
| sub64 | sub64 dst, imm | Kurang immediate |
| sub64 | sub64 dst, src | Kurang register |
| mul64 | mul64 dst, imm | Kali immediate |
| mul64 | mul64 dst, src | Kali register |
| div64 | div64 dst, imm | Bagi immediate (unsigned) |
| div64 | div64 dst, src | Bagi register (unsigned) |
| sdiv64 | sdiv64 dst, imm | Bagi immediate (signed) |
| sdiv64 | sdiv64 dst, src | Bagi register (signed) |
| mod64 | mod64 dst, imm | Modulo immediate (unsigned) |
| mod64 | mod64 dst, src | Modulo register (unsigned) |
| smod64 | smod64 dst, imm | Modulo immediate (signed) |
| smod64 | smod64 dst, src | Modulo register (signed) |
| neg64 | neg64 dst | Negasi |
Operasi Aritmatika (32-bit)
| opcode | Mnemonic | Deskripsi |
| add32 | add32 dst, imm | Tambah immediate (32-bit) |
| add32 | add32 dst, src | Tambah register (32-bit) |
| sub32 | sub32 dst, imm | Kurang immediate (32-bit) |
| sub32 | sub32 dst, src | Kurang register (32-bit) |
| mul32 | mul32 dst, imm | Kali immediate (32-bit) |
| mul32 | mul32 dst, src | Kali register (32-bit) |
| div32 | div32 dst, imm | Bagi immediate (32-bit) |
| div32 | div32 dst, src | Bagi register (32-bit) |
| sdiv32 | sdiv32 dst, imm | Bagi immediate (signed 32-bit) |
| sdiv32 | sdiv32 dst, src | Bagi register (signed 32-bit) |
| mod32 | mod32 dst, imm | Modulo immediate (32-bit) |
| mod32 | mod32 dst, src | Modulo register (32-bit) |
| smod32 | smod32 dst, imm | Modulo immediate (signed 32-bit) |
| smod32 | smod32 dst, src | Modulo register (signed 32-bit) |
Operasi Logika (64-bit)
| opcode | Mnemonic | Deskripsi |
| or64 | or64 dst, imm | Bitwise OR langsung |
| or64 | or64 dst, src | Bitwise OR register |
| and64 | and64 dst, imm | Bitwise AND langsung |
| and64 | and64 dst, src | Bitwise AND register |
| lsh64 | lsh64 dst, imm | Geser kiri langsung |
| lsh64 | lsh64 dst, src | Geser kiri register |
| rsh64 | rsh64 dst, imm | Geser kanan langsung |
| rsh64 | rsh64 dst, src | Geser kanan register |
| xor64 | xor64 dst, imm | Bitwise XOR langsung |
| xor64 | xor64 dst, src | Bitwise XOR register |
| mov64 | mov64 dst, imm | Pindah langsung |
| mov64 | mov64 dst, src | Pindah register |
| arsh64 | arsh64 dst, imm | Geser kanan aritmatika langsung |
| arsh64 | arsh64 dst, src | Geser kanan aritmatika register |
Operasi Logika (32-bit)
| opcode | Mnemonic | Deskripsi |
| or32 | or32 dst, imm | Bitwise OR langsung (32-bit) |
| or32 | or32 dst, src | Bitwise OR register (32-bit) |
| and32 | and32 dst, imm | Bitwise AND langsung (32-bit) |
| and32 | and32 dst, src | Bitwise AND register (32-bit) |
| lsh32 | lsh32 dst, imm | Geser kiri langsung (32-bit) |
| lsh32 | lsh32 dst, src | Geser kiri register (32-bit) |
| rsh32 | rsh32 dst, imm | Geser kanan langsung (32-bit) |
| rsh32 | rsh32 dst, src | Geser kanan register (32-bit) |
| xor32 | xor32 dst, imm | Bitwise XOR langsung (32-bit) |
| xor32 | xor32 dst, src | Bitwise XOR register (32-bit) |
| mov32 | mov32 dst, imm | Pindah langsung (32-bit) |
| mov32 | mov32 dst, src | Pindah register (32-bit) |
| arsh32 | arsh32 dst, imm | Geser kanan arit langsung (32-bit) |
| arsh32 | arsh32 dst, src | Geser kanan arit register (32-bit) |
Operasi Alur Kontrol
| opcode | Mnemonic | Description |
| ja | ja off | Lompatan tanpa syarat (jump 0 = lompat ke berikutnya) |
| jeq | jeq dst, imm, off | Lompat jika sama dengan nilai langsung |
| jeq | jeq dst, src, off | Lompat jika sama dengan register |
| jgt | jgt dst, imm, off | Lompat jika lebih besar dari nilai langsung (unsigned) |
| jgt | jgt dst, src, off | Lompat jika lebih besar dari register (unsigned) |
| jge | jge dst, imm, off | Lompat jika lebih besar atau sama dengan nilai langsung (unsigned) |
| jge | jge dst, src, off | Lompat jika lebih besar atau sama dengan register (unsigned) |
| jset | jset dst, imm, off | Lompat jika bit diatur (mask nilai langsung) |
| jset | jset dst, src, off | Lompat jika bit diatur (mask register) |
| jne | jne dst, imm, off | Lompat jika tidak sama dengan nilai langsung |
| jne | jne dst, src, off | Lompat jika tidak sama dengan register |
| jsgt | jsgt dst, imm, off | Lompat jika lebih besar dari nilai langsung (signed) |
| jsgt | jsgt dst, src, off | Lompat jika lebih besar dari register (signed) |
| jsge | jsge dst, imm, off | Lompat jika lebih besar atau sama dengan nilai langsung (signed) |
| jsge | jsge dst, src, off | Lompat jika lebih besar atau sama dengan register (signed) |
| jlt | jlt dst, imm, off | Lompat jika kurang dari nilai langsung (unsigned) |
| jlt | jlt dst, src, off | Lompat jika kurang dari register (unsigned) |
| jle | jle dst, imm, off | Lompat jika kurang dari atau sama dengan nilai langsung (unsigned) |
| jle | jle dst, src, off | Lompat jika kurang dari atau sama dengan register (unsigned) |
| jslt | jslt dst, imm, off | Lompat jika kurang dari nilai langsung (signed) |
| jslt | jslt dst, src, off | Lompat jika kurang dari register (signed) |
| jsle | jsle dst, imm, off | Lompat jika kurang dari atau sama dengan nilai langsung (signed) |
| jsle | jsle dst, src, off | Lompat jika kurang dari atau sama dengan register (signed) |
Operasi Pemanggilan Fungsi
| opcode | Mnemonic | Deskripsi |
| call | call imm atau syscall imm | Memanggil fungsi atau syscall |
| callx | callx imm | Panggilan tidak langsung (register di bidang imm) |
| exit | exit atau return | Kembali dari fungsi |
Operasi Byte Swap
| opcode | Mnemonic | Deskripsi |
| be16 | be16 dst | Byte swap (16-bit) |
| be32 | be32 dst | Byte swap (32-bit) |
| be64 | be64 dst | Byte swap (64-bit) |
| le16 | le16 dst | Bit mask (16-bit) |
| le32 | le32 dst | Bit mask (32-bit) |
| le64 | le64 dst | No op (64-bit) |