How to use `indirect_call` in WebAssembly?

Viewed 2476

There is no example of use of indirect_call available online. Based on semantics documentation, I tried

(call_indirect 
    (i32.const 0)
    (i32.const 0)
    )

Numbers are random, but instead of giving run-time error which I would have expected. I am getting parsing error.

What is the correct syntax for call_indirect?

2 Answers

An excerpt from the documentation:

Tables are basically resizable arrays of references that can be accessed by index from WebAssembly code. To see why tables are needed, we need to first observe that the call instruction takes a static function index and thus can only ever call one function — but what if the callee is a runtime value?

  • In JavaScript we see this all the time: functions are first-class values.
  • In C/C++, we see this with function pointers.
  • In C++, we see this with virtual functions.

WebAssembly needed a type of call instruction to achieve this, so we gave it call_indirect, which takes a dynamic function operand. The problem is that the only types we have to give operands in WebAssembly are (currently) i32/i64/f32/f64.

WebAssembly could add an anyfunc type ("any" because the type could hold functions of any signature), but unfortunately this anyfunc type couldn’t be stored in linear memory for security reasons. Linear memory exposes the raw contents of stored values as bytes and this would allow wasm content to arbitrarily observe and corrupt raw function addresses, which is something that cannot be allowed on the web.

The solution was to store function references in a table and pass around table indices instead, which are just i32 values. call_indirect’s operand can therefore be an i32 index value.

So, to sum it up, call_indirect needs the signature for validation, and takes an i32 value as an argument that points to the required function in the respective table. As with other WASM instructions, the argument can be either be popped from the stack, or retrieved from nested instructions. Thus we can have:

call_indirect (type 123)      ;; Type as an index in the table,
                              ;; argument on the stack from previous instructions.

or

call_indirect $funcSignature  ;; The same, but using a type name instead of index.

or

(call_indirect $funcSignature ;; The argument is provided by the nested instructions.
      (get_local $0)
)

See: https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format#webassembly_tables

Related