[features]
default = ["use_std"]
use_std = []
[dependencies]
itertools = { version = "0.7.8" , default-features = false, features = []}
def 移植(self):
if self支持 no_std then
不用修改,直接在依赖处配置好 no_std 的 features
return
# 移植依赖项 (忽略dev-dependencies)
for each dep of self.dependencies
移植 dep
# 移植自身
(1) wget 库代码 && tar xzf
(2) 编辑 Cargo.toml 修改每个依赖项为移植后的依赖项
(3) 编辑 src/lib.rs 添加特定header(见后文)
(4) 编辑每个源文件 添加 use std::prelude::v1::*;
(5) 仔细review每个使用 fs/path/net/time/env 等不可信输入的地方,修正那里的逻辑
(6) 检查每个 platform dependent 的 feature,将其固定为只适用于 linux-x86_64 的逻辑(因为 linux-SGX 就只有这个环境)
(7) 测试 `cargo build` 是否通过
return
wget https://crates.io/api/v1/crates/http/0.1.8/download -O http-0.1.8.crate
http v0.1.8 (file:///tmp/http-0.1.8)
[dependencies]
├── bytes v0.4.9
│ [dependencies]
│ ├── byteorder v1.2.3
│ └── iovec v0.1.2
│ [dependencies]
│ └── libc v0.2.42
├── fnv v1.0.6
└── itoa v0.4.2
[dev-dependencies]
...
[depdendencies]
sgx_trts = "=1.0.1"
sgx_tstd = "=1.0.1"
#![no_std]
extern crate sgx_tstd as std;
extern crate sgx_trts;
mod sys;
use std::{ops, mem};
pub mod unix;
use std::prelude::v1::*;
use IoVec;
use sgx_trts::libc;
use std::mem;
把src/sys/mod.rs改成:
mod unix;
pub use self::unix::{
IoVec,
MAX_LENGTH,
};
这里去掉了所有的unix和不必要的代码。把src/sys/unix.rs改成:
use std::prelude::v1::*;
use sgx_trts::libc;
use std::{mem, slice, usize};
~/iovec-0.1.2 $ cargo b
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading sgx_tstd v1.0.1
Downloading sgx_unwind v0.0.2
Downloading sgx_tprotected_fs v1.0.1
Downloading sgx_alloc v1.0.1
Compiling cfg-if v0.1.4
Compiling libc v0.2.42
Compiling sgx_unwind v0.0.2
Compiling sgx_alloc v1.0.1
Compiling sgx_tprotected_fs v1.0.1
Compiling filetime v0.1.15
Compiling sgx_build_helper v0.1.0
Compiling sgx_tstd v1.0.1
Compiling iovec v0.1.2 (file:///tmp/iovec-0.1.2)
warning: unused import: `std::prelude::v1::*`=====================> ] 14/15: iovec
--> src/unix.rs:21:5
|
21 | use std::prelude::v1::*;
| ^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_imports)] on by default
Finished dev [unoptimized + debuginfo] target(s) in 5.18s
这有一点点技巧性。首先需要明晰在 xargo 环境中是有一个所谓的 sysroot,包含了所有这个 target 下“环境自带”的 crate。在 Rust SGX 环境中的 sysroot 大概包括的 crates 可以查看这里。
以sgx_trts为例,在cargo环境下,需要在Cargo.toml里显式引用sgx_trts,但是在纯xargo环境下则不需要。所以,同时支持 cargo + xargo的Cargo.toml长成这个样子:
[target.'cfg(not(target_env = "sgx"))'.dependencies]
sgx_tstd = "=1.0.1"
sgx_trts = "=1.0.1"
这里的语义不难理解。值得注意的是target_env = “sgx” 这里的值的来源是平台配置json:x86_64-unknown-linux-sgx.json。这里声明了:“env”: “sgx”。所以在支持xargo时还需要这个json文件的。
对于之上的(3)步,也有一些改变。不再是向上述所说直接配置为#![no_std],而是加入了一定的条件:
#![cfg_attr(not(target_env = "sgx"), no_std)]
#![cfg_attr(target_env = "sgx", feature(rustc_private))]
#[cfg(not(target_env = "sgx"))]
#[macro_use]
extern crate sgx_tstd as std;
extern crate sgx_trts;
这里的 rustc_private 是必须的,不然xargo不允许从sysroot里读取sgx_trts,而是强制用户从 crates.io 下载 sgx_trts。
排除最后一行sgx_trts不管,头部的5行是必须的,我把它叫做 “The Magic 5”。
对于一个具有复杂依赖关系的 Rust SGX enclave 来说,如果要用 xargo 编译,那么就一定需要为其指明 target json 和提供一个 Xargo.toml 来指导 xargo。如果要单独测试一个 crate 是否移植成功,则可以在其代码根目录(Cargo.toml所在的目录)下放置这两个文件,然后:
$ cargo b # 测试 cargo build
$ RUST_TARGET_PATH=$(pwd) xargo build --target x86_64-unknown-linux-sgx # 测试 xargo build
如果不指明RUST_TARGET_PATH,则xargo会在编译每个依赖项时去其代码根目录下寻找x86_64-unknown-linux-sgx.json,少一个都不行。这个“默认去每个依赖项下寻找json”的行为是在xargo的某个版本加上去的,为此我们不得不把json复制到了每个third_party的目录下……
这里以rust-crypto为例。注意到原版rust-crypto对于aesni指令集的处理是根据这么写的:
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub mod aesni;
所有支持 SGX 的 cpu 都是有 aesni 支持的(参考 sgx-hardware),所以我们可以简单的强制开启aesni模块。我们 Rust SGX 的编译过程中没定义target_arch,所以就需要手工处理掉所有的 target_arch 相关判断,让这个库无条件的启用 aesni 模块。(其实加上target_arch可能更优雅一点?)
优秀的可移植性是 Rust SGX 优于 C/C++ SGX 的最大特点。在此基础之上我们可以复用非常多的轮子来构造我们强壮的 Rust SGX enclave。我们已经移植了 rustls/webpki/ring、rust-crypto、wasmi、serde、protobuf 等相当复杂的库,提供了在 enclave 内 terminate TLS 的能力,以及执行任意 WebAssembly 程序的能力。此外,借助这个 TLS stack,在 enclave 内可以轻松构建出 https client 和 server,中间人彻底拜拜~(但是没法调试也是很蛋疼的……)