Skip to content

Commit

Permalink
svm: include elf binary tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexicon226 committed Jan 4, 2025
1 parent f2668d7 commit c3f8b3a
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 18 deletions.
Binary file added data/test-elfs/reloc_64_64_sbpfv1.so
Binary file not shown.
Binary file added data/test-elfs/reloc_64_relative_data_sbpfv1.so
Binary file not shown.
Binary file added data/test-elfs/reloc_64_relative_sbpfv1.so
Binary file not shown.
Binary file added data/test-elfs/rodata_section_sbpfv1.so
Binary file not shown.
Binary file added data/test-elfs/static_internal_call_sbpfv1.so
Binary file not shown.
Binary file added data/test-elfs/syscall_reloc_64_32.so
Binary file not shown.
1 change: 1 addition & 0 deletions src/sig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ pub const TEST_STATE_DIR = "data/test-state/";
pub const FUZZ_DATA_DIR = "data/fuzz-data/";
pub const BENCHMARK_RESULTS_DIR = "results/";
pub const GENESIS_DIR = "data/genesis-files/";
pub const ELF_DATA_DIR = "data/test-elfs/";
11 changes: 2 additions & 9 deletions src/svm/Executable.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const std = @import("std");
const ebpf = @import("ebpf.zig");
const Elf = @import("Elf.zig");
const memory = @import("memory.zig");
const syscalls = @import("syscalls.zig");
const Vm = @import("Vm.zig");
const Executable = @This();

Expand Down Expand Up @@ -487,16 +488,8 @@ pub fn Registry(T: type) type {
};
}

pub const SyscallError = error{
InvalidVirtualAddress,
AccessNotMapped,
SyscallAbort,
AccessViolation,
VirtualAccessTooLong,
};

