From 40cc4d5590d7c42c3af88552e5d2010031e3e7c4 Mon Sep 17 00:00:00 2001 From: Reuben Dunnington Date: Tue, 1 Aug 2023 14:02:23 -0700 Subject: [PATCH] wip ffi * stub out ___chkstk_ms to avoid linking errors when zig.exe is not the linker * expose globals * expose import errors --- build.zig | 5 +++-- src/bytebox.h | 18 ++++++++++++++++++ src/cffi.zig | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 0c130aa..ac72651 100644 --- a/build.zig +++ b/build.zig @@ -3,6 +3,7 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; const Builder = std.build.Builder; const LibExeObjStep = std.build.LibExeObjStep; +const InstallFileStep = std.build.InstallFileStep; const ExeOpts = struct { exe_name: []const u8, @@ -47,9 +48,9 @@ pub fn build(b: *Builder) void { }, }); - var c_header = b.addInstallFileWithDir(std.build.FileSource{ .path = "src/bytebox.h" }, .header, "bytebox.h"); + var c_header: *InstallFileStep = b.addInstallFileWithDir(std.build.FileSource{ .path = "src/bytebox.h" }, .header, "bytebox.h"); - const lib_bytebox = b.addStaticLibrary("bytebox", "src/cffi.zig"); + const lib_bytebox: *LibExeObjStep = b.addStaticLibrary("bytebox", "src/cffi.zig"); lib_bytebox.setTarget(target); lib_bytebox.setBuildMode(b.standardReleaseOptions()); lib_bytebox.step.dependOn(&c_header.step); diff --git a/src/bytebox.h b/src/bytebox.h index 1171162..d4fc133 100644 --- a/src/bytebox.h +++ b/src/bytebox.h @@ -17,6 +17,8 @@ enum bb_error BB_ERROR_OUTOFMEMORY, BB_ERROR_INVALIDPARAM, BB_ERROR_UNKNOWNEXPORT, + BB_ERROR_UNKNOWNIMPORT, + BB_ERROR_INCOMPATIBLEIMPORT, }; typedef enum bb_error bb_error; @@ -90,6 +92,21 @@ typedef enum bb_debug_trap_mode bb_debug_trap_mode; typedef void bb_host_function(void* userdata, bb_module_instance* module, const bb_val* params, bb_val* returns); +enum bb_global_mut +{ + BB_GLOBAL_MUT_IMMUTABLE, + BB_GLOBAL_MUT_MUTABLE, +}; +typedef enum bb_global_mut bb_global_mut; + +struct bb_global +{ + bb_val* value; + bb_valtype type; + bb_global_mut mut; +}; +typedef struct bb_global bb_global; + // typedef void* bb_malloc_func(size_t size, void* userdata); // typedef void* bb_realloc_func(void* mem, size_t size, void* userdata); // typedef void bb_free_func(void* mem, void* userdata); @@ -118,5 +135,6 @@ bb_error bb_module_instance_step(bb_module_instance* instance, bb_val* returns, bb_error bb_module_instance_debug_set_trap(bb_module_instance* instance, uint32_t address, bb_debug_trap_mode trap_mode); void* bb_module_instance_mem(bb_module_instance* instance, size_t offset, size_t length); bb_slice bb_module_instance_mem_all(bb_module_instance* instance); +bb_global bb_module_instance_find_global(bb_module_instance* instance, const char* global_name); bool bb_func_handle_isvalid(bb_func_handle handle); diff --git a/src/cffi.zig b/src/cffi.zig index 3cbc456..8ba64ad 100644 --- a/src/cffi.zig +++ b/src/cffi.zig @@ -21,6 +21,8 @@ const CError = enum(c_int) { OutOfMemory, InvalidParameter, UnknownExport, + UnknownImport, + IncompatibleImport, }; const CModuleDefinitionInitOpts = extern struct { @@ -130,13 +132,15 @@ var cffi_gpa = std.heap.GeneralPurposeAllocator(.{}){}; // cffi_allocator.userdata = userdata; // } -export fn bb_error_str(c_error: CError) [*]const c_char { +export fn bb_error_str(c_error: CError) [*:0]const c_char { return switch (c_error) { .Ok => "BB_ERROR_OK", .Failed => "BB_ERROR_FAILED", .OutOfMemory => "BB_ERROR_OUTOFMEMORY", .InvalidParameter => "BB_ERROR_INVALIDPARAMETER", .UnknownExport => "BB_ERROR_UNKNOWNEXPORT", + .UnknownImport => "BB_ERROR_UNKNOWNIMPORT", + .IncompatibleImport => "BB_ERROR_INCOMPATIBLEIMPORT", }; } @@ -411,16 +415,62 @@ export fn bb_module_instance_mem_all(module: ?*ModuleInstance) CSlice { }; } +const CGlobalMut = enum(c_int) { + Immutable = 0, + Mutable = 1, +}; + +const CGlobalExport = extern struct { + value: ?*Val, + type: ValType, + mut: CGlobalMut, +}; + +export fn bb_module_instance_find_global(module: ?*ModuleInstance, c_global_name: ?[*:0]const c_char) CGlobalExport { + comptime { + std.debug.assert(@enumToInt(CGlobalMut.Immutable) == @enumToInt(core.GlobalMut.Immutable)); + std.debug.assert(@enumToInt(CGlobalMut.Mutable) == @enumToInt(core.GlobalMut.Mutable)); + } + + if (module != null and c_global_name != null) { + const global_name = std.mem.sliceTo(c_global_name.?, 0); + if (module.?.getGlobalExport(global_name)) |global| { + return CGlobalExport{ + .value = global.val, + .type = global.valtype, + .mut = @intToEnum(CGlobalMut, @enumToInt(global.mut)), + }; + } else |_| {} + } + + return CGlobalExport{ + .value = null, + .type = .I32, + .mut = .Immutable, + }; +} + export fn bb_func_handle_isvalid(c_handle: CFuncHandle) bool { return c_handle.index != INVALID_FUNC_INDEX; } +// NOTE: Zig expects this function to be present during linking, which would be fine if zig linked +// this code, but when linking with the MSVC compiler, the compiler runtime doesn't provide this +// function. Manually defining it here ensures the linker error gets resolved. +fn ___chkstk_ms() callconv(.Naked) void {} + +comptime { + @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = .Weak }); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// // Local helpers fn translateError(err: anyerror) CError { switch (err) { error.OutOfMemory => return CError.OutOfMemory, + error.UnlinkableUnknownImport => return CError.UnknownImport, + error.UnlinkableIncompatibleImportType => return CError.IncompatibleImport, else => return CError.Failed, } }