Assembly
Giới thiệu về Assembly

Giới thiệu về Assembly

Các lệnh

Bây giờ bạn đã hiểu về các thanh ghi và vùng nhớ của sBPF, hãy cùng xem xét các lệnh để thao tác với chúng.

Các lệnh là những thao tác cơ bản mà chương trình của bạn thực hiện—cộng các số, tải từ bộ nhớ, hoặc nhảy đến các vị trí khác nhau.

Lệnh là gì?

Các lệnh là những khối xây dựng cơ bản của chương trình. Hãy coi chúng như những mệnh lệnh chỉ dẫn chính xác cho bộ xử lý phải làm gì:

  • add64 r1, r2: "Cộng các giá trị trong thanh ghi r1r2, lưu kết quả vào r1"

  • ldxdw r0, [r10 - 8]: "Tải 8 byte từ bộ nhớ stack vào thanh ghi r0"

  • jeq r1, 42, +3: "Nếu r1 bằng 42, nhảy tiến 3 lệnh"

Mỗi lệnh thực hiện chính xác một thao tác và được mã hóa thành chính xác 8 byte dữ liệu để VM giải mã ngay lập tức.

Các lệnh sBPF làm việc với các kích thước dữ liệu khác nhau:

  • byte = 8 bit (1 byte)

  • halfword = 16 bit (2 byte)

  • word = 32 bit (4 byte)

  • doubleword = 64 bit (8 byte)

Hầu hết các thao tác sBPF sử dụng giá trị 64-bit (doublewords) vì các thanh ghi là 64 bit, nhưng bạn có thể tải và lưu trữ các kích thước nhỏ hơn khi cần thiết để tăng hiệu quả.

Danh mục và định dạng các lệnh

Khi bạn biên dịch mã Rust, C, hoặc assembly, toolchain sẽ tạo ra một luồng các lệnh có kích thước cố định 8-byte được đóng gói vào phần .text của ELF.

Mỗi lệnh tuân theo một cấu trúc nhất quán mà VM có thể giải mã trong một lần duyệt:

text
   1 byte    4 bits   4 bits     2 bytes         4 bytes
┌──────────┬────────┬────────┬──────────────┬──────────────────┐
│  opcode  │  dst   │  src   │   offset     │      imm         │
└──────────┴────────┴────────┴──────────────┴──────────────────┘
  • opcode: Xác định loại thao tác. 3 bit cao nhất xác định lớp lệnh (số học, bộ nhớ, nhảy, gọi, thoát), trong khi 5 bit thấp hơn chỉ định chính xác biến thể (cộng, nhân, tải, nhảy-nếu-bằng).

  • dst: Số thanh ghi đích (r0–r10) nơi kết quả được lưu trữ—kết quả số học, giá trị đã tải, hoặc giá trị trả về từ hàm bổ trợ.

  • src: Thanh ghi nguồn cung cấp đầu vào. Đối với số học hai toán hạng (add r1, r2), nó cung cấp giá trị thứ hai. Đối với các thao tác bộ nhớ, nó có thể cung cấp địa chỉ cơ sở. Đối với các biến thể tức thời (add r1, 10), 4 bit này được gộp vào mã lệnh.

  • offset: Một số nguyên nhỏ điều chỉnh hành vi lệnh. Đối với tải/lưu, nó được thêm vào địa chỉ nguồn để đạt đến [src + offset]. Đối với các lệnh nhảy, đó là mục tiêu nhánh tương đối được đo bằng số lệnh.

  • imm: Trường giá trị tức thời. Các thao tác số học sử dụng nó cho hằng số (add r1, 42), CALL sử dụng nó cho số syscall (sol_log = 16), và các thao tác bộ nhớ có thể coi nó như một con trỏ tuyệt đối.

Các danh mục lệnh

Các loại lệnh khác nhau sử dụng các trường này theo những cách cụ thể:

  • Di chuyển dữ liệu: Di chuyển giá trị giữa các thanh ghi và bộ nhớ:

sbpf
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=unused
  • Số học: Thực hiện các phép toán học:

sbpf
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=100
  • Luồng điều khiển: Thay đổi trình tự thực thi:

sbpf
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=42

Mã hóa opcode

