初始化enclave
1 | init_enclave() -> SgxResult<SgxEnclave> |
底层调用rsgx_create_enclave【通过file_name和launch_token来初始化一个enclave实例】,参数如下:
file_name:经过编译后的enclave签名文件(enclave.signed.so)
debug:
- 0 - 不开启debug
- 1 - 开启debug,enclave中的数据代码可以被enclave外的程序访问,用于调试
launch_token:初始化enclave时可以传入全0 buffer,函数会创建一个有效token与enclave对应,并更新该参数。之后如果要重新创建这个enclave,可以再使用这个token
launch_token_updated:
- 0 - token没有被自动更新
- 1 - token被自动更新
misc_attr:misc选择和enclave属性 {暂时不清楚作用}
init_enclave()返回一个SgxEnclave结构
1 | pub struct SgxEnclave { |
初始化sgxwasm的SpecDriver
1 | fn sgx_enclave_wasm_init(enclave: &SgxEnclave) -> Result<(),String> |
调用ECall函数sgxwasm_init(),传入两个隐参数:
- enclave_id:通过enclave.geteid()获取
- retval:ECall函数的返回值,由Intel定义,不用在ECall函数中手动修改
在Enclave中会通过lazy_static的方式建立一个全局变量SPECDRIVER(带有互斥量)
1 | lazy_static!{ |
sgxwasm_init()在SPECDRIVER中新建一个SpecDriver
1 | pub struct SpecDriver { |
在Enclave外加载wast文件
1 | wasm_main_loop(wast_file: &str, enclave: &SgxEnclave) -> Result<(), String> |
首先对读入的wast_file文本进行分析(使用ScriptParser),读取每一行的指令到结构Command中:
1 | pub struct Command<F32 = f32, F64 = f64> { |
对每一行指令匹配CommandKind,并执行对应的操作:
1 | pub enum CommandKind<F32 = f32, F64 = f64> { |
指令Action
wasm模块中的指令:
1 | pub enum Action<F32 = f32, F64 = f64> { |
sgxwasm中定义的指令:
1 | pub enum SgxWasmAction { |
CommandKind::Module
1 | // 加载一个模块 |
创建一个SgxWasmAction::LoadModule结构,再调用sgx_enclave_wasm_invoke()
CommandKind::AssertReturn
1 | // 根据wasm的Action执行指令 |
先匹配Action:
- 如果是Action::Invoke,那么创建一个SgxWasmAction::Invoke结构并交给sgx_enclave_wasm_invoke()
- 如果是Action::Get,那么创建一个SgxWasmAction::Get结构并交给sgx_enclave_wasm_invoke()
然后根据sgx_enclave_wasm_invoke()的返回结果来判断输出是否是我们想要的
**备注:**对于另外一些CommandKind(包括CommandKind::AssertReturnCanonicalNan、CommandKind::AssertReturnArithmeticNan、CommandKind::AssertExhaustion、CommandKind::AssertTrap),过程同CommandKind::AssertReturn,只不过是对返回值的判断有所不同
**备注2:**对于CommandKind::PerformAction,只需匹配和执行Action,不需要后面的判断
CommandKind::AssertInvalid
1 | // 尝试加载一个模块 |
先创建一个SgxWasmAction::TryLoad结构,再交给sgx_enclave_wasm_invoke()
**备注:**对于另外一些CommandKind(包括CommandKind::AssertMalformed、CommandKind::AssertUnlinkable、CommandKind::AssertUninstantiable),由于这些类型的SgxWasmAction都不能加载可用的模块,所以只能尝试加载模块
CommandKind::Register
1 | // 注册一个模块 |
创建一个SgxWasmAction::Register结构,再交给sgx_enclave_wasm_invoke()
调用函數sgx_enclave_wasm_invoke()
1 | // 根据SgxWasmAction通过ECall执行指定的指令 |
- req_str:将SgxWasmAction序列化得到
- result_max_len:返回结果的最大长度
sgx_enclave_wasm_invoke()调用ECall函数sgxwasm_run_action()
安全函数sgxwasm_run_action()
1 | fn sgxwasm_run_action(req_bin : *const u8, req_length: usize, |
- req_bin:需要执行的SgxWasmAction请求
- req_length:请求长度
- result_bin:返回的内容
- result_max_len:返回值的最大长度
首先通过req_bin和req_length来在Enclave中创建一个安全的SgxWasmAction结构action_req,再对其进行匹配:
SgxWasmAction::Invoke
1 | // 调用函数 |
首先获取全局的SPECDRIVER存入变量program,根据module的值在SpecDriver.instances中查找对应名字的模块,如果module的值爲None,则通过SpecDriver.last_module返回最后注册的模块,得到一个ModuleRef结构module通过module的invoke_export()方法调用导出函数:
1 | module.invoke_export(&field, &args, program.spec_module()) |
- &field:函数名
- &args:传入函数的参数
- program.spec_module():externals,导入wasm函数的外部环境,实现Externals trait
invoke_export()方法通过函数名查找导出函数,得到FuncRef类型的func_instance,并使用 FuncInstance::invoke(&func_instance, args, externals)
调用函数,FuncInstance结构是一个函数实例,在wasm中,函数的定义方式有两种:在wasm模块中定义或者从宿主环境导入,分别对应FuncInstanceInternal::Internal和FuncInstanceInternal::Host
1 | pub struct FuncInstance(FuncInstanceInternal); |
在FuncInstance::invoke()调用函数时,首先判断传入参数是否符合函数籤名,然后匹配函数的定义方式FuncInstanceInternal
-
Internal:初始化一个Interpreter,然后调用
interpreter.start_execution(externals)
执行函数(更底层的内容可能会写在下一章)1
2
3
4
5
6pub struct Interpreter {
value_stack: ValueStack,
call_stack: CallStack,
return_type: Option<ValueType>,
state: InterpreterState,
} -
Host:调用
externals.invoke_index(*host_func_index, args.into())
,externals即爲invoke_export()方法传入的第三参数:导入函数的外部环境。这里的externals就是SpecModule,它实现的Externals trait如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15impl Externals for SpecModule {
fn invoke_index(
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> {
match index {
PRINT_FUNC_INDEX => {
println!("print: {:?}", args);
Ok(None)
}
_ => panic!("SpecModule doesn't provide function at index {}", index),
}
}
}
SgxWasmAction::Get
1 | // 获取全局变量 |
首先获取全局的SPECDRIVER存入变量program,根据module的值在SpecDriver.instances中查找对应名字的模块,如果module的值爲None,则通过SpecDriver.last_module返回最后注册的模块,得到一个ModuleRef结构module
然后通过 module.export_by_name(&field)
获得一个wasm的运行时的实体引用ExternVal,可能的值包括:函数、Table、Memory、全局变量
1 | pub enum ExternVal { |
接着匹配module的该导出实体是否爲Global,若不是,返回None
SgxWasmAction::LoadModule
1 | // 加载一个模块到SpecDriver中 |
首先获取全局的SPECDRIVER存入变量spec_driver,然后通过 try_load_module(&module[..])
获得一个parity_wasm库的Module类型的module,接着通过 ModuleInstance::new(&module, &**spec_driver)
的方式新建一个NotStartedModuleRef实例,并使用 run_start(spec_driver.spec_module())
方法将其转变爲ModuleRef类型
最后将该实例通过 spec_driver.add_module(name, instance.clone())
存放入spec_driver中,将SpecDriver.last_module改爲该模块,并在SpecDriver.instances中插入该模块与其名字的键值对
SgxWasmAction::TryLoad
1 | // 尝试加载一个模块,不加载到SpecDriver中 |
首先获取全局的SPECDRIVER存入变量spec_driver,然后通过 try_load_module(&module[..])
获得一个parity_wasm库的Module类型的module,接着通过 ModuleInstance::new(&module, &ImportsBuilder::default())
的方式新建一个NotStartedModuleRef实例instance,尝试对其使用 run_start(spec_driver.spec_module())
方法,若成功,返回Ok(()),若失败,返回错误
SgxWasmAction::Register
1 | // 使用as_name注册一个新模块 |
首先获取全局的SPECDRIVER存入变量spec_driver,根据module的值在SpecDriver.instances中查找对应名字name的模块,如果name的值爲None,则通过SpecDriver.last_module返回最后注册的模块
最后将该模块通过 spec_driver.add_module(name, instance.clone())
存放入spec_driver中,将SpecDriver.last_module改爲该模块,并在SpecDriver.instances中插入该模块与其名字的键值对