Wagon实现一个log函数
[TOC]
实现方案
如果要实现一个自己的函数,有两种实现方式,一种是在虚拟机内部直接以内置指令的方式实现,如add函数等,但是这种方式需要修改编译器,并且通用性不够好,另一种就是通过解决外部引用的方式实现,这种的兼容性较好,需要解决的问题就是在内部解析引用的时候实现内部导入。
案例分析
如果我们需要实现一个log函数,那么就需要解决外部符号导入问题,如下面代码:
int main() {
Println("hello world");
return 0;
}它的wast文件为:
它的wasm文件内容如下:
解析器在解析文件时需要找到env中的Println函数,然后将hello world放入堆栈,接着调用call函数执行Println函数打印出hello world,那么我们需要解决的就是在虚拟机内部实现env中Println函数的解析部分。
解析流程
在上一章节中我们分析过,文件的解析主要在函数wasm.ReadModule(f, importer)中,它首先循环读取各个分区:
然后再去解决导入的函数:
这里的解决办法是从env的wasm文件中读取导入的函数:
然后再创建新的vm时会将函数列表放入vm中:
最后根据堆栈来调用相关函数:
函数从module到vm的拷贝过程如下:
那么这里的问题就是如何在保存现有解析新wasm文件的基础上,跳过对Println函数的解析呢?而且需要根据fn的规则实现一个内部的Println。
工程改造
首先是跳过部分,这部分比较简单,我们在解析外部引用时手动判断env并跳过即可:
这里就跳过了对env中Println的解析,注意这里需要添加code,否则会导致空指令的执行,这里的41表示call指令,我们需要在函数执行call时对自己的函数进行处理。 还有就是我们添加了两个变量,IsEnv和Name,这两个变量可以判断函数是否是我们自己实现的,从而实现外部函数的分支调用。
下一步就是创建新的VM了,创建新的VM时需要在函数列表中添加上我们加入的两个变量:
然后函数执行时就会调用到我们放入的code:
code里是0x41和0x2b,执行VM的函数调用:
funcTable是在创建VM时设置的,实际调用的是vm的call函数:
我们可以在这个函数中做分支,实现自己的API方法,当然,现在的Println函数并没有对堆栈进行处理,只是最简单的流程,如果涉及到堆栈处理,就更复杂一些了。
最后更新于
这有帮助吗?