Mã hóa opcode nắm bắt nhiều thông tin ngoài loại thao tác:

  • Lớp lệnh: Số học, bộ nhớ, nhảy, gọi, v.v.

  • Kích thước thao tác: các thao tác 32-bit hay 64-bit

  • Loại nguồn: Thanh ghi hay giá trị tức thời

  • Thao tác cụ thể: Cộng hay trừ, tải so hay lưu trữ, v.v.

Việc này tạo ra các mã thao tác riêng biệt cho các biến thể lệnh. Ví dụ, add64 r1, r2 (nguồn thanh ghi) sử dụng mã thao tác khác với add64 r1, 42 (nguồn giá trị tức thời). Tương tự, add64add32 có các mã thao tác khác nhau cho các kích thước thao tác khác nhau.

Các thao tác số học phân biệt thêm giữa các biến thể có dấu và không dấu. udiv64 coi các giá trị là không dấu (0 đến 18 nghìn tỉ), trong khi sdiv64 xử lý các giá trị có dấu (-9 nghìn tỉ đến +9 nghìn tỉ).

Thực thi lệnh

Mã opcode xác định cách VM diễn giải các trường còn lại.

Khi VM gặp add64 r1, r2, nó đọc opcode và nhận ra đây là một thao tác số học 64-bit sử dụng hai thanh ghi:

Trường dst chỉ ra kết quả lưu vào r1, trường src chỉ định r2 là toán hạng thứ hai, và các trường offsetimmediate bị bỏ qua.

Đối với add64 r1, 42, opcode thay đổi để chỉ ra một thao tác tức thời. Bây giờ dst vẫn trỏ đến r1, nhưng src trở nên vô nghĩa, và trường immediate cung cấp toán hạng thứ hai (42).

Các thao tác bộ nhớ kết hợp nhiều trường một cách có ý nghĩa:

Đối với ldxdw r1, [r2+8], opcode chỉ ra một thao tác tải bộ nhớ 64-bit, dst nhận giá trị được tải, src cung cấp địa chỉ cơ sở, và offset (8) được thêm vào để tạo địa chỉ cuối cùng r2 + 8.

Các lệnh điều khiển luồng tuân theo cùng một mẫu:

Khi bạn viết jeq r1, r2, +5, mã opcode mã hóa một lệnh nhảy có điều kiện so sánh hai thanh ghi. Nếu r1 bằng r2, VM sẽ thêm offset (5) vào bộ đếm chương trình, nhảy về phía trước 5 lệnh.

Mã opcode xác định những trường nào có ý nghĩa. Định dạng lệnh vẫn không đổi: mã opcode cho bạn biết cách diễn giải từng trường, loại bỏ các chế độ địa chỉ phức tạp hoặc các trường hợp đặc biệt.

Lệnh gọi hàm và Syscall

Cơ chế gọi của sBPF đã phát triển qua nhiều phiên bản để có được sự rõ ràng và độ bảo mật tốt hơn. Cho đến sBPF v3, call imm phục vụ hai mục đích: giá trị tức thời xác định liệu bạn đang gọi một hàm nội bộ hay đang gọi một syscall.

Runtime phân biệt giữa các loại này dựa trên phạm vi giá trị tức thời, với các số syscall thường là các số nguyên dương nhỏ như 16 cho sol_log.

Từ sBPF v3 trở đi, các lệnh được tách biệt để có hành vi rõ ràng. call giờ đây xử lý các lệnh gọi hàm nội bộ bằng cách sử dụng offset tương đối, trong khi syscall imm gọi rõ ràng các hàm runtime. Sự tách biệt này làm rõ ý định của bytecode và cho phép xác minh tốt hơn.

Các lệnh gọi gián tiếp thông qua callx cũng đã phát triển. Các phiên bản trước mã hóa thanh ghi đích trong trường tức thời, nhưng từ v2 trở đi, nó được mã hóa trong trường thanh ghi nguồn để đảm bảo tính nhất quán với định dạng lệnh chung.

Bảng tham khảo mã Opcode

Các thao tác tải bộ nhớ