pub const BuiltinProgram = struct {
functions: Registry(*const fn (*Vm) SyscallError!void) = .{},
functions: Registry(*const fn (*Vm) syscalls.Error!void) = .{},

pub fn deinit(program: *BuiltinProgram, allocator: std.mem.Allocator) void {
program.functions.deinit(allocator);
Expand Down
2 changes: 1 addition & 1 deletion src/svm/memory.zig
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ test "aligned region" {
);
try expectError(
error.AccessNotMapped,
m.region(INPUT_START + 4),
m.region(INPUT_START + 3),
);
}

Expand Down
29 changes: 21 additions & 8 deletions src/svm/syscalls.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,36 @@ const Executable = @import("Executable.zig");

const Pubkey = sig.core.Pubkey;

pub const Syscall = struct {
name: []const u8,
builtin_fn: *const fn (*Vm) Error!void,
};

pub const Error = error{
InvalidVirtualAddress,
AccessNotMapped,
SyscallAbort,
AccessViolation,
VirtualAccessTooLong,
};

// logging
pub fn printString(vm: *Vm) Executable.SyscallError!void {
pub fn printString(vm: *Vm) Error!void {
const vm_addr = vm.registers.get(.r1);
const len = vm.registers.get(.r2);
const host_addr = try vm.memory_map.vmap(.constant, vm_addr, len);
const string = std.mem.sliceTo(host_addr, 0);
if (!builtin.is_test) std.debug.print("{s}", .{string});
}

pub fn log(vm: *Vm) Executable.SyscallError!void {
pub fn log(vm: *Vm) Error!void {
const vm_addr = vm.registers.get(.r1);
const len = vm.registers.get(.r2);
const host_addr = try vm.memory_map.vmap(.constant, vm_addr, len);
std.debug.print("log: {s}\n", .{host_addr});
}

pub fn log64(vm: *Vm) Executable.SyscallError!void {
pub fn log64(vm: *Vm) Error!void {
const arg1 = vm.registers.get(.r1);
const arg2 = vm.registers.get(.r2);
const arg3 = vm.registers.get(.r3);
Expand All @@ -35,19 +48,19 @@ pub fn log64(vm: *Vm) Executable.SyscallError!void {
);
}

pub fn logPubkey(vm: *Vm) Executable.SyscallError!void {
pub fn logPubkey(vm: *Vm) Error!void {
const pubkey_addr = vm.registers.get(.r1);
const pubkey_bytes = try vm.memory_map.vmap(.constant, pubkey_addr, @sizeOf(Pubkey));
const pubkey: Pubkey = @bitCast(pubkey_bytes[0..@sizeOf(Pubkey)].*);
std.debug.print("log: {}\n", .{pubkey});
}

pub fn logComputeUnits(_: *Vm) Executable.SyscallError!void {
pub fn logComputeUnits(_: *Vm) Error!void {
std.debug.print("TODO: compute budget calculations\n", .{});
}

// memory operators
pub fn memset(vm: *Vm) Executable.SyscallError!void {
pub fn memset(vm: *Vm) Error!void {
const dst_addr = vm.registers.get(.r1);
const scalar = vm.registers.get(.r2);
const len = vm.registers.get(.r3);
Expand All @@ -56,7 +69,7 @@ pub fn memset(vm: *Vm) Executable.SyscallError!void {
@memset(host_addr, @truncate(scalar));
}

pub fn memcpy(vm: *Vm) Executable.SyscallError!void {
pub fn memcpy(vm: *Vm) Error!void {
const dst_addr = vm.registers.get(.r1);
const src_addr = vm.registers.get(.r2);
const len = vm.registers.get(.r3);
Expand All @@ -67,6 +80,6 @@ pub fn memcpy(vm: *Vm) Executable.SyscallError!void {
}

// special
pub fn abort(_: *Vm) Executable.SyscallError!void {
pub fn abort(_: *Vm) Error!void {
return error.SyscallAbort;
}
100 changes: 100 additions & 0 deletions src/svm/tests.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
const std = @import("std");
const sig = @import("../sig.zig");
const Executable = @import("Executable.zig");
const memory = @import("memory.zig");
const Vm = @import("Vm.zig");
const syscalls = @import("syscalls.zig");
const ebpf = @import("ebpf.zig");
const Elf = @import("Elf.zig");

const Region = memory.Region;
const MemoryMap = memory.MemoryMap;
Expand Down Expand Up @@ -1539,3 +1543,99 @@ test "fixed stack out of bounds" {
\\ exit
, error.AccessNotMapped);
}

fn testElf(path: []const u8, expected: anytype) !void {
return testElfWithSyscalls(path, &.{}, expected);
}

fn testElfWithSyscalls(
path: []const u8,
extra_syscalls: []const syscalls.Syscall,
expected: anytype,
) !void {
const allocator = std.testing.allocator;

const input_file = try std.fs.cwd().openFile(path, .{});
const bytes = try input_file.readToEndAlloc(allocator, ebpf.MAX_FILE_SIZE);
defer allocator.free(bytes);

var loader: Executable.BuiltinProgram = .{};
defer loader.deinit(allocator);

for (extra_syscalls) |syscall| {
_ = try loader.functions.registerFunctionHashed(
allocator,
syscall.name,
syscall.builtin_fn,
);
}

const elf = try Elf.parse(bytes, allocator, &loader);

var executable = try Executable.fromElf(allocator, &elf);
defer executable.deinit(allocator);

const stack_memory = try allocator.alloc(u8, 4096);
defer allocator.free(stack_memory);

const m = try MemoryMap.init(&.{
executable.getRoRegion(),
Region.init(.mutable, stack_memory, memory.STACK_START),
Region.init(.constant, &.{}, memory.HEAP_START),
Region.init(.mutable, &.{}, memory.INPUT_START),
}, .v1);

var vm = try Vm.init(allocator, &executable, m, &loader);
defer vm.deinit();

const result = vm.run();
try expectEqual(expected, result);
}

test "BPF_64_64 sbpfv1" {
// [ 1] .text PROGBITS 0000000000000120 000120 000018 00 AX 0 0 8
// prints the address of the first byte in the .text section
try testElf(
sig.ELF_DATA_DIR ++ "reloc_64_64_sbpfv1.so",
memory.PROGRAM_START + 0x120,
);
}

test "BPF_64_RELATIVE data sbpv1" {
// [ 1] .text PROGBITS 00000000000000e8 0000e8 000020 00 AX 0 0 8
// [ 2] .rodata PROGBITS 0000000000000108 000108 000019 01 AMS 0 0 1
// prints the address of the first byte in the .rodata sections
try testElf(
sig.ELF_DATA_DIR ++ "reloc_64_relative_data_sbpfv1.so",
memory.PROGRAM_START + 0x108,
);
}

test "BPF_64_RELATIVE sbpv1" {
try testElf(
sig.ELF_DATA_DIR ++ "reloc_64_relative_sbpfv1.so",
memory.PROGRAM_START + 0x138,
);
}

test "load elf rodata sbpfv1" {
try testElf(
sig.ELF_DATA_DIR ++ "rodata_section_sbpfv1.so",
42,
);
}

test "static internal call sbpv1" {
try testElf(
sig.ELF_DATA_DIR ++ "static_internal_call_sbpfv1.so",
10,
);
}

test "syscall reloc 64_32" {
try testElfWithSyscalls(
sig.ELF_DATA_DIR ++ "syscall_reloc_64_32.so",
&.{.{ .name = "log", .builtin_fn = syscalls.printString }},
0,
);
}

0 comments on commit c3f8b3a

Please sign in to comment.