获取 WebAssembly 导出函数的地址?
Get address of a WebAssembly exported function?
假设有这个 C 代码,
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
void *add_ptr() { return add; }
void *mul_ptr() { return mul; }
int op(int a, int b, int (*op) (int a, int b)) { return op(a, b); }
void *op_ptr() { return op; }
并使用常规 clang 8/9 编译它以构建 wasm 文件,
clang --target=wasm32 a.c -nostdlib -Wl,--no-entry -Wl,--export=add -Wl,--export=mul -Wl,--export=mul_ptr -Wl,--export=add_ptr -Wl,--export=op -Wl,--export=op_ptr -o a.wasm
并且运行它使用html这样的页面,
<script>
WebAssembly.instantiateStreaming(fetch('a.wasm')).then(result => alert(`
add: ${result.instance.exports.add(2, 3)}
mul: ${result.instance.exports.mul(2, 3)}
op_add: ${result.instance.exports.op(2, 3, result.instance.exports.add_ptr())}
op_mul: ${result.instance.exports.op(2, 3, result.instance.exports.mul_ptr())}
add_ptr: ${result.instance.exports.add_ptr()}
mul_ptr: ${result.instance.exports.mul_ptr()}
op_ptr: ${result.instance.exports.op_ptr()}
add.name: ${result.instance.exports.add.name}
mul.name: ${result.instance.exports.mul.name}
add_ptr.name: ${result.instance.exports.add_ptr.name}
mul_ptr.name: ${result.instance.exports.mul_ptr.name}
op.name: ${result.instance.exports.op.name}
`));
</script>
这给出了,
add: 5
mul: 6
op_add: 5
op_mul: 6
add_ptr: 1
mul_ptr: 2
op_ptr: 3
add.name: 0
mul.name: 1
add_ptr.name: 2
mul_ptr.name: 3
op.name: 4
在没有 {add|mul}_ptr
类函数的情况下,在 WebAssembly 实例中获取 add
/mul
指针地址的可靠方法是什么?它不能是 .name
,因为 op.name
不同于 op_ptr
结果。
这里的最终目的(我已经将我的用例缩减为这个片段)是有 add
或 mul
地址并将它传递给 op
就像获取函数的函数指针。
WebAssembly 函数及其包含的可执行代码位于与数据不同的命名空间中。因此,您无法获取函数的地址/指针并将其用作调用它的间接机制。
我认为您在代码中看到的是 WebAssembly 模块 function section.
中函数的索引
add_ptr: 1
mul_ptr: 2
op_ptr: 3
可以通过底层 call_indirect
WebAssembly 操作执行动态函数调用。这允许您调用存储在 table section.
中的函数
但是,我不确定 clang 编译器是否支持利用此功能来支持您正在寻找的行为的方法。
假设有这个 C 代码,
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
void *add_ptr() { return add; }
void *mul_ptr() { return mul; }
int op(int a, int b, int (*op) (int a, int b)) { return op(a, b); }
void *op_ptr() { return op; }
并使用常规 clang 8/9 编译它以构建 wasm 文件,
clang --target=wasm32 a.c -nostdlib -Wl,--no-entry -Wl,--export=add -Wl,--export=mul -Wl,--export=mul_ptr -Wl,--export=add_ptr -Wl,--export=op -Wl,--export=op_ptr -o a.wasm
并且运行它使用html这样的页面,
<script>
WebAssembly.instantiateStreaming(fetch('a.wasm')).then(result => alert(`
add: ${result.instance.exports.add(2, 3)}
mul: ${result.instance.exports.mul(2, 3)}
op_add: ${result.instance.exports.op(2, 3, result.instance.exports.add_ptr())}
op_mul: ${result.instance.exports.op(2, 3, result.instance.exports.mul_ptr())}
add_ptr: ${result.instance.exports.add_ptr()}
mul_ptr: ${result.instance.exports.mul_ptr()}
op_ptr: ${result.instance.exports.op_ptr()}
add.name: ${result.instance.exports.add.name}
mul.name: ${result.instance.exports.mul.name}
add_ptr.name: ${result.instance.exports.add_ptr.name}
mul_ptr.name: ${result.instance.exports.mul_ptr.name}
op.name: ${result.instance.exports.op.name}
`));
</script>
这给出了,
add: 5
mul: 6
op_add: 5
op_mul: 6
add_ptr: 1
mul_ptr: 2
op_ptr: 3
add.name: 0
mul.name: 1
add_ptr.name: 2
mul_ptr.name: 3
op.name: 4
在没有 {add|mul}_ptr
类函数的情况下,在 WebAssembly 实例中获取 add
/mul
指针地址的可靠方法是什么?它不能是 .name
,因为 op.name
不同于 op_ptr
结果。
这里的最终目的(我已经将我的用例缩减为这个片段)是有 add
或 mul
地址并将它传递给 op
就像获取函数的函数指针。
WebAssembly 函数及其包含的可执行代码位于与数据不同的命名空间中。因此,您无法获取函数的地址/指针并将其用作调用它的间接机制。
我认为您在代码中看到的是 WebAssembly 模块 function section.
中函数的索引add_ptr: 1
mul_ptr: 2
op_ptr: 3
可以通过底层 call_indirect
WebAssembly 操作执行动态函数调用。这允许您调用存储在 table section.
但是,我不确定 clang 编译器是否支持利用此功能来支持您正在寻找的行为的方法。