Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/asm_parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const expectEqualSlices = std.testing.expectEqualSlices;

/// Represents an operand in an eBPF instruction
pub const Operand = union(enum) {
Register: u8,
Register: i64,
Integer: i64,
Memory: struct {
base: u8,
base: i64,
offset: i64,
},
Nil,
Expand Down Expand Up @@ -69,9 +69,9 @@ fn parseInteger(input: []const u8) !i64 {
}

/// Parses a register from the input
fn parseRegister(input: []const u8) !u8 {
fn parseRegister(input: []const u8) !i64 {
if (input.len < 2 or input[0] != 'r') return error.InvalidRegister;
return std.fmt.parseInt(u8, input[1..], 0);
return std.fmt.parseInt(i64, input[1..], 0);
}

/// Parses an operand from the input
Expand All @@ -95,7 +95,7 @@ fn parseOperand(input: []const u8) !Operand {

/// Parses a single instruction from the input
fn parseInstruction(input: []const u8) !Instruction {
var tokens = std.mem.split(u8, input, " ");
var tokens = std.mem.splitSequence(u8, input, " ");
const name_token = tokens.next() orelse return error.InvalidToken;
const name = try parseIdent(name_token);
var operands: [3]Operand = .{ Operand.Nil, Operand.Nil, Operand.Nil };
Expand All @@ -104,7 +104,7 @@ fn parseInstruction(input: []const u8) !Instruction {
// Get the remaining part of the input after the instruction name
const remaining_input = std.mem.trim(u8, input[name.len..], " ");
if (remaining_input.len > 0) {
var operand_tokens = std.mem.split(u8, remaining_input, ",");
var operand_tokens = std.mem.splitSequence(u8, remaining_input, ",");
while (operand_tokens.next()) |token| {
if (i >= operands.len) break;
operands[i] = try parseOperand(std.mem.trim(u8, token, " "));
Expand All @@ -120,7 +120,7 @@ pub fn parse(input: []const u8) ![]Instruction {
var instructions = std.ArrayList(Instruction).init(std.heap.page_allocator);
defer instructions.deinit();

var lines = std.mem.split(u8, input, "\n");
var lines = std.mem.splitSequence(u8, input, "\n");
while (lines.next()) |line| {
const trimmed_line = std.mem.trim(u8, line, " \t");
if (trimmed_line.len == 0) continue;
Expand Down
52 changes: 31 additions & 21 deletions src/assembler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn makeInstructionMap() !InstructionMap {
// JumpConditional
for (jumpConditions) |jmp| {
try result.put(jmp.name, InstructionMapEntry{ .instType = .JumpConditional, .opcode = ebpf.BPF_JMP | jmp.condition });
try result.put(try std.fmt.allocPrint(std.heap.page_allocator, "{s}32", .{jmp.name}), InstructionMapEntry{ .instType = .JumpConditional, .opcode = ebpf.BPF_JMP32 | jmp.condition });
try result.put(try std.fmt.allocPrint(std.heap.page_allocator, "{s}32", .{jmp.name}), InstructionMapEntry{ .instType = .JumpConditional, .opcode = ebpf.BPF_JMP | jmp.condition });
}

// Endian
Expand All @@ -108,7 +108,7 @@ fn makeInstructionMap() !InstructionMap {
}

/// Creates an eBPF instruction from its components
fn insn(opc: u8, dst: u8, src: u8, off: i16, imm: i32) ebpf.Instruction {
fn ix(opc: u8, dst: u8, src: u8, off: i16, imm: i32) ebpf.Instruction {
return ebpf.Instruction{
.op = opc,
.dst = dst,
Expand All @@ -128,10 +128,10 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
switch (operands[1]) {
.Register => |src_reg| {
if (src_reg > 10) return AssemblerError.InvalidRegister;
return insn(opc | ebpf.BPF_X, reg, src_reg, 0, 0);
return ix(opc | ebpf.BPF_X, @intCast(reg), @intCast(src_reg), 0, 0);
},
.Integer => |imm| {
return insn(opc | ebpf.BPF_K, reg, 0, 0, @intCast(imm));
return ix(opc | ebpf.BPF_K, @intCast(reg), 0, 0, @intCast(imm));
},
else => return AssemblerError.InvalidOperands,
}
Expand All @@ -141,13 +141,13 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
.AluUnary => switch (operands[0]) {
.Register => |reg| {
if (reg > 10) return AssemblerError.InvalidRegister;
return insn(opc, reg, 0, 0, 0);
return ix(opc, @intCast(reg), 0, 0, 0);
},
else => return AssemblerError.InvalidOperands,
},
.LoadAbs => switch (operands[0]) {
.Integer => |imm| {
return insn(opc, 0, 0, 0, @intCast(imm));
return ix(opc, 0, 0, 0, @intCast(imm));
},
else => return AssemblerError.InvalidOperands,
},
Expand All @@ -156,7 +156,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
if (reg > 10) return AssemblerError.InvalidRegister;
switch (operands[1]) {
.Integer => |off| {
return insn(opc, 0, reg, 0, @intCast(off));
return ix(opc, 0, @intCast(reg), 0, @intCast(off));
},
else => return AssemblerError.InvalidOperands,
}
Expand All @@ -168,7 +168,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
if (reg > 10) return AssemblerError.InvalidRegister;
switch (operands[1]) {
.Integer => |imm| {
return insn(opc, reg, 0, 0, @intCast(imm));
return ix(opc, @intCast(reg), 0, 0, @intCast(imm));
},
else => return AssemblerError.InvalidOperands,
}
Expand All @@ -181,7 +181,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
switch (operands[1]) {
.Memory => |mem| {
if (mem.base > 10) return AssemblerError.InvalidRegister;
return insn(opc, dst, mem.base, @intCast(mem.offset), 0);
return ix(opc, @intCast(dst), @intCast(mem.base), @intCast(mem.offset), 0);
},
else => return AssemblerError.InvalidOperands,
}
Expand All @@ -193,7 +193,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
if (mem.base > 10) return AssemblerError.InvalidRegister;
switch (operands[1]) {
.Integer => |imm| {
return insn(opc, mem.base, 0, @intCast(mem.offset), @intCast(imm));
return ix(opc, @intCast(mem.base), 0, @intCast(mem.offset), @intCast(imm));
},
else => return AssemblerError.InvalidOperands,
}
Expand All @@ -206,7 +206,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
switch (operands[1]) {
.Register => |src| {
if (src > 10) return AssemblerError.InvalidRegister;
return insn(opc, mem.base, src, @intCast(mem.offset), 0);
return ix(opc, @intCast(mem.base), @intCast(src), @intCast(mem.offset), 0);
},
else => return AssemblerError.InvalidOperands,
}
Expand All @@ -215,7 +215,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
},
.JumpUnconditional => switch (operands[0]) {
.Integer => |off| {
return insn(opc, 0, 0, @intCast(off), 0);
return ix(opc, 0, 0, @intCast(off), 0);
},
else => return AssemblerError.InvalidOperands,
},
Expand All @@ -227,15 +227,15 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
if (src_reg > 10) return AssemblerError.InvalidRegister;
switch (operands[2]) {
.Integer => |off| {
return insn(opc | ebpf.BPF_X, reg, src_reg, @intCast(off), 0);
return ix(opc | ebpf.BPF_X, @intCast(reg), @intCast(src_reg), @intCast(off), 0);
},
else => return AssemblerError.InvalidOperands,
}
},
.Integer => |imm| {
switch (operands[2]) {
.Integer => |off| {
return insn(opc | ebpf.BPF_K, reg, 0, @intCast(off), @intCast(imm));
return ix(opc | ebpf.BPF_K, @intCast(reg), 0, @intCast(off), @intCast(imm));
},
else => return AssemblerError.InvalidOperands,
}
Expand All @@ -247,7 +247,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
},
.Call => switch (operands[0]) {
.Integer => |imm| {
return insn(opc, 0, 0, 0, @intCast(imm));
return ix(opc, 0, 0, 0, @intCast(imm));
},
else => return AssemblerError.InvalidOperands,
},
Expand Down Expand Up @@ -279,11 +279,11 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In
},
else => return AssemblerError.InvalidOperands,
};
return insn(opc, reg, 0, 0, size);
return ix(opc, @intCast(reg), 0, 0, size);
},
else => return AssemblerError.InvalidOperands,
},
.NoOperand => return insn(opc, 0, 0, 0, 0),
.NoOperand => return ix(opc, 0, 0, 0, 0),
}
}

Expand All @@ -300,7 +300,7 @@ fn assembleInternal(parsed: []Instruction) ![]ebpf.Instruction {
// Special case for lddw
if (entry.instType == .LoadImm and instruction.operands[1] != Operand.Nil) {
if (instruction.operands[1] == Operand.Integer) {
try insns.append(insn(0, 0, 0, 0, @intCast(instruction.operands[1].Integer >> 32)));
try insns.append(ix(0, 0, 0, 0, @intCast(instruction.operands[1].Integer >> 32)));
}
}
}
Expand All @@ -315,11 +315,21 @@ pub fn assemble(src: []const u8) ![]const u8 {

var result = std.ArrayList(u8).init(std.heap.page_allocator);
for (insns) |instruction| {
const instr_array = instruction.to_array();
try result.appendSlice(&instr_array);
try result.appendSlice(&instruction.to_array());
}

return result.toOwnedSlice();
var formatted_result = std.ArrayList(u8).init(std.heap.page_allocator);
for (result.items, 0..) |byte, index| {
if (index % 8 == 0 and index != 0) {
try formatted_result.appendSlice("\n");
}
try std.fmt.format(formatted_result.writer(), "0x{x:0>2}", .{byte});
if (index % 8 != 7 and index != result.items.len - 1) {
try formatted_result.appendSlice(", ");
}
}

return formatted_result.items;
}

/// Possible errors during assembly
Expand Down
44 changes: 43 additions & 1 deletion tests/alu.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const testing = std.testing;
const interpreter = @import("zig-ebpf").interpreter;
const expect = std.testing.expect;
const ebpf = @import("zig-ebpf").ebpf;

const asem = @import("zig-ebpf").assembler;
test "simple_alu64_add" {
var buffer: [512]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
Expand Down Expand Up @@ -38,3 +38,45 @@ test "simple_jsle" {
// std.log.warn("jsgt_imm: {d}", .{result});
try expect(result == 0x1);
}


test "simple_jeq64_reg" {
var buffer: [512]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();

var syscalls_map = std.AutoHashMap(usize, ebpf.Syscall).init(std.testing.allocator);
defer syscalls_map.deinit();
const input =
\\mov r9, 1
\\lsh r9, 32
\\mov64 r0, 0
\\mov64 r1, 0xa
\\mov64 r2, 0xb
\\jeq r1, r2, +5
\\mov64 r0, 1
\\mov64 r1, 0xb
\\or r1, r9
\\jeq r1, r2, +1
\\mov64 r0, 2
\\exit
;

var prog: []u8 = undefined;
var buff: [563]u8 = undefined;
prog = buff[0..563];

const temp = (try asem.assemble(input));
std.log.warn("jsgt_imm: {any}", .{temp.len});
// std.mem.copyForwards(u8, prog, temp);
@memcpy(prog, temp);
// var prog = [_]u8{ 183, 0, 0, 0, 0, 0, 0, 0, 183, 1, 0, 0, 254, 255, 255, 255, 101, 1, 4, 0, 255, 255, 255, 255, 183, 0, 0, 0, 1, 0, 0, 0, 183, 1, 0, 0, 0, 0, 0, 0, 101, 1, 1, 0, 255, 255, 255, 255, 183, 0, 0, 0, 2, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0 };
// var prog = [_]u8{ 183, 9, 0, 0, 1, 0, 0, 0, 103, 9, 0, 0, 32, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 183, 1, 0, 0, 10, 0, 0, 0, 183, 2, 0, 0, 11, 0, 0, 0, 29, 33, 5, 0, 0, 0, 0, 0, 183, 0, 0, 0, 1, 0, 0, 0, 183, 1, 0, 0, 11, 0, 0, 0, 79, 145, 0, 0, 0, 0, 0, 0, 29, 33, 1, 0, 0, 0, 0, 0, 183, 0, 0, 0, 2, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0 };
const mem = [_]u8{ 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd };
const mbuff = [_]u8{0} ** 32;


const result = try interpreter.execute_program(allocator, prog[0..], &mem, &mbuff, &syscalls_map);
try expect(result == 0x2);
}

Loading