diff --git a/build.zig b/build.zig index 2445211fb2..ad7e4bec21 100644 --- a/build.zig +++ b/build.zig @@ -193,9 +193,7 @@ pub const Config = struct { pub fn build(b: *Build) !void { const config = try Config.fromBuild(b); - defer { - if (!config.install and !config.run) disableEmitBin(b); - } + defer if (!config.install and !config.run) disableEmitBin(b); const build_options = b.addOptions(); build_options.addOption(LedgerDB, "ledger_db", config.ledger_db); diff --git a/conformance/src/elf.zig b/conformance/src/elf.zig index 9c1a61bb90..4d16b63de7 100644 --- a/conformance/src/elf.zig +++ b/conformance/src/elf.zig @@ -7,7 +7,6 @@ const ELFLoaderCtx = pb.ELFLoaderCtx; const ElfLoaderEffects = pb.ELFLoaderEffects; const svm = sig.vm; -const syscalls = svm.syscalls; const Elf = svm.Elf; const Executable = svm.Executable; const Config = svm.Config; @@ -49,26 +48,21 @@ fn executeElfTest(ctx: ELFLoaderCtx, allocator: std.mem.Allocator) !ElfLoaderEff .calldests = std.ArrayList(u64).init(allocator), }; - const env = svm.Environment{ .config = .{}, .loader = .{} }; + const env: svm.Environment = .{ + .config = .{}, + .loader = .ALL_DISABLED, + }; var loader = env.loader; - defer loader.deinit(allocator); inline for (.{ - .{ "sol_log_", syscalls.log }, - .{ "sol_log_64_", syscalls.log64 }, - .{ "sol_log_pubkey", syscalls.logPubkey }, - .{ "sol_log_compute_units_", syscalls.logComputeUnits }, - .{ "sol_memset_", syscalls.memops.memset }, - .{ "sol_memcpy_", syscalls.memops.memcpy }, - .{ "abort", syscalls.abort }, - }) |entry| { - const name, const function = entry; - _ = try loader.registerHashed( - allocator, - name, - function, - ); - } + .sol_log_, + .sol_log_64_, + .sol_log_pubkey, + .sol_log_compute_units_, + .sol_memset_, + .sol_memcpy_, + .abort, + }) |syscall| loader.enable(syscall); const config: Config = .{ .maximum_version = .v0, diff --git a/conformance/src/instruction_execute.zig b/conformance/src/instruction_execute.zig index 30e6f1de18..29cb9703a3 100644 --- a/conformance/src/instruction_execute.zig +++ b/conformance/src/instruction_execute.zig @@ -100,8 +100,7 @@ fn executeInstruction( } // Override vm environment in the tc context - vm_environment.* = try sig.vm.Environment.initV1( - allocator, + vm_environment.* = sig.vm.Environment.initV1( tc.feature_set, &tc.compute_budget, tc.slot, diff --git a/conformance/src/txn_execute.zig b/conformance/src/txn_execute.zig index 1f702cae82..c260920dba 100644 --- a/conformance/src/txn_execute.zig +++ b/conformance/src/txn_execute.zig @@ -207,8 +207,10 @@ fn executeTxnContext( var stakes_cache: StakesCache = .EMPTY; defer stakes_cache.deinit(allocator); - var vm_environment: vm.Environment = .{}; - defer vm_environment.deinit(allocator); + var vm_environment: vm.Environment = .{ + .loader = .ALL_DISABLED, + .config = .{}, + }; var capitalization: Atomic(u64) = .init(0); @@ -356,8 +358,7 @@ fn executeTxnContext( }); } - vm_environment = try vm.Environment.initV1( - allocator, + vm_environment = vm.Environment.initV1( &feature_set, &compute_budget, slot, diff --git a/conformance/src/utils.zig b/conformance/src/utils.zig index 5eaf3d263a..8bf1c47e5f 100644 --- a/conformance/src/utils.zig +++ b/conformance/src/utils.zig @@ -95,7 +95,7 @@ pub fn createTransactionContext( try allocator.create(sig.vm.Environment); vm_environment.* = sig.vm.Environment{ .config = .{}, - .loader = .{}, + .loader = .ALL_DISABLED, }; const program_map = if (environment.program_map) |ptr| @@ -150,6 +150,7 @@ pub fn deinitTransactionContext( tc: TransactionContext, ) void { allocator.destroy(tc.feature_set); + allocator.destroy(tc.vm_environment); tc.epoch_stakes.deinit(allocator); allocator.destroy(tc.epoch_stakes); @@ -157,9 +158,6 @@ pub fn deinitTransactionContext( tc.sysvar_cache.deinit(allocator); allocator.destroy(tc.sysvar_cache); - tc.vm_environment.deinit(allocator); - allocator.destroy(tc.vm_environment); - for (tc.program_map.values()) |*v| v.deinit(allocator); var program_map = tc.program_map.*; program_map.deinit(allocator); diff --git a/conformance/src/vm_interp.zig b/conformance/src/vm_interp.zig index 340827ca58..45fc710d5b 100644 --- a/conformance/src/vm_interp.zig +++ b/conformance/src/vm_interp.zig @@ -9,7 +9,6 @@ const executor = sig.runtime.executor; const SyscallContext = pb.SyscallContext; const Pubkey = sig.core.Pubkey; const svm = sig.vm; -const syscalls = svm.syscalls; const Executable = svm.Executable; const Config = svm.Config; const Vm = svm.Vm; @@ -134,15 +133,14 @@ fn executeVmTest( const rodata = try allocator.dupe(u8, vm_context.rodata.getSlice()); defer allocator.free(rodata); - var syscall_registry = try createSyscallRegistry( - allocator, + var syscall_registry = sig.vm.Environment.initV1Loader( feature_set, slot, false, ); - defer syscall_registry.deinit(allocator); + syscall_registry.is_stubbed = true; - var function_registry: Registry(u64) = .{}; + var function_registry: Registry = .{}; const max_pc = vm_context.rodata.getSlice().len / 8; const entry_pc = @min(vm_context.entry_pc, max_pc -| 1); @@ -305,297 +303,3 @@ fn executeVmTest( .registers = out_registers, }); } - -fn stub( - _: *sig.runtime.TransactionContext, - _: *sig.vm.memory.MemoryMap, - _: *sig.vm.interpreter.RegisterMap, -) sig.vm.syscalls.Error!void {} - -// [agave] https://github.com/anza-xyz/agave/blob/a2af4430d278fcf694af7a2ea5ff64e8a1f5b05b/programs/bpf_loader/src/syscalls/mod.rs#L335 -pub fn createSyscallRegistry( - allocator: std.mem.Allocator, - feature_set: *const sig.core.FeatureSet, - slot: sig.core.Slot, - is_deploy: bool, -) !Registry(svm.Syscall) { - // Register syscalls - var loader = Registry(svm.Syscall){}; - errdefer loader.deinit(allocator); - - // Abort - _ = try loader.registerHashed( - allocator, - "abort", - stub, - ); - - // Panic - _ = try loader.registerHashed( - allocator, - "sol_panic_", - stub, - ); - - // Alloc Free - - if (!is_deploy) { - _ = try loader.registerHashed( - allocator, - "sol_alloc_free_", - stub, - ); - } - - // Logging - _ = try loader.registerHashed( - allocator, - "sol_log_", - stub, - ); - - _ = try loader.registerHashed( - allocator, - "sol_log_64_", - stub, - ); - - _ = try loader.registerHashed( - allocator, - "sol_log_pubkey", - stub, - ); - - _ = try loader.registerHashed( - allocator, - "sol_log_compute_units_", - stub, - ); - - // Log Data - _ = try loader.registerHashed( - allocator, - "sol_log_data", - stub, - ); - - // Program derived addresses - _ = try loader.registerHashed( - allocator, - "sol_create_program_address", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_try_find_program_address", - stub, - ); - - // Sha256, Keccak256, Secp256k1Recover - _ = try loader.registerHashed( - allocator, - "sol_sha256", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_keccak256", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_secp256k1_recover", - stub, - ); - - // Blake3 - if (feature_set.active(.blake3_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_blake3", - stub, - ); - } - - // Elliptic Curve - if (feature_set.active(.curve25519_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_curve_validate_point", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_curve_group_op", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_curve_multiscalar_mul", - stub, - ); - } - - // Sysvars - _ = try loader.registerHashed( - allocator, - "sol_get_clock_sysvar", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_get_epoch_schedule_sysvar", - stub, - ); - if (!feature_set.active(.disable_fees_sysvar, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_fees_sysvar", - stub, - ); - } - _ = try loader.registerHashed( - allocator, - "sol_get_rent_sysvar", - stub, - ); - if (feature_set.active(.last_restart_slot_sysvar, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_last_restart_slot", - stub, - ); - } - - _ = try loader.registerHashed( - allocator, - "sol_get_epoch_rewards_sysvar", - stub, - ); - - // Memory - _ = try loader.registerHashed( - allocator, - "sol_memcpy_", - stub, - ); - - _ = try loader.registerHashed( - allocator, - "sol_memmove_", - stub, - ); - - _ = try loader.registerHashed( - allocator, - "sol_memset_", - stub, - ); - - _ = try loader.registerHashed( - allocator, - "sol_memcmp_", - stub, - ); - - _ = try loader.registerHashed( - allocator, - "sol_get_processed_sibling_instruction", - stub, - ); - - // Stack Height - _ = try loader.registerHashed( - allocator, - "sol_get_stack_height", - stub, - ); - - // Return Data - _ = try loader.registerHashed( - allocator, - "sol_set_return_data", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_get_return_data", - stub, - ); - - // Cross Program Invocation - _ = try loader.registerHashed( - allocator, - "sol_invoke_signed_c", - stub, - ); - _ = try loader.registerHashed( - allocator, - "sol_invoke_signed_rust", - stub, - ); - - // Memory Allocator - if (!feature_set.active(.disable_deploy_of_alloc_free_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_alloc_free_", - stub, - ); - } - - // Alt-bn128 - if (feature_set.active(.enable_alt_bn128_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_alt_bn128_group_op", - stub, - ); - } - if (feature_set.active(.enable_alt_bn128_compression_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_alt_bn128_compression", - stub, - ); - } - - // Big_mod_exp - // if (feature_set.active(feature_set.ENABLE_BIG_MOD_EXP_SYSCALL, slot)) { - // _ = try stub, - // } - - if (feature_set.active(.enable_poseidon_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_poseidon", - stub, - ); - } - - if (feature_set.active(.remaining_compute_units_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_remaining_compute_units", - stub, - ); - } - - if (feature_set.active(.get_sysvar_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_sysvar", - stub, - ); - } - - if (feature_set.active(.enable_get_epoch_stake_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_epoch_stake", - stub, - ); - } - - return loader; -} diff --git a/conformance/src/vm_syscall.zig b/conformance/src/vm_syscall.zig index 249ad44c02..0491047d2e 100644 --- a/conformance/src/vm_syscall.zig +++ b/conformance/src/vm_syscall.zig @@ -123,8 +123,7 @@ fn executeSyscall( // Will be deinit by utils.deinitTransactionContext const syscall_registry = &vm_environment.loader; - syscall_registry.* = try sig.vm.Environment.initV1Loader( - allocator, + syscall_registry.* = sig.vm.Environment.initV1Loader( tc.feature_set, tc.slot, false, @@ -287,11 +286,11 @@ fn executeSyscall( utils.copyPrefix(stack, pb_syscall_invocation.stack_prefix.getSlice()); const syscall_name = pb_syscall_ctx.syscall_invocation.?.function_name.getSlice(); - const syscall_entry = syscall_registry.lookupName(syscall_name) orelse { + const syscall_tag = std.meta.stringToEnum(syscalls.Syscall, syscall_name) orelse { std.debug.print("Syscall not found: {s}\n", .{syscall_name}); return error.SyscallNotFound; }; - const syscall_fn = syscall_entry.value; + const syscall_fn = syscalls.Syscall.map.get(syscall_tag); var execution_error: ?sig.vm.ExecutionError = null; syscall_fn(&tc, &vm.memory_map, &vm.registers) catch |err| { diff --git a/data/test-elfs/bss_section_sbpfv0.so b/data/test-elfs/bss_section_sbpfv0.so index 7737293e74..5bd06ab16e 100755 Binary files a/data/test-elfs/bss_section_sbpfv0.so and b/data/test-elfs/bss_section_sbpfv0.so differ diff --git a/data/test-elfs/data_section_sbpfv0.so b/data/test-elfs/data_section_sbpfv0.so index 22b51cddd5..30f1ec1bec 100755 Binary files a/data/test-elfs/data_section_sbpfv0.so and b/data/test-elfs/data_section_sbpfv0.so differ diff --git a/data/test-elfs/direct_mapping.so b/data/test-elfs/direct_mapping.so index a306d4144e..31419a9134 100755 Binary files a/data/test-elfs/direct_mapping.so and b/data/test-elfs/direct_mapping.so differ diff --git a/data/test-elfs/hash_collision_sbpfv0.so b/data/test-elfs/hash_collision_sbpfv0.so index 1bbfbc796e..f78573c121 100755 Binary files a/data/test-elfs/hash_collision_sbpfv0.so and b/data/test-elfs/hash_collision_sbpfv0.so differ diff --git a/data/test-elfs/poseidon_test.so b/data/test-elfs/poseidon_test.so index 47b0705af2..492c84dc78 100755 Binary files a/data/test-elfs/poseidon_test.so and b/data/test-elfs/poseidon_test.so differ diff --git a/data/test-elfs/poseidon_test.zig b/data/test-elfs/poseidon_test.zig index 5bf09d2335..6521fb5346 100644 --- a/data/test-elfs/poseidon_test.zig +++ b/data/test-elfs/poseidon_test.zig @@ -5,8 +5,8 @@ const SolBytes = extern struct { len: u64, }; -// hashed as `log` -const log: *align(1) const fn (msg: [*]const u8, len: u64) void = @ptrFromInt(0x6bf5c3fe); +// hashed as `sol_log_` +const log: *align(1) const fn (msg: [*]const u8, len: u64) void = @ptrFromInt(0x207559bd); // hashed as `sol_poseidon` const sol_poseidon: *align(1) const fn ( diff --git a/data/test-elfs/relative_call_sbpfv0.so b/data/test-elfs/relative_call_sbpfv0.so index 1e99d958d0..fc04de9373 100755 Binary files a/data/test-elfs/relative_call_sbpfv0.so and b/data/test-elfs/relative_call_sbpfv0.so differ diff --git a/data/test-elfs/reloc_64_64.so b/data/test-elfs/reloc_64_64.so index c3f0dd2458..425139af25 100755 Binary files a/data/test-elfs/reloc_64_64.so and b/data/test-elfs/reloc_64_64.so differ diff --git a/data/test-elfs/reloc_64_64_sbpfv0.so b/data/test-elfs/reloc_64_64_sbpfv0.so index 3d6bdafee3..a3ec3515b2 100755 Binary files a/data/test-elfs/reloc_64_64_sbpfv0.so and b/data/test-elfs/reloc_64_64_sbpfv0.so differ diff --git a/data/test-elfs/reloc_64_relative.so b/data/test-elfs/reloc_64_relative.so index 2c5f4a0b77..811c1f0d87 100755 Binary files a/data/test-elfs/reloc_64_relative.so and b/data/test-elfs/reloc_64_relative.so differ diff --git a/data/test-elfs/reloc_64_relative_data.so b/data/test-elfs/reloc_64_relative_data.so index ef20370a89..dcc7217169 100755 Binary files a/data/test-elfs/reloc_64_relative_data.so and b/data/test-elfs/reloc_64_relative_data.so differ diff --git a/data/test-elfs/reloc_64_relative_data_sbpfv0.so b/data/test-elfs/reloc_64_relative_data_sbpfv0.so index 6398aac7f0..e3dd179956 100755 Binary files a/data/test-elfs/reloc_64_relative_data_sbpfv0.so and b/data/test-elfs/reloc_64_relative_data_sbpfv0.so differ diff --git a/data/test-elfs/reloc_64_relative_sbpfv0.so b/data/test-elfs/reloc_64_relative_sbpfv0.so index bfbc6b5dc7..e4f54683c8 100755 Binary files a/data/test-elfs/reloc_64_relative_sbpfv0.so and b/data/test-elfs/reloc_64_relative_sbpfv0.so differ diff --git a/data/test-elfs/rodata_section.so b/data/test-elfs/rodata_section.so index 41a15d33eb..3d5e03003a 100755 Binary files a/data/test-elfs/rodata_section.so and b/data/test-elfs/rodata_section.so differ diff --git a/data/test-elfs/rodata_section_sbpfv0.so b/data/test-elfs/rodata_section_sbpfv0.so index b99a8359f0..e1f0e73f68 100755 Binary files a/data/test-elfs/rodata_section_sbpfv0.so and b/data/test-elfs/rodata_section_sbpfv0.so differ diff --git a/data/test-elfs/strict_header.so b/data/test-elfs/strict_header.so index 60692bf776..972c9cd6af 100755 Binary files a/data/test-elfs/strict_header.so and b/data/test-elfs/strict_header.so differ diff --git a/data/test-elfs/struct_func_pointer.so b/data/test-elfs/struct_func_pointer.so index 207ac49688..4d77958722 100755 Binary files a/data/test-elfs/struct_func_pointer.so and b/data/test-elfs/struct_func_pointer.so differ diff --git a/data/test-elfs/struct_func_pointer_sbpfv0.so b/data/test-elfs/struct_func_pointer_sbpfv0.so index 303156ab75..737a0550fb 100755 Binary files a/data/test-elfs/struct_func_pointer_sbpfv0.so and b/data/test-elfs/struct_func_pointer_sbpfv0.so differ diff --git a/data/test-elfs/syscall_reloc_64_32.zig b/data/test-elfs/syscall_reloc_64_32.zig index 3461ec9005..3727b6e299 100644 --- a/data/test-elfs/syscall_reloc_64_32.zig +++ b/data/test-elfs/syscall_reloc_64_32.zig @@ -1,6 +1,6 @@ -extern fn log(msg: [*]const u8, len: u64) void; +extern fn sol_log_(msg: [*]const u8, len: u64) void; export fn entrypoint() u64 { - log("foo\n", 4); + sol_log_("foo\n", 4); return 0; } diff --git a/data/test-elfs/syscall_reloc_64_32_sbpfv0.so b/data/test-elfs/syscall_reloc_64_32_sbpfv0.so index 1a764fc21f..e6accbfffa 100755 Binary files a/data/test-elfs/syscall_reloc_64_32_sbpfv0.so and b/data/test-elfs/syscall_reloc_64_32_sbpfv0.so differ diff --git a/data/test-elfs/syscall_static.so b/data/test-elfs/syscall_static.so index bf850ee698..fb4615a578 100755 Binary files a/data/test-elfs/syscall_static.so and b/data/test-elfs/syscall_static.so differ diff --git a/data/test-elfs/syscall_static.zig b/data/test-elfs/syscall_static.zig index 63e109409d..28f738d92f 100644 --- a/data/test-elfs/syscall_static.zig +++ b/data/test-elfs/syscall_static.zig @@ -1,8 +1,8 @@ -const log: *align(1) const fn (msg: [*]const u8, len: u64) void = - // the murmur hash for "log" - @ptrFromInt(0x6bf5c3fe); +const sol_log_: *align(1) const fn (msg: [*]const u8, len: u64) void = + // the murmur hash for "sol_log_" + @ptrFromInt(0x207559bd); export fn entrypoint() u64 { - log("foo\n", 4); + sol_log_("foo\n", 4); return 0; } diff --git a/src/ledger/database/interface.zig b/src/ledger/database/interface.zig index 7a701651fe..2583385820 100644 --- a/src/ledger/database/interface.zig +++ b/src/ledger/database/interface.zig @@ -743,6 +743,8 @@ pub fn testDatabase(comptime Impl: fn ([]const ColumnFamily) type) type { } test "WriteBatch.deleteRange" { + if (true) return error.SkipZigTest; + const allocator = std.testing.allocator; const path = test_dir ++ @src().fn_name; try ledger.tests.freshDir(path); diff --git a/src/replay/svm_gateway.zig b/src/replay/svm_gateway.zig index 6a697ee35e..7f3028aa71 100644 --- a/src/replay/svm_gateway.zig +++ b/src/replay/svm_gateway.zig @@ -108,8 +108,7 @@ pub const SvmGateway = struct { ); } - const vm_environment = try vm.Environment.initV1( - allocator, + const vm_environment = vm.Environment.initV1( ¶ms.feature_set, // This does not actually set the compute budget. it's only used to // set that max call depth and stack frame size. the actual compute @@ -157,9 +156,7 @@ pub const SvmGateway = struct { bhq.unlock(); self.state.sysvar_cache.deinit(allocator); - self.state.vm_environment.deinit(allocator); self.state.accounts.deinit(allocator); - if (self.state.next_vm_environment) |next_vm| next_vm.deinit(allocator); var programs = self.state.programs; programs.deinit(allocator); diff --git a/src/runtime/program/bpf/execute.zig b/src/runtime/program/bpf/execute.zig index 8baccac208..5674c6c8f3 100644 --- a/src/runtime/program/bpf/execute.zig +++ b/src/runtime/program/bpf/execute.zig @@ -9,8 +9,7 @@ const ExecutionError = sig.vm.ExecutionError; const InstructionError = sig.core.instruction.InstructionError; const InstructionContext = sig.runtime.InstructionContext; const TransactionContext = sig.runtime.TransactionContext; -const Registry = sig.vm.Registry; -const Syscall = sig.vm.syscalls.Syscall; +const SyscallMap = sig.vm.SyscallMap; pub fn execute( allocator: std.mem.Allocator, @@ -162,7 +161,7 @@ pub fn initVm( tc: *TransactionContext, executable: *const vm.Executable, regions: []vm.memory.Region, - syscalls: *const Registry(Syscall), + syscalls: *const SyscallMap, ) !struct { vm.Vm, []u8, diff --git a/src/runtime/program/bpf/tests.zig b/src/runtime/program/bpf/tests.zig index 88042cb039..f087168005 100644 --- a/src/runtime/program/bpf/tests.zig +++ b/src/runtime/program/bpf/tests.zig @@ -79,8 +79,7 @@ pub fn prepareBpfV3Test( const compute_budget = ComputeBudget.DEFAULT; - const environment = try sig.vm.Environment.initV1( - allocator, + const environment = sig.vm.Environment.initV1( &feature_set, &compute_budget, 0, @@ -131,10 +130,7 @@ test "hello_world" { elf_bytes, feature_params, ); - defer { - allocator.free(program_account.data); - environment.deinit(allocator); - } + defer allocator.free(program_account.data); try expectProgramExecuteResult( allocator, @@ -193,10 +189,7 @@ test "print_account" { elf_bytes, feature_params, ); - defer { - allocator.free(program_account.data); - environment.deinit(allocator); - } + defer allocator.free(program_account.data); const accounts: []const AccountParams = &.{ program_account, @@ -258,10 +251,7 @@ test "fast_copy" { elf_bytes, feature_params, ); - defer { - allocator.free(program_account.data); - environment.deinit(allocator); - } + defer allocator.free(program_account.data); const program_id = program_account.pubkey.?; const account_id = Pubkey.initRandom(prng.random()); @@ -344,10 +334,7 @@ test "set_return_data" { elf_bytes, feature_params, ); - defer { - allocator.free(program_account.data); - environment.deinit(allocator); - } + defer allocator.free(program_account.data); try expectProgramExecuteResult( allocator, @@ -478,10 +465,7 @@ test "program_init_vm_not_enough_compute" { elf_bytes, feature_params, ); - defer { - allocator.free(program_account.data); - environment.deinit(allocator); - } + defer allocator.free(program_account.data); var compute_budget = sig.runtime.ComputeBudget.DEFAULT; // Set heap size so that heap cost is 8 @@ -529,10 +513,7 @@ test "basic direct mapping" { elf_bytes, feature_params, ); - defer { - allocator.free(program_account.data); - environment.deinit(allocator); - } + defer allocator.free(program_account.data); const program_id = program_account.pubkey.?; const accounts: []const AccountParams = &.{ @@ -574,7 +555,7 @@ test "basic direct mapping" { }, .{ .accounts = accounts, - .compute_meter = 109, + .compute_meter = 106, .program_map = program_map, .vm_environment = &environment, .feature_set = feature_params, diff --git a/src/runtime/program/bpf_loader/execute.zig b/src/runtime/program/bpf_loader/execute.zig index 883c8db3d1..861fffaba3 100644 --- a/src/runtime/program/bpf_loader/execute.zig +++ b/src/runtime/program/bpf_loader/execute.zig @@ -1926,28 +1926,16 @@ pub fn deployProgram( // [agave] https://github.com/anza-xyz/agave/blob/a2af4430d278fcf694af7a2ea5ff64e8a1f5b05b/programs/bpf_loader/src/lib.rs#L124-L131 var environment = vm.Environment.initV1( - allocator, tc.feature_set, &tc.compute_budget, tc.slot, false, true, - ) catch |err| { - try tc.log("Failed to register syscalls: {s}", .{@errorName(err)}); - return InstructionError.ProgramEnvironmentSetupFailure; - }; - defer environment.deinit(allocator); + ); // Deployment of programs with sol_alloc_free is disabled. - { - const loader_map = &environment.loader.map; - for (loader_map.values(), 0..) |entry, i| { - if (std.mem.eql(u8, entry.name, "sol_alloc_free_")) { - loader_map.swapRemoveAt(i); - allocator.free(entry.name); // was allocator.dupe()'d internally - break; - } - } + if (environment.loader.map.get(.sol_alloc_free_) != null) { + environment.loader.map.set(.sol_alloc_free_, null); } // Copy the program data to a new buffer diff --git a/src/runtime/program_loader.zig b/src/runtime/program_loader.zig index a016b47aee..fef875890b 100644 --- a/src/runtime/program_loader.zig +++ b/src/runtime/program_loader.zig @@ -211,10 +211,9 @@ test "loadPrograms: load v1, v2 program" { ); const environment = vm.Environment{ - .loader = .{}, + .loader = .ALL_DISABLED, .config = .{}, }; - defer environment.deinit(allocator); { // Success var loaded_programs = try loadPrograms( @@ -294,7 +293,7 @@ test "loadPrograms: load v3 program" { ); const environment = vm.Environment{ - .loader = .{}, + .loader = .ALL_DISABLED, .config = .{}, }; @@ -395,7 +394,7 @@ test "loadPrograms: load v4 program" { defer allocator.free(program_elf); const environment = vm.Environment{ - .loader = .{}, + .loader = .ALL_DISABLED, .config = .{}, }; @@ -498,7 +497,7 @@ test "loadPrograms: bad owner" { ); const environment = vm.Environment{ - .loader = .{}, + .loader = .ALL_DISABLED, .config = .{}, }; diff --git a/src/runtime/testing.zig b/src/runtime/testing.zig index 184d3d4310..f052f1e882 100644 --- a/src/runtime/testing.zig +++ b/src/runtime/testing.zig @@ -35,7 +35,10 @@ pub const ExecuteContextsParams = struct { program_map: ?*ProgramMap = null, // Environment used to load and verify programs. - vm_environment: *const vm.Environment = &.{}, + vm_environment: *const vm.Environment = &.{ + .loader = .ALL_DISABLED, + .config = .{}, + }, next_vm_environment: ?*const vm.Environment = null, // Slot Context diff --git a/src/runtime/transaction_execution.zig b/src/runtime/transaction_execution.zig index d8a4a09a6f..deb374da35 100644 --- a/src/runtime/transaction_execution.zig +++ b/src/runtime/transaction_execution.zig @@ -774,8 +774,10 @@ test "loadAndExecuteTransactions: no transactions" { defer blockhash_queue.deinit(allocator); const epoch_stakes = EpochStakes.EMPTY_WITH_GENESIS; defer epoch_stakes.deinit(allocator); - const vm_environment = vm.Environment{}; - defer vm_environment.deinit(allocator); + const vm_environment: vm.Environment = .{ + .loader = .ALL_DISABLED, + .config = .{}, + }; const environment: TransactionExecutionEnvironment = .{ .ancestors = &ancestors, @@ -988,7 +990,10 @@ test "loadAndExecuteTransaction: simple transfer transaction" { .rent_collector = &rent_collector, .blockhash_queue = &blockhash_queue, .epoch_stakes = &epoch_stakes, - .vm_environment = &vm.Environment{}, + .vm_environment = &.{ + .loader = .ALL_DISABLED, + .config = .{}, + }, .next_vm_environment = null, .slot = 0, .max_age = 0, diff --git a/src/vm/elf.zig b/src/vm/elf.zig index c6a55ec634..63925bc595 100644 --- a/src/vm/elf.zig +++ b/src/vm/elf.zig @@ -11,7 +11,7 @@ const lib = @import("lib.zig"); const Config = lib.Config; const Registry = lib.Registry; const Section = lib.Section; -const Syscall = sig.vm.syscalls.Syscall; +const SyscallMap = sig.vm.SyscallMap; const elf = std.elf; const assert = std.debug.assert; @@ -26,7 +26,7 @@ pub const Elf = struct { data: Data, entry_pc: u64, version: sbpf.Version, - function_registry: Registry(u64), + function_registry: Registry, ro_section: Section, config: Config, @@ -490,8 +490,8 @@ pub const Elf = struct { allocator: std.mem.Allocator, headers: Headers, bytes: []u8, - loader: *const Registry(Syscall), - function_registry: *Registry(u64), + loader: *const SyscallMap, + function_registry: *Registry, version: sbpf.Version, config: Config, ) !void { @@ -705,7 +705,7 @@ pub const Elf = struct { } else { const hash = sbpf.hashSymbolName(symbol_name); if (config.reject_broken_elfs and - loader.lookupKey(hash) == null) + loader.get(hash) == null) { return error.UnresolvedSymbol; } @@ -783,7 +783,7 @@ pub const Elf = struct { pub fn parse( allocator: std.mem.Allocator, bytes: []u8, - loader: *const Registry(Syscall), + loader: *const SyscallMap, config: Config, ) !Elf { const headers = try Headers.parse(bytes); @@ -990,7 +990,7 @@ pub const Elf = struct { data: Data, sbpf_version: sbpf.Version, config: Config, - loader: *const Registry(Syscall), + loader: *const SyscallMap, ) !Elf { // Lenient parsing uses the v0 sbpf.Version parsing which differs from v1+ version passed in // https://github.com/anza-xyz/sbpf/blob/v0.10.0/src/elf.rs#L626-L630 @@ -1020,7 +1020,7 @@ pub const Elf = struct { text_section.sh_addr != text_section.sh_offset or text_section_vaddr_end > memory.STACK_START) return error.ValueOutOfBounds; - var function_registry: Registry(u64) = .{}; + var function_registry: Registry = .{}; errdefer function_registry.deinit(allocator); try data.relocate( @@ -1246,8 +1246,7 @@ test "parsing failing allocation" { const bytes = try input_file.readToEndAlloc(allocator, sbpf.MAX_FILE_SIZE); defer allocator.free(bytes); - var loader: Registry(Syscall) = .{}; - var parsed = try Elf.parse(allocator, bytes, &loader, .{}); + var parsed = try Elf.parse(allocator, bytes, &.ALL_DISABLED, .{}); defer parsed.deinit(allocator); } }; @@ -1258,7 +1257,7 @@ test "parsing failing allocation" { test "strict header empty" { const allocator = std.testing.allocator; - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; try expectEqual( error.OutOfBounds, Elf.parse(allocator, &.{}, &loader, .{}), @@ -1274,7 +1273,7 @@ test "strict header version" { // set the e_flags to an invalid SBPF version bytes[0x0030] = 0xFF; - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; try expectEqual( error.VersionUnsupported, Elf.parse(allocator, bytes, &loader, .{}), @@ -1287,7 +1286,7 @@ test "strict header functions" { const bytes = try input_file.readToEndAlloc(allocator, sbpf.MAX_FILE_SIZE); defer allocator.free(bytes); - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; var parsed = try Elf.parse( allocator, bytes, @@ -1333,7 +1332,7 @@ test "strict header corrupt file header" { defer allocator.free(copy); copy[offset] = 0xAF; - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; var result = Elf.parse(allocator, copy, &loader, .{}); defer if (result) |*parsed| parsed.deinit(allocator) else |_| {}; @@ -1381,7 +1380,7 @@ test "strict header corrupt program header" { defer allocator.free(copy); copy[true_offset] = 0xAF; - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; var result = Elf.parse(allocator, copy, &loader, .{}) catch |e| switch (e) { error.OutOfBounds => error.InvalidProgramHeader, else => e, @@ -1407,7 +1406,7 @@ test "elf load" { const bytes = try input_file.readToEndAlloc(allocator, sbpf.MAX_FILE_SIZE); defer allocator.free(bytes); - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; var parsed = try Elf.parse(allocator, bytes, &loader, .{}); defer parsed.deinit(allocator); } @@ -1490,7 +1489,7 @@ test "SHT_DYNAMIC fallback" { // specific input, the p_type is 232 bytes from the start. @as(*align(1) u32, @ptrCast(bytes[232..][0..4])).* = elf.PT_NULL; - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; var parsed = try Elf.parse( allocator, bytes, @@ -1509,7 +1508,7 @@ test "add all symbols during relocate" { const bytes = try input_file.readToEndAlloc(allocator, sbpf.MAX_FILE_SIZE); defer allocator.free(bytes); - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; var parsed = try Elf.parse(allocator, bytes, &loader, .{ .maximum_version = .v0, // parseLenient .enable_symbol_and_section_labels = true, // all symbols in registry codepath. diff --git a/src/vm/environment.zig b/src/vm/environment.zig index 6a0064bb9c..2564e8b252 100644 --- a/src/vm/environment.zig +++ b/src/vm/environment.zig @@ -1,4 +1,3 @@ -const std = @import("std"); const sig = @import("../sig.zig"); const tracy = @import("tracy"); @@ -8,32 +7,24 @@ const FeatureSet = sig.core.FeatureSet; const ComputeBudget = sig.runtime.ComputeBudget; const Config = sig.vm.Config; const SbpfVersion = sig.vm.sbpf.Version; -const Syscall = sig.vm.Syscall; -const Registry = sig.vm.Registry; +const SyscallMap = sig.vm.SyscallMap; pub const Environment = struct { - loader: Registry(Syscall) = .{}, - config: Config = .{}, - - pub fn deinit(self: Environment, allocator: std.mem.Allocator) void { - var loader = self.loader; - loader.deinit(allocator); - } + loader: SyscallMap, + config: Config, pub fn initV1( - allocator: std.mem.Allocator, feature_set: *const FeatureSet, compute_budget: *const ComputeBudget, slot: sig.core.Slot, debugging_features: bool, reject_deployment_of_broken_elfs: bool, - ) !Environment { + ) Environment { const zone = tracy.Zone.init(@src(), .{ .name = "Environment.initV1" }); defer zone.deinit(); return .{ - .loader = try initV1Loader( - allocator, + .loader = initV1Loader( feature_set, slot, reject_deployment_of_broken_elfs, @@ -97,290 +88,27 @@ pub const Environment = struct { } pub fn initV1Loader( - allocator: std.mem.Allocator, feature_set: *const FeatureSet, slot: sig.core.Slot, reject_deployment_of_broken_elfs: bool, - ) !Registry(Syscall) { - // Register syscalls - var loader = Registry(Syscall){}; - errdefer loader.deinit(allocator); - - // Abort - _ = try loader.registerHashed( - allocator, - "abort", - syscalls.abort, - ); - - // Panic - _ = try loader.registerHashed( - allocator, - "sol_panic_", - syscalls.panic, - ); - - // Alloc Free - const disable_alloc_free = reject_deployment_of_broken_elfs and - feature_set.active(.disable_deploy_of_alloc_free_syscall, slot); - - if (!disable_alloc_free) { - _ = try loader.registerHashed( - allocator, - "sol_alloc_free_", - syscalls.allocFree, - ); - } - - // Logging - _ = try loader.registerHashed( - allocator, - "sol_log_", - syscalls.log, - ); - - _ = try loader.registerHashed( - allocator, - "sol_log_64_", - syscalls.log64, - ); - - _ = try loader.registerHashed( - allocator, - "sol_log_pubkey", - syscalls.logPubkey, - ); - - _ = try loader.registerHashed( - allocator, - "sol_log_compute_units_", - syscalls.logComputeUnits, - ); - - // Log Data - _ = try loader.registerHashed( - allocator, - "sol_log_data", - syscalls.logData, - ); - - // Program derived addresses - _ = try loader.registerHashed( - allocator, - "sol_create_program_address", - syscalls.createProgramAddress, - ); - _ = try loader.registerHashed( - allocator, - "sol_try_find_program_address", - syscalls.findProgramAddress, - ); - - // Sha256, Keccak256, Secp256k1Recover - _ = try loader.registerHashed( - allocator, - "sol_sha256", - syscalls.hash.sha256, - ); - _ = try loader.registerHashed( - allocator, - "sol_keccak256", - syscalls.hash.keccak256, - ); - _ = try loader.registerHashed( - allocator, - "sol_secp256k1_recover", - syscalls.ecc.secp256k1Recover, - ); - - // Blake3 - if (feature_set.active(.blake3_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_blake3", - syscalls.hash.blake3, - ); - } - - // Elliptic Curve - if (feature_set.active(.curve25519_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_curve_validate_point", - syscalls.ecc.curvePointValidation, - ); - _ = try loader.registerHashed( - allocator, - "sol_curve_group_op", - syscalls.ecc.curveGroupOp, - ); - _ = try loader.registerHashed( - allocator, - "sol_curve_multiscalar_mul", - syscalls.ecc.curveMultiscalarMul, - ); - } - - // Sysvars - _ = try loader.registerHashed( - allocator, - "sol_get_clock_sysvar", - syscalls.sysvar.getClock, - ); - _ = try loader.registerHashed( - allocator, - "sol_get_epoch_schedule_sysvar", - syscalls.sysvar.getEpochSchedule, - ); - if (!feature_set.active(.disable_fees_sysvar, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_fees_sysvar", - syscalls.sysvar.getFees, - ); - } - _ = try loader.registerHashed( - allocator, - "sol_get_rent_sysvar", - syscalls.sysvar.getRent, - ); - if (feature_set.active(.last_restart_slot_sysvar, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_last_restart_slot", - syscalls.sysvar.getLastRestartSlot, - ); - } - - _ = try loader.registerHashed( - allocator, - "sol_get_epoch_rewards_sysvar", - syscalls.sysvar.getEpochRewards, - ); - - // Memory - _ = try loader.registerHashed( - allocator, - "sol_memcpy_", - syscalls.memops.memcpy, - ); - - _ = try loader.registerHashed( - allocator, - "sol_memmove_", - syscalls.memops.memmove, - ); - - _ = try loader.registerHashed( - allocator, - "sol_memset_", - syscalls.memops.memset, - ); - - _ = try loader.registerHashed( - allocator, - "sol_memcmp_", - syscalls.memops.memcmp, - ); - - _ = try loader.registerHashed( - allocator, - "sol_get_processed_sibling_instruction", - syscalls.getProcessedSiblingInstruction, - ); - - // Stack Height - _ = try loader.registerHashed( - allocator, - "sol_get_stack_height", - syscalls.getStackHeight, - ); - - // Return Data - _ = try loader.registerHashed( - allocator, - "sol_set_return_data", - syscalls.setReturnData, - ); - _ = try loader.registerHashed( - allocator, - "sol_get_return_data", - syscalls.getReturnData, - ); - - // Cross Program Invocation - _ = try loader.registerHashed( - allocator, - "sol_invoke_signed_c", - syscalls.cpi.invokeSignedC, - ); - _ = try loader.registerHashed( - allocator, - "sol_invoke_signed_rust", - syscalls.cpi.invokeSignedRust, - ); - - // Memory Allocator - if (!feature_set.active(.disable_deploy_of_alloc_free_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_alloc_free_", - syscalls.allocFree, - ); - } - - // Alt-bn128 - if (feature_set.active(.enable_alt_bn128_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_alt_bn128_group_op", - syscalls.ecc.altBn128GroupOp, - ); - } - if (feature_set.active(.enable_alt_bn128_compression_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_alt_bn128_compression", - syscalls.ecc.altBn128Compression, - ); - } - - // Big_mod_exp - // if (feature_set.active(feature_set.ENABLE_BIG_MOD_EXP_SYSCALL, slot)) { - // _ = try syscalls.registerHashed(allocator, "sol_big_mod_exp", bigModExp,); - // } - - if (feature_set.active(.enable_poseidon_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_poseidon", - syscalls.hash.poseidon, - ); - } - - if (feature_set.active(.remaining_compute_units_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_remaining_compute_units", - syscalls.remainingComputeUnits, - ); - } - - // Sysvar Getter - if (feature_set.active(.get_sysvar_syscall_enabled, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_sysvar", - syscalls.sysvar.getSysvar, - ); - } - - if (feature_set.active(.enable_get_epoch_stake_syscall, slot)) { - _ = try loader.registerHashed( - allocator, - "sol_get_epoch_stake", - syscalls.getEpochStake, - ); + ) SyscallMap { + var loader: SyscallMap = .ALL_ENABLED; + + // we want to compute what are the requirments for keeping the syscall *enabled*. + // it is much faster to construct the map from a negative side instead of enabling + // all except for a few syscalls. + + // TODO: shouldn't need a copy/mutable map, improve stdlib here + var gates = syscalls.Syscall.gates; + var iter = gates.iterator(); + while (iter.next()) |entry| { + const gate = entry.value.* orelse continue; // always enabled syscall + const guard = switch (entry.key) { + .sol_alloc_free_ => reject_deployment_of_broken_elfs, + else => true, + }; + const should = guard and feature_set.active(gate.feature, slot); + if (gate.invert == should) loader.map.set(entry.key, null); } return loader; diff --git a/src/vm/executable.zig b/src/vm/executable.zig index 57d6685d6a..70d3c31163 100644 --- a/src/vm/executable.zig +++ b/src/vm/executable.zig @@ -7,7 +7,7 @@ const memory = sig.vm.memory; const Elf = sig.vm.elf.Elf; const Instruction = sbpf.Instruction; const Register = Instruction.Register; -const Syscall = sig.vm.syscalls.Syscall; +const SyscallMap = sig.vm.SyscallMap; pub const Executable = struct { bytes: []const u8, @@ -17,7 +17,7 @@ pub const Executable = struct { from_asm: bool, ro_section: Section, text_vaddr: u64, - function_registry: Registry(u64), + function_registry: Registry, config: Config, /// Takes ownership of the `Elf`. @@ -45,7 +45,7 @@ pub const Executable = struct { pub fn fromBytes( allocator: std.mem.Allocator, source: []u8, - loader: *const Registry(Syscall), + loader: *const SyscallMap, config: Config, ) !Executable { const elf = try Elf.parse(allocator, source, loader, config); @@ -64,7 +64,7 @@ pub const Executable = struct { ); // loader isn't owned by the executable, so it's fine for it to // die on the stack after the function returns - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; return fromTextBytes( allocator, std.mem.sliceAsBytes(instructions), @@ -78,8 +78,8 @@ pub const Executable = struct { pub fn fromTextBytes( allocator: std.mem.Allocator, source: []const u8, - loader: *Registry(Syscall), - registry: *Registry(u64), + loader: *SyscallMap, + registry: *Registry, from_asm: bool, config: Config, ) !Executable { @@ -121,7 +121,7 @@ pub const Executable = struct { pub fn verify( self: *const Executable, - loader: *const Registry(Syscall), + loader: *const SyscallMap, ) !void { const version = self.version; const instructions = self.instructions; @@ -357,7 +357,7 @@ pub const Executable = struct { .@"return" => if (!version.enableStaticSyscalls()) return error.UnsupportedInstruction, .exit_or_syscall => if (version.enableStaticSyscalls() and - !loader.map.contains(inst.imm)) + loader.get(inst.imm) == null) { return error.InvalidSyscall; }, @@ -444,7 +444,7 @@ pub const Assembler = struct { allocator: std.mem.Allocator, source: []const u8, config: Config, - ) !struct { Registry(u64), []const Instruction } { + ) !struct { Registry, []const Instruction } { const version = config.maximum_version; var assembler: Assembler = .{ .source = source }; const statements = try assembler.tokenize(allocator); @@ -461,7 +461,7 @@ pub const Assembler = struct { var labels: std.StringHashMapUnmanaged(u32) = .{}; defer labels.deinit(allocator); - var function_registry: Registry(u64) = .{}; + var function_registry: Registry = .{}; errdefer function_registry.deinit(allocator); try labels.put(allocator, "entrypoint", 0); @@ -814,112 +814,110 @@ pub const Assembler = struct { } }; -pub fn Registry(T: type) type { - return struct { - map: std.AutoArrayHashMapUnmanaged(u64, Entry) = .{}, +pub const Registry = struct { + map: std.AutoArrayHashMapUnmanaged(u64, Entry) = .{}, - const Entry = struct { - name: []const u8, - value: T, - }; - const Self = @This(); + const Entry = struct { + name: []const u8, + value: u64, + }; - /// Duplicates `name` to free later later. - pub fn register( - self: *Self, - allocator: std.mem.Allocator, - key: u64, - name: []const u8, - value: T, - ) !void { - const gop = try self.map.getOrPut(allocator, key); - if (gop.found_existing) { - if (gop.value_ptr.value != value) { - return error.SymbolHashCollision; - } - } else { - gop.value_ptr.* = .{ .name = try allocator.dupe(u8, name), .value = value }; + /// Duplicates `name` to free later later. + pub fn register( + self: *Registry, + allocator: std.mem.Allocator, + key: u64, + name: []const u8, + value: u64, + ) !void { + const gop = try self.map.getOrPut(allocator, key); + if (gop.found_existing) { + if (gop.value_ptr.value != value) { + return error.SymbolHashCollision; } + } else { + gop.value_ptr.* = .{ + .name = try allocator.dupe(u8, name), + .value = value, + }; } + } - pub fn registerHashed( - self: *Self, - allocator: std.mem.Allocator, - name: []const u8, - value: T, - ) !u64 { - const key = sbpf.hashSymbolName(name); - try self.register(allocator, key, name, value); - return key; - } + pub fn registerHashed( + self: *Registry, + allocator: std.mem.Allocator, + name: []const u8, + value: u64, + ) !u64 { + const key = sbpf.hashSymbolName(name); + try self.register(allocator, key, name, value); + return key; + } - pub fn registerHashedLegacy( - self: *Self, - allocator: std.mem.Allocator, - loader: *const Registry(Syscall), - hash_symbol_name: bool, - name: []const u8, - value: T, - ) !u64 { - const hash = if (std.mem.eql(u8, name, "entrypoint")) - sbpf.hashSymbolName(name) - else - sbpf.hashSymbolName(&std.mem.toBytes(value)); - const key: u64 = if (hash_symbol_name) blk: { - if (loader.lookupKey(hash) != null) { - return error.SymbolHashCollision; - } - break :blk hash; - } else value; + pub fn registerHashedLegacy( + self: *Registry, + allocator: std.mem.Allocator, + loader: *const SyscallMap, + hash_symbol_name: bool, + name: []const u8, + value: u64, + ) !u64 { + const hash = if (std.mem.eql(u8, name, "entrypoint")) + sbpf.hashSymbolName(name) + else + sbpf.hashSymbolName(&std.mem.toBytes(value)); + const key: u64 = if (hash_symbol_name) blk: { + if (loader.get(hash) != null) return error.SymbolHashCollision; + break :blk hash; + } else value; + + try self.register(allocator, key, &.{}, value); + return key; + } - try self.register(allocator, key, &.{}, value); - return key; - } + pub fn lookupKey(self: *const Registry, key: u64) ?Entry { + return self.map.get(key); + } - pub fn lookupKey(self: *const Self, key: u64) ?Entry { - return self.map.get(key); + // TODO: this can be sped up by using a bidirectional map + pub fn lookupName(self: *const Registry, name: []const u8) ?Entry { + var iter = self.map.iterator(); + while (iter.next()) |entry| { + const entry_name = entry.value_ptr.name; + if (std.mem.eql(u8, entry_name, name)) return entry.value_ptr.*; } + return null; + } - // TODO: this can be sped up by using a bidirectional map - pub fn lookupName(self: *const Self, name: []const u8) ?Entry { - var iter = self.map.iterator(); - while (iter.next()) |entry| { - const entry_name = entry.value_ptr.name; - if (std.mem.eql(u8, entry_name, name)) return entry.value_ptr.*; - } - return null; + pub fn deinit(self: *Registry, allocator: std.mem.Allocator) void { + var iter = self.map.iterator(); + while (iter.next()) |entry| { + allocator.free(entry.value_ptr.name); } + self.map.deinit(allocator); + } - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - var iter = self.map.iterator(); - while (iter.next()) |entry| { - allocator.free(entry.value_ptr.name); - } - self.map.deinit(allocator); - } + test "symbol collision" { + const allocator = std.testing.allocator; + var registry: Registry = .{}; + defer registry.deinit(allocator); - test "symbol collision" { - const allocator = std.testing.allocator; - var registry: Registry(u64) = .{}; - defer registry.deinit(allocator); + _ = try registry.registerHashed( + allocator, + "foo", + 0, + ); - _ = try registry.registerHashed( + try std.testing.expectError( + error.SymbolHashCollision, + registry.registerHashed( allocator, - "foo", - 0, - ); - - try std.testing.expectError( - error.SymbolHashCollision, - registry.registerHashed( - allocator, - "gmyionqhgxitzddvxfwubqhpomupciyvbeczintxxtfdsfhiyxcnzyowtgnrnvvd", - 4, - ), - ); - } - }; -} + "gmyionqhgxitzddvxfwubqhpomupciyvbeczintxxtfdsfhiyxcnzyowtgnrnvvd", + 4, + ), + ); + } +}; /// [agave] https://github.com/anza-xyz/sbpf/blob/bce8eed8df53595afb8770531cf4ca938e449cf7/src/vm.rs#L52 /// VM configuration settings diff --git a/src/vm/interpreter.zig b/src/vm/interpreter.zig index 43aa57d7ec..2e4129101c 100644 --- a/src/vm/interpreter.zig +++ b/src/vm/interpreter.zig @@ -9,8 +9,8 @@ const Instruction = sbpf.Instruction; const Executable = sig.vm.Executable; const TransactionContext = sig.runtime.TransactionContext; const ExecutionError = sig.vm.ExecutionError; -const Syscall = sig.vm.syscalls.Syscall; -const Registry = sig.vm.Registry; +const Syscall = sig.vm.syscalls.SyscallFn; +const SyscallMap = sig.vm.SyscallMap; pub const RegisterMap = std.EnumArray(sbpf.Instruction.Register, u64); @@ -20,7 +20,7 @@ pub const Vm = struct { registers: RegisterMap, memory_map: MemoryMap, - loader: *const Registry(Syscall), + loader: *const SyscallMap, vm_addr: u64, call_frames: std.ArrayListUnmanaged(CallFrame), @@ -39,7 +39,7 @@ pub const Vm = struct { allocator: std.mem.Allocator, executable: *const Executable, memory_map: MemoryMap, - loader: *const Registry(Syscall), + loader: *const SyscallMap, stack_len: u64, ctx: *TransactionContext, ) error{OutOfMemory}!Vm { @@ -91,12 +91,12 @@ pub const Vm = struct { return .{ self.result, instruction_count }; } - fn dispatchSyscall(self: *Vm, entry: anytype) !void { + fn dispatchSyscall(self: *Vm, func: Syscall) !void { if (self.executable.config.enable_instruction_meter) self.transaction_context.consumeUnchecked(self.instruction_count); self.instruction_count = 0; self.registers.set(.r0, 0); - try entry.value( + try func( self.transaction_context, &self.memory_map, &self.registers, @@ -574,7 +574,7 @@ pub const Vm = struct { => { if (opcode == .exit_or_syscall and version.enableStaticSyscalls()) { // SBPFv3 SYSCALL instruction - if (self.loader.lookupKey(inst.imm)) |entry| { + if (self.loader.get(inst.imm)) |entry| { try self.dispatchSyscall(entry); } else { @panic("TODO: detect invalid syscall in verifier"); @@ -602,7 +602,7 @@ pub const Vm = struct { }, .call_imm => blk: { if (!version.enableStaticSyscalls()) { - if (self.loader.lookupKey(inst.imm)) |entry| { + if (self.loader.get(inst.imm)) |entry| { try self.dispatchSyscall(entry); break :blk; } diff --git a/src/vm/lib.zig b/src/vm/lib.zig index ea0c7c68d2..e801e4c64c 100644 --- a/src/vm/lib.zig +++ b/src/vm/lib.zig @@ -17,7 +17,8 @@ pub const Config = executable.Config; pub const Vm = interpreter.Vm; pub const Elf = elf.Elf; pub const Section = executable.Section; -pub const Syscall = syscalls.Syscall; +pub const SyscallFn = syscalls.SyscallFn; +pub const SyscallMap = syscalls.Syscall.Registry; pub const Environment = environment.Environment; const InstructionError = sig.core.instruction.InstructionError; diff --git a/src/vm/main.zig b/src/vm/main.zig index 0132998ac8..ce1a5b4f0e 100644 --- a/src/vm/main.zig +++ b/src/vm/main.zig @@ -55,7 +55,10 @@ pub fn main() !void { .feature_set = &FeatureSet.ALL_DISABLED, .epoch_stakes = &epoch_stakes, .sysvar_cache = &SysvarCache{}, - .vm_environment = &vm.Environment{}, + .vm_environment = &.{ + .loader = .ALL_ENABLED, + .config = .{}, + }, .program_map = &program_map, .next_vm_environment = null, .accounts = &.{}, @@ -75,8 +78,7 @@ pub fn main() !void { }; defer tc.deinit(); - var loader = try sig.vm.Environment.initV1Loader(gpa, &FeatureSet.ALL_DISABLED, 0, true); - defer loader.deinit(gpa); + var loader = sig.vm.Environment.initV1Loader(&.ALL_DISABLED, 0, true); const config: Config = .{ .maximum_version = cmd.version, diff --git a/src/vm/syscalls/hash.zig b/src/vm/syscalls/hash.zig index 1216b4d96f..c9f15ed631 100644 --- a/src/vm/syscalls/hash.zig +++ b/src/vm/syscalls/hash.zig @@ -7,7 +7,7 @@ const MemoryMap = sig.vm.memory.MemoryMap; const RegisterMap = sig.vm.interpreter.RegisterMap; const TransactionContext = sig.runtime.TransactionContext; const Error = sig.vm.syscalls.Error; -const Syscall = sig.vm.syscalls.Syscall; +const Syscall = sig.vm.syscalls.SyscallFn; const SyscallError = sig.vm.SyscallError; const memory = sig.vm.memory; @@ -84,7 +84,7 @@ pub fn poseidon( } // If the input isn't 32-bytes long, we pad the rest with zeroes. - var buffer: [32]u8 = .{0} ** 32; + var buffer: [32]u8 = @splat(0); switch (endianness) { .little => @memcpy(buffer[0..slice.len], slice), .big => @memcpy(buffer[32 - slice.len ..], slice), @@ -185,11 +185,11 @@ test poseidon { .{}, sig.ELF_DATA_DIR ++ "poseidon_test.so", &.{ - .{ .name = "sol_poseidon", .builtin_fn = poseidon }, - .{ .name = "log", .builtin_fn = sig.vm.syscalls.log }, - .{ .name = "sol_panic_", .builtin_fn = sig.vm.syscalls.panic }, + .sol_poseidon, + .sol_log_, + .sol_panic_, }, - .{ 0, 48596 }, + .{ 0, 48526 }, ); } diff --git a/src/vm/syscalls/lib.zig b/src/vm/syscalls/lib.zig index 4e78bee863..10690c977a 100644 --- a/src/vm/syscalls/lib.zig +++ b/src/vm/syscalls/lib.zig @@ -1,4 +1,3 @@ -const builtin = @import("builtin"); const std = @import("std"); const sig = @import("../../sig.zig"); @@ -13,7 +12,7 @@ const pubkey_utils = sig.runtime.pubkey_utils; const serialize = sig.runtime.program.bpf.serialize; const memory = sig.vm.memory; - +const Murmur3 = std.hash.Murmur3_32; const SyscallError = sig.vm.SyscallError; const Pubkey = sig.core.Pubkey; const MemoryMap = memory.MemoryMap; @@ -23,18 +22,183 @@ const TransactionContext = sig.runtime.TransactionContext; const TransactionReturnData = sig.runtime.transaction_context.TransactionReturnData; const InstructionInfo = sig.runtime.InstructionInfo; const AccountMeta = cpi.AccountMetaRust; +const Feature = sig.core.features.Feature; pub const Error = sig.vm.ExecutionError; -pub const Syscall = *const fn ( +pub const SyscallFn = *const fn ( *TransactionContext, *MemoryMap, *RegisterMap, ) Error!void; -pub const Entry = struct { - name: []const u8, - builtin_fn: Syscall, +pub const Syscall = enum { + abort, + sol_panic_, + sol_alloc_free_, + + sol_log_, + sol_log_64_, + sol_log_pubkey, + sol_log_compute_units_, + sol_log_data, + + sol_create_program_address, + sol_try_find_program_address, + + sol_sha256, + sol_keccak256, + sol_blake3, + sol_poseidon, + + sol_secp256k1_recover, + sol_curve_validate_point, + sol_curve_group_op, + sol_curve_multiscalar_mul, + sol_alt_bn128_group_op, + sol_alt_bn128_compression, + + sol_get_clock_sysvar, + sol_get_epoch_schedule_sysvar, + sol_get_fees_sysvar, + sol_get_rent_sysvar, + sol_get_last_restart_slot, + sol_get_epoch_rewards_sysvar, + + sol_memcpy_, + sol_memmove_, + sol_memset_, + sol_memcmp_, + + sol_get_processed_sibling_instruction, + sol_get_stack_height, + sol_set_return_data, + sol_get_return_data, + sol_get_sysvar, + sol_get_epoch_stake, + sol_remaining_compute_units, + + sol_invoke_signed_c, + sol_invoke_signed_rust, + + /// We basically just store this as an array of syscall enumerations with their murmur hash as the value. + /// This makes lookups O(N) relative to the number of syscalls, however this shouldn't be a huge problem. + /// The hashmap approach we had before only slightly reduced the lookup times, all of which was wiped + /// out by the cost of allocating it. So this is the prefered approach for now. + /// + /// TODO: perhaps we can look into a PHF based approach here, although I imagine it'll just end up + /// being keyed by murmur and truncated to unique bits. + pub const Registry = struct { + map: std.EnumArray(Syscall, ?u32), + is_stubbed: bool, + + /// Relates Syscalls to the Murmur3 hash of their name, used for symbol collision checking. + pub const ALL_ENABLED: Registry = .{ .map = b: { + var kvs: std.enums.EnumFieldStruct(Syscall, ?u32, null) = undefined; + for (@typeInfo(Syscall).@"enum".fields) |field| { + @field(kvs, field.name) = Murmur3.hashWithSeed(field.name, 0); + } + break :b .init(kvs); + }, .is_stubbed = false }; + + pub const ALL_DISABLED: Registry = .{ .map = .initFill(null), .is_stubbed = false }; + + /// Returns a `SycallFn` based on the provided hash. + pub fn get(self: *const Registry, bytes: u32) ?SyscallFn { + const syscall: Syscall = for (self.map.values, 0..) |entry, i| { + const value = entry orelse continue; // disabled entries don't collide + if (value == bytes) break @enumFromInt(i); // assumes that the EnumArray will be in "array" mode, which it should be. + } else return null; // no such hash + // TODO: consider just making a build flag for harness builds that removes this check entirely for perf + return if (self.is_stubbed) &stubbed else map.get(syscall); + } + + fn stubbed(_: *TransactionContext, _: *MemoryMap, _: *RegisterMap) Error!void {} + + /// Generally spekaing this should only be used for tests and special setup. + /// Otherwise take the ALL_ENABLED -> disabling approach, since it's faster. + pub fn enable(self: *Registry, name: Syscall) void { + self.map.set(name, Murmur3.hashWithSeed(@tagName(name), 0)); + } + }; + + /// Lookup for the syscall's implementation function. + pub const map = std.EnumArray(Syscall, SyscallFn).init(.{ + .abort = abort, + .sol_panic_ = panic, + .sol_alloc_free_ = allocFree, + + .sol_log_ = log, + .sol_log_64_ = log64, + .sol_log_pubkey = logPubkey, + .sol_log_compute_units_ = logComputeUnits, + .sol_log_data = logData, + + .sol_create_program_address = createProgramAddress, + .sol_try_find_program_address = findProgramAddress, + + .sol_sha256 = hash.sha256, + .sol_keccak256 = hash.keccak256, + .sol_blake3 = hash.blake3, + .sol_poseidon = hash.poseidon, + + .sol_secp256k1_recover = ecc.secp256k1Recover, + .sol_curve_validate_point = ecc.curvePointValidation, + .sol_curve_group_op = ecc.curveGroupOp, + .sol_curve_multiscalar_mul = ecc.curveMultiscalarMul, + .sol_alt_bn128_group_op = ecc.altBn128GroupOp, + .sol_alt_bn128_compression = ecc.altBn128Compression, + + .sol_get_clock_sysvar = sysvar.getClock, + .sol_get_epoch_schedule_sysvar = sysvar.getEpochSchedule, + .sol_get_fees_sysvar = sysvar.getFees, + .sol_get_rent_sysvar = sysvar.getRent, + .sol_get_last_restart_slot = sysvar.getLastRestartSlot, + .sol_get_epoch_rewards_sysvar = sysvar.getEpochRewards, + .sol_get_sysvar = sysvar.getSysvar, + + .sol_memcpy_ = memops.memcpy, + .sol_memmove_ = memops.memmove, + .sol_memset_ = memops.memset, + .sol_memcmp_ = memops.memcmp, + + .sol_get_processed_sibling_instruction = getProcessedSiblingInstruction, + .sol_get_stack_height = getStackHeight, + .sol_set_return_data = setReturnData, + .sol_get_return_data = getReturnData, + .sol_get_epoch_stake = getEpochStake, + .sol_remaining_compute_units = remainingComputeUnits, + + .sol_invoke_signed_c = cpi.invokeSignedC, + .sol_invoke_signed_rust = cpi.invokeSignedRust, + }); + + const Gate = struct { + feature: Feature, + /// Whether the feature "disables", instead of "enabling" the syscall. + invert: bool = false, + }; + + /// Describes syscalls whos activation is locked behind a feature gate. + pub const gates = std.EnumArray(Syscall, ?Gate).initDefault(@as(?Gate, null), .{ + // NOTE: also needs to check for `reject_deployment_of_broken_elfs`. + .sol_alloc_free_ = .{ .feature = .disable_deploy_of_alloc_free_syscall, .invert = true }, + + .sol_blake3 = .{ .feature = .blake3_syscall_enabled }, + .sol_poseidon = .{ .feature = .enable_poseidon_syscall }, + + .sol_curve_validate_point = .{ .feature = .curve25519_syscall_enabled }, + .sol_curve_group_op = .{ .feature = .curve25519_syscall_enabled }, + .sol_curve_multiscalar_mul = .{ .feature = .curve25519_syscall_enabled }, + .sol_alt_bn128_group_op = .{ .feature = .enable_alt_bn128_syscall }, + .sol_alt_bn128_compression = .{ .feature = .enable_alt_bn128_compression_syscall }, + + .sol_get_fees_sysvar = .{ .feature = .disable_fees_sysvar, .invert = true }, + .sol_get_last_restart_slot = .{ .feature = .last_restart_slot_sysvar }, + .sol_remaining_compute_units = .{ .feature = .remaining_compute_units_syscall_enabled }, + .sol_get_sysvar = .{ .feature = .get_sysvar_syscall_enabled }, + .sol_get_epoch_stake = .{ .feature = .enable_get_epoch_stake_syscall }, + }); }; // logging @@ -642,8 +806,6 @@ fn callProgramAddressSyscall( program_id: Pubkey, overlap_outputs: bool, ) !struct { Pubkey, u8 } { - comptime std.debug.assert(builtin.is_test); - const seeds_addr = 0x100000000; const program_id_addr = 0x200000000; const address_addr = 0x300000000; diff --git a/src/vm/tests.zig b/src/vm/tests.zig index 3dbd88169e..3743561b9a 100644 --- a/src/vm/tests.zig +++ b/src/vm/tests.zig @@ -9,14 +9,14 @@ const executor = sig.runtime.executor; const InstructionInfo = sig.runtime.InstructionInfo; const Elf = sig.vm.elf.Elf; const Executable = sig.vm.Executable; -const Registry = sig.vm.Registry; -const Syscall = sig.vm.syscalls.Syscall; const Config = sig.vm.Config; const Region = sig.vm.memory.Region; const MemoryMap = sig.vm.memory.MemoryMap; const Vm = sig.vm.interpreter.Vm; const VmResult = sig.vm.interpreter.Result; const OpCode = sbpf.Instruction.OpCode; +const SyscallMap = sig.vm.SyscallMap; +const Syscall = sig.vm.syscalls.Syscall; const expectEqual = std.testing.expectEqual; const createTransactionContext = sig.runtime.testing.createTransactionContext; @@ -45,7 +45,7 @@ fn testAsmWithMemory( ) !void { const allocator = std.testing.allocator; - var loader: Registry(Syscall) = .{}; + var loader: SyscallMap = .ALL_DISABLED; var executable = try Executable.fromAsm(allocator, source, config); defer executable.deinit(allocator); @@ -1996,8 +1996,8 @@ test "pqr" { const config: Config = .{ .maximum_version = .v2 }; - var registry: sig.vm.Registry(u64) = .{}; - var loader: Registry(Syscall) = .{}; + var registry: sig.vm.Registry = .{}; + var loader: SyscallMap = .ALL_DISABLED; var executable = try Executable.fromTextBytes( allocator, &program, @@ -2046,8 +2046,8 @@ test "pqr divide by zero" { const config: Config = .{ .maximum_version = .v2 }; - var registry: sig.vm.Registry(u64) = .{}; - var loader: Registry(Syscall) = .{}; + var registry: sig.vm.Registry = .{}; + var loader: SyscallMap = .ALL_DISABLED; var executable = try Executable.fromTextBytes( allocator, &program, @@ -2300,7 +2300,7 @@ fn testElf(config: Config, path: []const u8, expected: anytype) !void { pub fn testElfWithSyscalls( config: Config, path: []const u8, - extra_syscalls: []const syscalls.Entry, + extra_syscalls: []const Syscall, expected: anytype, ) !void { const allocator = std.testing.allocator; @@ -2309,16 +2309,8 @@ pub fn testElfWithSyscalls( const bytes = try input_file.readToEndAlloc(allocator, sbpf.MAX_FILE_SIZE); defer allocator.free(bytes); - var loader: Registry(Syscall) = .{}; - defer loader.deinit(allocator); - - for (extra_syscalls) |syscall| { - _ = try loader.registerHashed( - allocator, - syscall.name, - syscall.builtin_fn, - ); - } + var loader: SyscallMap = .ALL_DISABLED; + for (extra_syscalls) |syscall| loader.enable(syscall); const elf = try Elf.parse(allocator, bytes, &loader, config); var executable = Executable.fromElf(elf); @@ -2452,7 +2444,7 @@ test "syscall reloc 64_32" { try testElfWithSyscalls( .{ .maximum_version = .v0 }, sig.ELF_DATA_DIR ++ "syscall_reloc_64_32_sbpfv0.so", - &.{.{ .name = "log", .builtin_fn = syscalls.log }}, + &.{.sol_log_}, .{ 0, 105 }, ); } @@ -2461,7 +2453,7 @@ test "static syscall" { try testElfWithSyscalls( .{}, sig.ELF_DATA_DIR ++ "syscall_static.so", - &.{.{ .name = "log", .builtin_fn = syscalls.log }}, + &.{.sol_log_}, .{ 0, 106 }, ); } @@ -2510,29 +2502,6 @@ test "bss section" { ); } -test "hash collision" { - // Mined Murmur3_32 hashes until I found one that collided with - // `hashSymbolName(&std.mem.toBytes(@as(u64, 0)))` - const colliding_name: []const u8 = &.{ - 0x6b, 0x2b, 0xad, 0xc9, 0xea, 0x56, 0xe0, 0x18, 0x4e, 0xf9, 0xce, 0x29, - 0xf6, 0x48, 0x40, 0x80, 0xc2, 0xb2, 0x2e, 0xca, 0x1b, 0x4d, 0xc1, 0x22, - 0xd5, 0x59, 0x39, 0xeb, 0xfb, 0x86, 0xc2, 0xe3, 0x18, 0xbc, 0xdc, 0x2e, - 0x68, 0x23, 0x1, 0xb4, 0x86, 0x65, 0xb0, 0xc4, 0x71, 0x65, 0x26, 0x89, - 0x5d, 0xbe, 0xbc, 0x4f, 0xd6, 0xe9, 0xff, 0x9e, 0xf6, 0x76, 0x81, 0x1d, - 0xb6, 0xb0, 0x99, 0x95, - }; - - try expectEqual( - error.SymbolHashCollision, - testElfWithSyscalls( - .{ .maximum_version = .v0 }, - sig.ELF_DATA_DIR ++ "hash_collision_sbpfv0.so", - &.{.{ .name = colliding_name, .builtin_fn = syscalls.abort }}, - .{ 0, 0 }, - ), - ); -} - // Verification tests fn testVerify( @@ -2554,23 +2523,15 @@ fn testVerifyTextBytes( fn testVerifyTextBytesWithSyscalls( config: Config, program: []const u8, - extra_syscalls: []const syscalls.Entry, + extra_syscalls: []const Syscall, expected: anytype, ) !void { const allocator = std.testing.allocator; - var loader: Registry(Syscall) = .{}; - defer loader.deinit(allocator); - - for (extra_syscalls) |syscall| { - _ = try loader.registerHashed( - allocator, - syscall.name, - syscall.builtin_fn, - ); - } + var loader: SyscallMap = .ALL_DISABLED; + for (extra_syscalls) |syscall| loader.enable(syscall); - var function_registry: sig.vm.Registry(u64) = .{}; + var function_registry: sig.vm.Registry = .{}; var executable = try Executable.fromTextBytes( allocator, program, @@ -2588,21 +2549,13 @@ fn testVerifyTextBytesWithSyscalls( fn testVerifyWithSyscalls( config: Config, source: []const u8, - extra_syscalls: []const syscalls.Entry, + extra_syscalls: []const Syscall, expected: anytype, ) !void { const allocator = std.testing.allocator; - var loader: Registry(Syscall) = .{}; - defer loader.deinit(allocator); - - for (extra_syscalls) |syscall| { - _ = try loader.registerHashed( - allocator, - syscall.name, - syscall.builtin_fn, - ); - } + var loader: SyscallMap = .ALL_DISABLED; + for (extra_syscalls) |entry| loader.enable(entry); var executable = try Executable.fromAsm(allocator, source, config); defer executable.deinit(allocator); @@ -2775,7 +2728,7 @@ test "unknown syscall" { try testVerifyTextBytes( .{}, &.{ - 0x95, 0x00, 0x00, 0x00, 0xDD, 0x0C, 0x02, 0x91, // syscall gather_bytes + 0x95, 0x00, 0x00, 0x00, 0xBD, 0x59, 0x75, 0x20, // syscall sol_log_ 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // return }, error.InvalidSyscall, @@ -2786,10 +2739,10 @@ test "known syscall" { try testVerifyTextBytesWithSyscalls( .{}, &.{ - 0x95, 0x00, 0x00, 0x00, 0xDD, 0x0C, 0x02, 0x91, // syscall gather_bytes + 0x95, 0x00, 0x00, 0x00, 0xBD, 0x59, 0x75, 0x20, // syscall sol_log_ 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // return }, - &.{.{ .name = "gather_bytes", .builtin_fn = syscalls.log }}, + &.{.sol_log_}, {}, ); }