struct Insn [src]
a single BPF instruction
Fields
code: u8
dst: u4
src: u4
off: i16
imm: i32
Members
- add (Function)
- alu (Function)
- alu_and (Function)
- alu_or (Function)
- AluOp (enum)
- arsh (Function)
- be (Function)
- call (Function)
- div (Function)
- exit (Function)
- ja (Function)
- jeq (Function)
- jge (Function)
- jgt (Function)
- jle (Function)
- jlt (Function)
- jmp (Function)
- JmpOp (enum)
- jne (Function)
- jset (Function)
- jsge (Function)
- jsgt (Function)
- jsle (Function)
- jslt (Function)
- ld_abs (Function)
- ld_dw1 (Function)
- ld_dw2 (Function)
- ld_ind (Function)
- ld_map_fd1 (Function)
- ld_map_fd2 (Function)
- ldx (Function)
- le (Function)
- lsh (Function)
- mod (Function)
- mov (Function)
- mul (Function)
- neg (Function)
- Reg (enum)
- rsh (Function)
- Size (enum)
- st (Function)
- stx (Function)
- sub (Function)
- xadd (Function)
- xor (Function)
Source
pub const Insn = packed struct {
code: u8,
dst: u4,
src: u4,
off: i16,
imm: i32,
/// r0 - r9 are general purpose 64-bit registers, r10 points to the stack
/// frame
pub const Reg = enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };
const Source = enum(u1) { reg, imm };
const Mode = enum(u8) {
imm = IMM,
abs = ABS,
ind = IND,
mem = MEM,
len = LEN,
msh = MSH,
};
pub const AluOp = enum(u8) {
add = ADD,
sub = SUB,
mul = MUL,
div = DIV,
alu_or = OR,
alu_and = AND,
lsh = LSH,
rsh = RSH,
neg = NEG,
mod = MOD,
xor = XOR,
mov = MOV,
arsh = ARSH,
};
pub const Size = enum(u8) {
byte = B,
half_word = H,
word = W,
double_word = DW,
};
pub const JmpOp = enum(u8) {
ja = JA,
jeq = JEQ,
jgt = JGT,
jge = JGE,
jset = JSET,
jlt = JLT,
jle = JLE,
jne = JNE,
jsgt = JSGT,
jsge = JSGE,
jslt = JSLT,
jsle = JSLE,
};
const ImmOrReg = union(Source) {
reg: Reg,
imm: i32,
};
fn imm_reg(code: u8, dst: Reg, src: anytype, off: i16) Insn {
const imm_or_reg = if (@TypeOf(src) == Reg or @typeInfo(@TypeOf(src)) == .enum_literal)
ImmOrReg{ .reg = @as(Reg, src) }
else
ImmOrReg{ .imm = src };
const src_type: u8 = switch (imm_or_reg) {
.imm => K,
.reg => X,
};
return Insn{
.code = code | src_type,
.dst = @intFromEnum(dst),
.src = switch (imm_or_reg) {
.imm => 0,
.reg => |r| @intFromEnum(r),
},
.off = off,
.imm = switch (imm_or_reg) {
.imm => |i| i,
.reg => 0,
},
};
}
pub fn alu(comptime width: comptime_int, op: AluOp, dst: Reg, src: anytype) Insn {
const width_bitfield = switch (width) {
32 => ALU,
64 => ALU64,
else => @compileError("width must be 32 or 64"),
};
return imm_reg(width_bitfield | @intFromEnum(op), dst, src, 0);
}
pub fn mov(dst: Reg, src: anytype) Insn {
return alu(64, .mov, dst, src);
}
pub fn add(dst: Reg, src: anytype) Insn {
return alu(64, .add, dst, src);
}
pub fn sub(dst: Reg, src: anytype) Insn {
return alu(64, .sub, dst, src);
}
pub fn mul(dst: Reg, src: anytype) Insn {
return alu(64, .mul, dst, src);
}
pub fn div(dst: Reg, src: anytype) Insn {
return alu(64, .div, dst, src);
}
pub fn alu_or(dst: Reg, src: anytype) Insn {
return alu(64, .alu_or, dst, src);
}
pub fn alu_and(dst: Reg, src: anytype) Insn {
return alu(64, .alu_and, dst, src);
}
pub fn lsh(dst: Reg, src: anytype) Insn {
return alu(64, .lsh, dst, src);
}
pub fn rsh(dst: Reg, src: anytype) Insn {
return alu(64, .rsh, dst, src);
}
pub fn neg(dst: Reg) Insn {
return alu(64, .neg, dst, 0);
}
pub fn mod(dst: Reg, src: anytype) Insn {
return alu(64, .mod, dst, src);
}
pub fn xor(dst: Reg, src: anytype) Insn {
return alu(64, .xor, dst, src);
}
pub fn arsh(dst: Reg, src: anytype) Insn {
return alu(64, .arsh, dst, src);
}
pub fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {
return imm_reg(JMP | @intFromEnum(op), dst, src, off);
}
pub fn ja(off: i16) Insn {
return jmp(.ja, .r0, 0, off);
}
pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jeq, dst, src, off);
}
pub fn jgt(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jgt, dst, src, off);
}
pub fn jge(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jge, dst, src, off);
}
pub fn jlt(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jlt, dst, src, off);
}
pub fn jle(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jle, dst, src, off);
}
pub fn jset(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jset, dst, src, off);
}
pub fn jne(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jne, dst, src, off);
}
pub fn jsgt(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jsgt, dst, src, off);
}
pub fn jsge(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jsge, dst, src, off);
}
pub fn jslt(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jslt, dst, src, off);
}
pub fn jsle(dst: Reg, src: anytype, off: i16) Insn {
return jmp(.jsle, dst, src, off);
}
pub fn xadd(dst: Reg, src: Reg) Insn {
return Insn{
.code = STX | XADD | DW,
.dst = @intFromEnum(dst),
.src = @intFromEnum(src),
.off = 0,
.imm = 0,
};
}
fn ld(mode: Mode, size: Size, dst: Reg, src: Reg, imm: i32) Insn {
return Insn{
.code = @intFromEnum(mode) | @intFromEnum(size) | LD,
.dst = @intFromEnum(dst),
.src = @intFromEnum(src),
.off = 0,
.imm = imm,
};
}
pub fn ld_abs(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
return ld(.abs, size, dst, src, imm);
}
pub fn ld_ind(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
return ld(.ind, size, dst, src, imm);
}
pub fn ldx(size: Size, dst: Reg, src: Reg, off: i16) Insn {
return Insn{
.code = MEM | @intFromEnum(size) | LDX,
.dst = @intFromEnum(dst),
.src = @intFromEnum(src),
.off = off,
.imm = 0,
};
}
fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {
return Insn{
.code = LD | DW | IMM,
.dst = @intFromEnum(dst),
.src = @intFromEnum(src),
.off = 0,
.imm = @as(i32, @intCast(@as(u32, @truncate(imm)))),
};
}
fn ld_imm_impl2(imm: u64) Insn {
return Insn{
.code = 0,
.dst = 0,
.src = 0,
.off = 0,
.imm = @as(i32, @intCast(@as(u32, @truncate(imm >> 32)))),
};
}
pub fn ld_dw1(dst: Reg, imm: u64) Insn {
return ld_imm_impl1(dst, .r0, imm);
}
pub fn ld_dw2(imm: u64) Insn {
return ld_imm_impl2(imm);
}
pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {
return ld_imm_impl1(dst, @as(Reg, @enumFromInt(PSEUDO_MAP_FD)), @as(u64, @intCast(map_fd)));
}
pub fn ld_map_fd2(map_fd: fd_t) Insn {
return ld_imm_impl2(@as(u64, @intCast(map_fd)));
}
pub fn st(size: Size, dst: Reg, off: i16, imm: i32) Insn {
return Insn{
.code = MEM | @intFromEnum(size) | ST,
.dst = @intFromEnum(dst),
.src = 0,
.off = off,
.imm = imm,
};
}
pub fn stx(size: Size, dst: Reg, off: i16, src: Reg) Insn {
return Insn{
.code = MEM | @intFromEnum(size) | STX,
.dst = @intFromEnum(dst),
.src = @intFromEnum(src),
.off = off,
.imm = 0,
};
}
fn endian_swap(endian: std.builtin.Endian, comptime size: Size, dst: Reg) Insn {
return Insn{
.code = switch (endian) {
.big => 0xdc,
.little => 0xd4,
},
.dst = @intFromEnum(dst),
.src = 0,
.off = 0,
.imm = switch (size) {
.byte => @compileError("can't swap a single byte"),
.half_word => 16,
.word => 32,
.double_word => 64,
},
};
}
pub fn le(comptime size: Size, dst: Reg) Insn {
return endian_swap(.little, size, dst);
}
pub fn be(comptime size: Size, dst: Reg) Insn {
return endian_swap(.big, size, dst);
}
pub fn call(helper: Helper) Insn {
return Insn{
.code = JMP | CALL,
.dst = 0,
.src = 0,
.off = 0,
.imm = @intFromEnum(helper),
};
}
/// exit BPF program
pub fn exit() Insn {
return Insn{
.code = JMP | EXIT,
.dst = 0,
.src = 0,
.off = 0,
.imm = 0,
};
}
}