opcodeMnemonicMô tả
lddwlddw dst, immTải giá trị tức thời 64-bit (slot đầu tiên)
lddwlddw dst, immTải giá trị tức thời 64-bit (slot thứ hai)
ldxwldxw dst, [src + off]Tải word từ bộ nhớ
ldxhldxh dst, [src + off]Tải halfword từ bộ nhớ
ldxbldxb dst, [src + off]Tải byte từ bộ nhớ
ldxdwldxdw dst, [src + off]Tải doubleword từ bộ nhớ

Các thao tác lưu trữ bộ nhớ

opcodeMnemonicMô tả
stwstw [dst + off], immLưu trữ word tức thời
sthsth [dst + off], immLưu trữ halfword tức thời
stbstb [dst + off], immLưu trữ byte tức thời
stdwstdw [dst + off], immLưu trữ doubleword tức thời
stxwstxw [dst + off], srcLưu trữ word từ thanh ghi
stxhstxh [dst + off], srcLưu trữ halfword từ thanh ghi
stxbstxb [dst + off], srcLưu trữ byte từ thanh ghi
stxdwstxdw [dst + off], srcLưu trữ doubleword từ thanh ghi

Các thao tác số học (64-bit)

opcodeMnemonicMô tả
add64add64 dst, immCộng tức thời
add64add64 dst, srcCộng thanh ghi
sub64sub64 dst, immTrừ tức thời
sub64sub64 dst, srcTrừ thanh ghi
mul64mul64 dst, immNhân tức thời
mul64mul64 dst, srcNhân thanh ghi
div64div64 dst, immChia tức thời (không dấu)
div64div64 dst, srcChia thanh ghi (không dấu)
sdiv64sdiv64 dst, immChia tức thời (có dấu)
sdiv64sdiv64 dst, srcChia thanh ghi (có dấu)
mod64mod64 dst, immChia lấy dư tức thời (không dấu)
mod64mod64 dst, srcChia lấy dư thanh ghi (không dấu)
smod64smod64 dst, immChia lấy dư tức thời (có dấu)
smod64smod64 dst, srcChia lấy dư thanh ghi (có dấu)
neg64neg64 dstĐổi dấu

Các thao tác số học (32-bit)

opcodeMnemonicMô tả
add32add32 dst, immCộng tức thời (32-bit)
add32add32 dst, srcCộng thanh ghi (32-bit)
sub32sub32 dst, immTrừ tức thời (32-bit)
sub32sub32 dst, srcTrừ thanh ghi (32-bit)
mul32mul32 dst, immNhân tức thời (32-bit)
mul32mul32 dst, srcNhân thanh ghi (32-bit)
div32div32 dst, immChia tức thời (32-bit)
div32div32 dst, srcChia thanh ghi (32-bit)
sdiv32sdiv32 dst, immChia tức thời (32-bit có dấu)
sdiv32sdiv32 dst, srcChia thanh ghi (32-bit có dấu)
mod32mod32 dst, immChia lấy dư tức thời (32-bit)
mod32mod32 dst, srcChia lấy dư thanh ghi (32-bit)
smod32smod32 dst, immChia lấy dư tức thời (32-bit có dấu)
smod32smod32 dst, srcChia lấy dư thanh ghi (32-bit có dấu)

Các phép toán logic (64-bit)

opcodeMnemonicDescription
or64or64 dst, immPhép OR bit tức thời
or64or64 dst, srcPhép OR bit thanh ghi
and64and64 dst, immPhép AND bit tức thời
and64and64 dst, srcPhép AND bit thanh ghi
lsh64lsh64 dst, immDịch trái tức thời
lsh64lsh64 dst, srcDịch trái thanh ghi
rsh64rsh64 dst, immDịch phải tức thời
rsh64rsh64 dst, srcDịch phải thanh ghi
xor64xor64 dst, immPhép XOR bit tức thời
xor64xor64 dst, srcPhép XOR bit thanh ghi
mov64mov64 dst, immDi chuyển tức thời
mov64mov64 dst, srcDi chuyển thanh ghi
arsh64arsh64 dst, immDịch phải số học tức thời
arsh64arsh64 dst, srcDịch phải số học thanh ghi

Các phép toán logic (32-bit)

