-
Notifications
You must be signed in to change notification settings - Fork 15
Helper Functions
The inNative Default Environment comes with several helper functions intended to make debugging native WebAssembly easier. All these functions are considered raw C functions with a default calling convention, and should be imported like any other C function. There are also some intrinsic functions provided by the compiler itself, which act like helper functions from WebAssembly's perspective. Some of these functions are not safe, and all of them must be whitelisted like other C functions if the whitelist is enabled.
(import "" "_innative_internal_env_print" (func $print (param i64)))
Prints out the hexadecimal representation of a single 64-bit integer to standard out, plus a newline. This is intended for investigating the values of WebAssembly stack values or local variables. It is written in such a way that most C++ compilers optimize all stack values out, allowing it to function even when the program state is corrupted.
Example:
(module
(import "" "_innative_internal_env_print" (func $print (param $i i64)))
(func $caller
i64.const 14
call $print
)
(start $caller)
)
(import "" "_innative_internal_write_out" (func $write (param $p cref) (param $i i64)))
This writes a string of bytes from a given location $p
in memory using the provided length $i
to the standard output. This is essentially fwrite($p, 1, $i, stdout);
and can be used as a primitive to re-implement standard output functions. The cref
parameter relies on the inNative cref Extension, but can be replaced with i64
if the _innative_to_c
helper function is used (see below).
Example:
(module
(import "" "_innative_internal_write_out" (func $write (param $p cref) (param $i i64)))
(memory (;0;) 1 1)
(func $caller
i32.const 0
i64.const 14
call $write
)
(start $caller)
(data (i32.const 0)
"Hello, world!\n\00"
)
)
(import "" "_innative_internal_env_memdump" (func $memdump (param $p cref) (param $i i64)))
This is a memory dump helper function, intended to inspect large blocks of memory for errors. It dumps a 2-character hexadecimal representation of each byte starting from $p and printing $i bytes. $p
uses the inNative cref Extension, but can also be changed to an i64
and paired with _innative_to_c
. Calling this function is identical to calling _innative_internal_write_out
, only the result is different.
(import "" "_innative_to_c" (func $toc (param i64) (result i64)))
This function takes a 64-bit integer index into the default linear memory and transforms it into a valid C pointer. This is done by adding the offset to the linear memory pointer. The resulting integer will have no meaning inside WebAssembly, but when sent to a C function expecting a pointer, it will point to that part of memory in the WebAssembly module. It takes and returns an i64
value, so you should call it right after pushing the index to the stack when calling a function. Here is an example that uses the _innative_to_c
function instead of cref
to call _innative_internal_env_memdump
:
(module
(import "" "_innative_internal_env_memdump" (func $dump (param $p i64) (param $i i64)))
(import "" "_innative_to_c" (func $toc (param i64) (result i64)))
(memory (;0;) 1 1)
(func $caller
i64.const 0
call $toc
i64.const 14
call $dump
)
(start $caller)
(data (i32.const 0)
"Hello, world!\n\00"
)
)
One advantage of using this function instead of cref
is that you can declare void* _innative_to_c(void*);
and then convince a C compiler to treat it as a normal function, which prevents having to write WebAssembly by hand, or having to adjust compiled WebAssembly modules by hand after the fact.
(import "" "_innative_from_c" (func $fromc (param i64) (result i64)))
This function takes a C pointer and subtracts the webassembly default linear memory location from that pointer. If this was originally a WebAssembly index, this will recover the original index and it can be used normally. However, it can also be used on C pointers pointing elsewhere. If the memory range checks are enabled, this will simply crash, but if memory range checks are disabled, it will enable your WebAssembly module to manipulate arbitrary C memory. This is obviously extremely dangerous and should only be done by trusted modules, but it is an option. Care must be taken if working with 64-bit pointers, as they are likely to be out of the range of the 32-bit integer that webassembly requires for the memory load instructions.
This function is particularly special because cref
does not convert back into an integer for you, so the only way to get this functionality is by using _innative_from_c
(mostly because it breaks the sandbox and allows the module to inspect arbitrary memory pointers).
Example:
(module
(import "" "unsafe_function" (func $unsafe (param f32) (result i64)))
(import "" "_innative_from_c" (func $fromc (param i64) (result i64)))
(memory (;0;) 1 1)
(func $caller
f32.const 0
call $unsafe
call $fromc
i32.wrap/i64
i64.load
drop
)
(start $caller)
(data (i32.const 0)
"Hello, world!\n\00"
)
)
(import "" "_innative_funcptr" (func $funcptr (param i32) (result i64)))
This function returns a raw C function pointer to the given function index. It's important to remember that WebAssembly function indexes include imported functions. The returned pointer is returned as a 64-bit integer that can be passed to a C function that expects a function pointer. The calling convention of the function is preserved. The function index is a 32-bit integer (since WebAssembly function indexes are themselves 32-bit). Because this works with imported functions, it can be used to get the function pointer to a C function.
(import "" "_innative_trap" (func $trap))
This function simply forces a trap instruction to be generated, in the same way a WebAssembly trap would generate the instruction (which is usually an illegal instruction that triggers SIGILL
on x86).