opcodeMnemonicDescription
or32or32 dst, immPhép OR bit tức thời (32-bit)
or32or32 dst, srcPhép OR bit thanh ghi (32-bit)
and32and32 dst, immPhép AND bit tức thời (32-bit)
and32and32 dst, srcPhép AND bit thanh ghi (32-bit)
lsh32lsh32 dst, immDịch trái tức thời (32-bit)
lsh32lsh32 dst, srcDịch trái thanh ghi (32-bit)
rsh32rsh32 dst, immDịch phải tức thời (32-bit)
rsh32rsh32 dst, srcDịch phải thanh ghi (32-bit)
xor32xor32 dst, immPhép XOR bit tức thời (32-bit)
xor32xor32 dst, srcPhép XOR bit thanh ghi (32-bit)
mov32mov32 dst, immDi chuyển tức thời (32-bit)
mov32mov32 dst, srcDi chuyển thanh ghi (32-bit)
arsh32arsh32 dst, immDịch phải số học tức thời (32-bit)
arsh32arsh32 dst, srcDịch phải số học thanh ghi (32-bit)

Các thao tác điều khiển luồng

opcodeMnemonicDescription
jaja offNhảy vô điều kiện (jump 0 = nhảy đến lệnh kế tiếp)
jeqjeq dst, imm, offNhảy nếu bằng giá trị tức thời
jeqjeq dst, src, offNhảy nếu bằng giá trị thanh ghi
jgtjgt dst, imm, offNhảy nếu lớn hơn giá trị tức thời (không dấu)
jgtjgt dst, src, offNhảy nếu lớn hơn giá trị thanh ghi (không dấu)
jgejge dst, imm, offNhảy nếu lớn hơn hoặc bằng giá trị tức thời (không dấu)
jgejge dst, src, offNhảy nếu lớn hơn hoặc bằng giá trị thanh ghi (không dấu)
jsetjset dst, imm, offNhảy nếu bit được thiết lập (mặt nạ tức thời)
jsetjset dst, src, offNhảy nếu bit được thiết lập (mặt nạ thanh ghi)
jnejne dst, imm, offNhảy nếu không bằng giá trị tức thời
jnejne dst, src, offNhảy nếu không bằng giá trị thanh ghi
jsgtjsgt dst, imm, offNhảy nếu lớn hơn giá trị tức thời (có dấu)
jsgtjsgt dst, src, offNhảy nếu lớn hơn giá trị thanh ghi (có dấu)
jsgejsge dst, imm, offNhảy nếu lớn hơn hoặc bằng giá trị tức thời (có dấu)
jsgejsge dst, src, offNhảy nếu lớn hơn hoặc bằng giá trị thanh ghi (có dấu)
jltjlt dst, imm, offNhảy nếu nhỏ hơn giá trị tức thời (không dấu)
jltjlt dst, src, offNhảy nếu nhỏ hơn giá trị thanh ghi (không dấu)
jlejle dst, imm, offNhảy nếu nhỏ hơn hoặc bằng giá trị tức thời (không dấu)
jlejle dst, src, offNhảy nếu nhỏ hơn hoặc bằng giá trị thanh ghi (không dấu)
jsltjslt dst, imm, offNhảy nếu nhỏ hơn giá trị tức thời (có dấu)
jsltjslt dst, src, offNhảy nếu nhỏ hơn giá trị thanh ghi (có dấu)
jslejsle dst, imm, offNhảy nếu nhỏ hơn hoặc bằng giá trị tức thời (có dấu)
jslejsle dst, src, offNhảy nếu nhỏ hơn hoặc bằng giá trị thanh ghi (có dấu)

Các thao tác gọi hàm

opcodeMnemonicMô tả
callcall imm hoặc syscall immGọi hàm hoặc syscall
callxcallx immGọi gián tiếp (thanh ghi trong trường imm)
exitexit hoặc returnTrở về từ hàm

Các thao tác hoán đổi byte

opcodeMnemonicMô tả
be16be16 dstHoán đổi byte (16-bit)
be32be32 dstHoán đổi byte (32-bit)
be64be64 dstHoán đổi byte (64-bit)
le16le16 dstMặt nạ bit (16-bit)
le32le32 dstMặt nạ bit (32-bit)
le64le64 dstKhông thao tác (64-bit)
Nội dung
Xem mã nguồn
Blueshift © 2025Commit: e573eab