Compare commits

...

10 Commits

Author SHA1 Message Date
Hui, Chunyang
ffe6a750b0 Bump version to 0.30.1
Some checks are pending
SGX Hardware Mode Test / Sqlite_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Sqlite_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Python_musl_support_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Python_musl_support_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Openvino_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Openvino_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Grpc_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Grpc_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Gvisor_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Init_RA_grpc ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Gvisor_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Test_deb_deploy ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Tensorflow_serving_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Tensorflow_serving_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Remote_attestation_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Remote_attestation_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Init_RA_grpc ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Init_RA_AECS ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Init_RA_AECS ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / MySQL_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / MySQL_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Netty_UT_test ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Netty_UT_test ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Stress_test_with_musl ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Stress_test_with_musl ([self-hosted SGX2-HW]) (push) Waiting to run
SGX Hardware Mode Test / Stress_test_with_glibc ([self-hosted SGX2-HW EDMM]) (push) Waiting to run
SGX Hardware Mode Test / Stress_test_with_glibc ([self-hosted SGX2-HW]) (push) Waiting to run
Essential Test / Make_test_on_ubuntu (push) Waiting to run
Rune Test / Rune_test (ubuntu18.04) (push) Waiting to run
Rune Test / Rune_test (centos8.2) (push) Waiting to run
2024-03-06 17:45:10 +08:00
ClawSeven
f8e8d400d3 [time] Fix wrong SGX_CPUID leaf 2024-03-06 17:44:30 +08:00
Qi Zheng
060d2ec1b3 [deps] Update rust sgx sdk 2024-03-06 17:44:30 +08:00
Hui, Chunyang
3b90df67dc Add "kernel_heap_monitor" feature 2024-03-06 17:44:30 +08:00
ClawSeven
d3e84d2269 [time] Adapt vdso module to SGX1 platform 2024-03-06 17:44:30 +08:00
ClawSeven
501abda5ca [crates] Implement vdso for time precision 2024-03-06 17:44:30 +08:00
ClawSeven
5b704984e3 [crates] Seperate error module into errno crate 2024-03-06 17:44:30 +08:00
Qi Zheng
4027258ec5 [toolchains] Support set glibc branch for Occlum glibc build 2024-03-06 17:44:30 +08:00
Hui, Chunyang
0c1c0621e6 Fix feature configuration for make test 2024-03-06 17:44:30 +08:00
Hui, Chunyang
f58652da96 Remove reuse actions for release image 2024-03-06 15:59:26 +08:00
74 changed files with 3018 additions and 402 deletions

@ -12,12 +12,6 @@ on:
description: 'Whether it is a release image or test image (must choose from <Y/N/T>)'
required: true
default: 'N'
# If rc image works well, directly tag with the release version
# source image: "reuse_image" specified here
# target iamge: Get from the current source code
reuse_image:
description: 'Set reuse image name, e.g.: 0.30.0-rc (When release is set to <Y>, admin can choose to reuse an old rc image as the new release image)'
required: false
jobs:
generate-anolis-image:
@ -44,6 +38,16 @@ jobs:
# ${GITHUB_REF##*/} == branch-name
run: echo "OCCLUM_BRANCH=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV;
- name: Get image tag name for release image
if: github.event.inputs.release == 'Y'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
- name: Get image tage name for dev image
if: github.event.inputs.release == 'N'
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
- name: Get image tage name for test image
if: github.event.inputs.release == 'T'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
@ -58,19 +62,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get image tag name for release image
if: github.event.inputs.release == 'Y'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
- name: Get image tage name for dev image
if: github.event.inputs.release == 'N'
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
- name: Get image tage name for test image
if: github.event.inputs.release == 'T'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
# Rebuild new image
- name: Build and push
if: "${{ github.event.inputs.reuse_image == '' }}"
uses: docker/build-push-action@v2
with:
context: .
@ -80,15 +72,6 @@ jobs:
push: true
tags: occlum/occlum:${{ env.IMAGE_TAG }}-anolis8.8
# Reuse old image
- name: Reuse old image
if: "${{ github.event.inputs.reuse_image != '' }}"
run: |
echo "REUSE_VERSION=${{ github.event.inputs.reuse_image }}" >> $GITHUB_ENV;
docker pull occlum/occlum:${{ env.REUSE_VERSION }}-anolis8.8
docker tag occlum/occlum:${{ env.REUSE_VERSION }}-anolis8.8 occlum/occlum:${{ env.IMAGE_TAG }}-anolis8.8
docker push occlum/occlum:${{ env.IMAGE_TAG }}-anolis8.8
generate-ubuntu20-image:
runs-on: ubuntu-20.04
@ -113,6 +96,16 @@ jobs:
# ${GITHUB_REF##*/} == branch-name
run: echo "OCCLUM_BRANCH=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV;
- name: Get image tag name for release image
if: github.event.inputs.release == 'Y'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
- name: Get image tage name for dev image
if: github.event.inputs.release == 'N'
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
- name: Get image tage name for test image
if: github.event.inputs.release == 'T'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
@ -127,19 +120,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get image tag name for release image
if: github.event.inputs.release == 'Y'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
- name: Get image tage name for dev image
if: github.event.inputs.release == 'N'
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
- name: Get image tage name for test image
if: github.event.inputs.release == 'T'
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
# Rebuild new image
- name: Build and push
if: "${{ github.event.inputs.reuse_image == '' }}"
uses: docker/build-push-action@v2
with:
context: .
@ -148,12 +129,3 @@ jobs:
build-args: OCCLUM_BRANCH=${{ env.OCCLUM_BRANCH }}
push: true
tags: occlum/occlum:${{ env.IMAGE_TAG }}-ubuntu20.04
# Reuse old image
- name: Reuse old image
if: "${{ github.event.inputs.reuse_image != '' }}"
run: |
echo "REUSE_VERSION=${{ github.event.inputs.reuse_image }}" >> $GITHUB_ENV;
docker pull occlum/occlum:${{ env.REUSE_VERSION }}-ubuntu20.04
docker tag occlum/occlum:${{ env.REUSE_VERSION }}-ubuntu20.04 occlum/occlum:${{ env.IMAGE_TAG }}-ubuntu20.04
docker push occlum/occlum:${{ env.IMAGE_TAG }}-ubuntu20.04

@ -54,6 +54,15 @@ jobs:
container-name: ${{ github.job }}
build-envs: 'OCCLUM_RELEASE_BUILD=1'
# Udpate the test json file
# When there comes new features, the configuration should be enabled accordingly
- name: Configure Occlum features
run: |
if [[ "${{ matrix.self_runner[2] }}" == "EDMM" ]]; then
docker exec ${{ env.CONTAINER_NAME }} bash -c "jq '.feature.enable_posix_shm = true | .feature.enable_edmm = true' /root/occlum/test/Occlum.json > /tmp.json && mv /tmp.json /root/occlum/test/Occlum.json"
fi;
shell: bash
- name: Integration test
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum; OCCLUM_LOG_LEVEL=trace make test"

@ -68,6 +68,12 @@ jobs:
if: ${{ failure() }}
run: docker exec ${{ github.job }} bash -c "cat /root/occlum/build/test/.fail"
- name: Integration test with optional Occlum features
run: |
docker exec ${{ github.job }} bash -c 'source /opt/intel/sgxsdk/environment; cd /root/occlum; make clean && LIBOS_FEATURES="kernel_heap_monitor" make install'
docker exec ${{ github.job }} bash -c "cd /root/occlum; SGX_MODE=SIM make test-glibc"
shell: bash
# Make_test_on_centos:
# runs-on: ubuntu-18.04

2
deps/rust-sgx-sdk vendored

@ -1 +1 @@
Subproject commit e28e25ce718cc21b232a1bf37bb6446535d7ea00
Subproject commit 81384ce4d10c67eea5e1ba4ea332087940c1836b

@ -6,6 +6,7 @@ enclave {
from "sgx_tprotected_fs.edl" import *;
from "sgx_net.edl" import *;
from "sgx_occlum_utils.edl" import *;
from "sgx_vdso_time_ocalls.edl" import *;
include "sgx_quote.h"
include "occlum_edl_types.h"
@ -148,9 +149,6 @@ enclave {
int occlum_ocall_thread_getcpuclock([out] struct timespec* ts) propagate_errno;
void occlum_ocall_gettimeofday([out] struct timeval* tv);
void occlum_ocall_clock_gettime(clockid_t clockid, [out] struct timespec* ts);
void occlum_ocall_clock_getres(clockid_t clockid, [out] struct timespec* res);
void occlum_ocall_rdtsc([out] uint32_t* low, [out] uint32_t* high);
void occlum_ocall_get_timerslack([out] int *timer_slack);

2
src/exec/Cargo.lock generated

@ -542,7 +542,7 @@ dependencies = [
[[package]]
name = "occlum_exec"
version = "0.30.0"
version = "0.30.1"
dependencies = [
"chrono",
"clap",

@ -1,6 +1,6 @@
[package]
name = "occlum_exec"
version = "0.30.0"
version = "0.30.1"
edition = "2021"
[lib]

2
src/libos/Cargo.lock generated

@ -4,7 +4,7 @@ version = 3
[[package]]
name = "Occlum"
version = "0.30.0"
version = "0.30.1"
dependencies = [
"aligned",
"atomic",

@ -1,6 +1,6 @@
[package]
name = "Occlum"
version = "0.30.0"
version = "0.30.1"
edition = "2021"
[lib]
@ -25,6 +25,8 @@ rcore-fs-devfs = { path = "../../deps/sefs/rcore-fs-devfs" }
resolv-conf = { path = "../../deps/resolv-conf" }
serde = { path = "../../deps/serde-sgx/serde", features = ["derive"] }
serde_json = { path = "../../deps/serde-json-sgx" }
errno = { path = "crates/errno", features = ["occlum"] }
vdso-time = { path = "crates/vdso-time", default-features = false, features = ["sgx"] }
memoffset = "0.6.1"
scroll = { version = "0.11.0", default-features = false }
itertools = { version = "0.10.0", default-features = false, features = ["use_alloc"] }
@ -48,6 +50,7 @@ dcap = [] # DCAP support. The compilation relies on DCAP package.
cov = ["sgx_cov"] # Enable coverage colletcion.
hyper_mode = [] # For running in hyper mode.
pku = [] # PKU Support
kernel_heap_monitor = []# Kernel heap usage tracking. With overhead.
[target.'cfg(not(target_env = "sgx"))'.dependencies]
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }

@ -45,7 +45,7 @@ LIBOS_LOG ?= error
LIBOS_SONAME := libocclum-libos.so.$(MAJOR_VER_NUM)
LIBOS_FEATURES :=
LIBOS_FEATURES := $(LIBOS_FEATURES)
ifeq ($(SGX_MODE), HW)
LIBOS_CORE_LIB_NAME := occlum-libos-core
@ -162,7 +162,11 @@ $(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.o: $(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.c
@echo "CC <= $@"
$(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.c: $(SGX_EDGER8R) ../Enclave.edl
@cd $(OBJ_DIR)/libos/$(SRC_OBJ) && $(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --trusted $(CUR_DIR)/../Enclave.edl --search-path $(SGX_SDK)/include --search-path $(RUST_SGX_SDK_DIR)/edl
@cd $(OBJ_DIR)/libos/$(SRC_OBJ) && \
$(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --trusted $(CUR_DIR)/../Enclave.edl \
--search-path $(SGX_SDK)/include \
--search-path $(RUST_SGX_SDK_DIR)/edl \
--search-path $(CRATES_DIR)/vdso-time/ocalls
@echo "GEN <= $@"
$(C_OBJS):$(OBJ_DIR)/libos/$(SRC_OBJ)/%.o: src/%.c

265
src/libos/crates/errno/Cargo.lock generated Normal file

@ -0,0 +1,265 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "errno"
version = "0.1.0"
dependencies = [
"log",
"rcore-fs",
"serde_json",
"sgx_tstd 1.1.6",
]
[[package]]
name = "hashbrown_tstd"
version = "0.12.0"
[[package]]
name = "itoa"
version = "0.4.5"
dependencies = [
"sgx_tstd 1.1.0",
]
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "rcore-fs"
version = "0.1.0"
dependencies = [
"bitflags",
"spin",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "serde"
version = "1.0.104"
dependencies = [
"sgx_tstd 1.1.0",
]
[[package]]
name = "serde_json"
version = "1.0.40"
dependencies = [
"itoa",
"ryu",
"serde",
"sgx_tstd 1.1.0",
]
[[package]]
name = "sgx_alloc"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
[[package]]
name = "sgx_alloc"
version = "1.1.6"
[[package]]
name = "sgx_backtrace_sys"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"cc",
"sgx_build_helper 0.1.0",
"sgx_libc 1.1.0",
]
[[package]]
name = "sgx_backtrace_sys"
version = "1.1.6"
dependencies = [
"cc",
"sgx_build_helper 1.1.6",
"sgx_libc 1.1.6",
]
[[package]]
name = "sgx_build_helper"
version = "0.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
[[package]]
name = "sgx_build_helper"
version = "1.1.6"
[[package]]
name = "sgx_demangle"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
[[package]]
name = "sgx_demangle"
version = "1.1.6"
[[package]]
name = "sgx_libc"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_types 1.1.0",
]
[[package]]
name = "sgx_libc"
version = "1.1.6"
dependencies = [
"sgx_types 1.1.6",
]
[[package]]
name = "sgx_tcrypto"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_types 1.1.0",
]
[[package]]
name = "sgx_tprotected_fs"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_trts 1.1.0",
"sgx_types 1.1.0",
]
[[package]]
name = "sgx_tprotected_fs"
version = "1.1.6"
dependencies = [
"sgx_trts 1.1.6",
"sgx_types 1.1.6",
]
[[package]]
name = "sgx_trts"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_libc 1.1.0",
"sgx_types 1.1.0",
]
[[package]]
name = "sgx_trts"
version = "1.1.6"
dependencies = [
"sgx_libc 1.1.6",
"sgx_types 1.1.6",
]
[[package]]
name = "sgx_tse"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_types 1.1.0",
]
[[package]]
name = "sgx_tseal"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_tcrypto",
"sgx_trts 1.1.0",
"sgx_tse",
"sgx_types 1.1.0",
]
[[package]]
name = "sgx_tstd"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_alloc 1.1.0",
"sgx_backtrace_sys 1.1.0",
"sgx_demangle 1.1.0",
"sgx_libc 1.1.0",
"sgx_tprotected_fs 1.1.0",
"sgx_trts 1.1.0",
"sgx_tseal",
"sgx_types 1.1.0",
"sgx_unwind 0.1.0",
]
[[package]]
name = "sgx_tstd"
version = "1.1.6"
dependencies = [
"hashbrown_tstd",
"sgx_alloc 1.1.6",
"sgx_backtrace_sys 1.1.6",
"sgx_demangle 1.1.6",
"sgx_libc 1.1.6",
"sgx_tprotected_fs 1.1.6",
"sgx_trts 1.1.6",
"sgx_types 1.1.6",
"sgx_unwind 1.1.6",
]
[[package]]
name = "sgx_types"
version = "1.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
[[package]]
name = "sgx_types"
version = "1.1.6"
[[package]]
name = "sgx_unwind"
version = "0.1.0"
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
dependencies = [
"sgx_build_helper 0.1.0",
]
[[package]]
name = "sgx_unwind"
version = "1.1.6"
dependencies = [
"sgx_build_helper 1.1.6",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"

@ -0,0 +1,19 @@
[package]
name = "errno"
version = "0.1.0"
authors = ["Tate, Hongliang Tian <tate.thl@antgroup.com>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = []
std = []
occlum = ["sgx", "serde_json", "rcore-fs"]
sgx = ["sgx_tstd"]
[dependencies]
log = "0.4"
serde_json = { path = "../../../../deps/serde-json-sgx", optional = true }
sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true }
rcore-fs = { path = "../../../../deps/sefs/rcore-fs", optional = true }

@ -1,4 +1,9 @@
use super::*;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use super::Error;
#[derive(Debug, Clone)]
pub struct ErrorBacktrace<'a> {
@ -15,7 +20,7 @@ impl<'a> ErrorBacktrace<'a> {
impl<'a> fmt::Display for ErrorBacktrace<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let error_strings: Vec<String> = self.clone().map(|e| e.to_string()).collect();
let error_strings: Vec<String> = self.clone().map(|e| alloc::format!("{}", e)).collect();
let error_backtrace = error_strings.join("\n Caused by ");
write!(f, "{}", error_backtrace)
}
@ -48,18 +53,3 @@ impl Error {
ErrorBacktrace::new(self)
}
}
pub trait ResultExt<T> {
fn cause_err<F>(self, f: F) -> Result<T>
where
F: FnOnce(&Error) -> Error;
}
impl<T> ResultExt<T> for Result<T> {
fn cause_err<F>(self, f: F) -> Result<T>
where
F: FnOnce(&Error) -> Error,
{
self.map_err(|old_e| old_e.cause_err(f))
}
}

@ -1,4 +1,4 @@
use super::*;
use core::fmt;
/// POSIX errno
#[derive(Clone, Copy, Debug, PartialEq)]
@ -46,7 +46,6 @@ pub enum Errno {
ENOSYS = 38,
ENOTEMPTY = 39,
ELOOP = 40,
EWOULDBLOCK = 41,
ENOMSG = 42,
EIDRM = 43,
ECHRNG = 44,
@ -145,6 +144,9 @@ const ERRNO_MIN: u32 = Errno::EPERM as u32;
const ERRNO_MAX: u32 = Errno::EHWPOISON as u32;
impl Errno {
// EWOULDBLOCK was used on BSD/Sun variants of Unix, and EAGAIN was the AT&T System V error code.
// Here we keep same with linux, define EWOULDBLOCK with EAGAIN.
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
pub(crate) fn as_str(&self) -> &'static str {
use self::Errno::*;
match *self {

@ -1,4 +1,7 @@
use super::*;
use alloc::boxed::Box;
use core::fmt;
use super::{Errno, ToErrno};
#[derive(Debug)]
pub struct Error {
@ -10,7 +13,7 @@ pub struct Error {
#[derive(Debug)]
enum Error__ {
Embedded((Errno, &'static str)),
Boxed(Box<dyn ToErrno + 'static>),
Boxed(Box<dyn ToErrno + Send + 'static>),
}
#[derive(Debug, Clone, Copy)]
@ -30,7 +33,7 @@ impl Error {
pub fn boxed<T>(inner: T, location: Option<ErrorLocation>) -> Error
where
T: ToErrno + 'static,
T: ToErrno + Send + 'static,
{
Error {
inner: Error__::Boxed(Box::new(inner)),
@ -64,23 +67,6 @@ impl ErrorLocation {
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
self.errno().as_str()
}
fn cause(&self) -> Option<&dyn std::error::Error> {
self.cause.as_ref().map(|e| e as &dyn std::error::Error)
}
/*
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.cause
.as_ref()
.map(|e| e as &(dyn std::error::Error + 'static))
}
*/
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.inner)?;
@ -105,3 +91,25 @@ impl fmt::Display for ErrorLocation {
write!(f, "[line = {}, file = {}]", self.line, self.file)
}
}
#[cfg(any(feature = "std", feature = "sgx", test, doctest))]
mod if_std {
use super::*;
impl std::error::Error for Error {
fn description(&self) -> &str {
self.errno().as_str()
}
fn cause(&self) -> Option<&dyn std::error::Error> {
self.cause.as_ref().map(|e| e as &dyn std::error::Error)
}
/*
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.cause
.as_ref()
.map(|e| e as &(dyn std::error::Error + 'static))
}
*/
}
}

@ -0,0 +1,204 @@
//! User-friendly error handling with build-in support for POSIX errno.
//!
//! This crate extends Rust's standard error handling with the abilities of
//! reporting error locations, providing backtrace information, and unifying
//! all types of errors with POSIX errno.
//!
//! # Motivation
//!
//! While the built-in error handling mechanism of Rust is undoubtedly superior
//! than that of a traditional system programming language (e.g., C/C++), it
//! is _not perfect_.
//!
//! First, trait `std::error::Error` does not provide any means to
//! record the location of the source code that triggers an error, leading to
//! a slow process of diagnosing errors or bugs.
//!
//! Second, while the `Error` trait (which has a `cause` method)
//! supports backtrace in theory, it is inconvenient---in practice---to implement
//! backtrace. This is because the users still need to manually write the concrete
//! implementation that stores the cause for every error struct.
//!
//! Third, one challenging aspect of error handling in Rust is
//! dealing with the various types of errors. The standard library
//! defines errors like `std::io::Error`, `std::fmt::Error`, `std::str::Utf8Error`, etc.
//! Not to mention the error types defined by third-party libraries.
//! To make it even worse, we, as OS writers, have to convert all these errors
//! into POSIX errno eventually.
//!
//! To cope with the issues above, this crate extends Rust's standard error
//! handling mechanism. Specifically, it aims at the following design goals:
//!
//! * **Fast diagnose** (e.g., reporting the backtrace and the code location of an error).
//! * **First-class POSIX errno** (e.g., every error has an errno).
//! * **Zero-overhead abstraction** (e.g., no heap allocation unless absolutely necesary).
//! * **Ergonomic grammar** (e.g., use macros to avoid writing code manually).
//! * **Compatibility with `no_std`**.
//!
//! # How to Use
//!
//! ## Basic Usage
//!
//! The simplest usage involves just one macro---`errno!`.
//! See the sample code below:
//! ```rust
//! use errno::prelude::*;
//!
//! fn return_err() -> Result<()> {
//! Err(errno!(EINVAL, "the root error"))
//! }
//!
//! # fn main() {
//! if let Err(e) = return_err() {
//! println!("{}", e);
//! }
//! # }
//! ```
//! which prints something like
//! ```text
//! EINVAL (#22, Invalid argument): the root error [line = 45, file = src/lib.rs]
//! ```
//! Note that the specific line and file of source code that generates the error
//! is printed. This facilitates diagnosing errors.
//!
//! ## Backtrace
//!
//! A more interesting usage is to print the backtrace of an error. To create
//! the chains of errors, `std::result::Result` is extended with a new method
//! named `cause_err`. If the result is `Ok`, the method does nothing; otherwise,
//! this method executes a user-given closure to output a new error whose cause
//! is the error contained in the result. The method consumes the current result
//! and generates a new result that contains the new error. The two errors are
//! chained. More calls to `cause_err` form deeper backtraces.
//!
//! See the sample code below:
//! ```rust
//! use errno::prelude::*;
//!
//! fn return_err() -> Result<()> {
//! Err(errno!(EINVAL, "the root error"))
//! }
//!
//! fn cause_err() -> Result<()> {
//! return_err()
//! .cause_err(|_e| errno!(EIO, "another error"))
//! }
//!
//! # fn main() {
//! if let Err(e) = cause_err() {
//! println!("{}", e.backtrace());
//! }
//! # }
//! ```
//! which prints something like
//! ```text
//! EIO (#5, I/O error): another error [line = 71, file = src/lib.rs]
//! Caused by EINVAL (#22, Invalid argument): the root error [line = 68, file = src/lib.rs]
//! ```
//!
#![feature(allocator_api)]
// Use no_std and alloc crate except when given std feature or during test.
#![cfg_attr(not(any(feature = "std", test, doctest)), no_std)]
extern crate alloc;
// Use Rust SGX SDK's std when given SGX feature.
#[cfg(feature = "sgx")]
extern crate sgx_tstd as std;
#[macro_use]
extern crate log;
mod backtrace;
mod errno;
mod error;
pub mod prelude;
mod result;
mod to_errno;
pub use self::backtrace::ErrorBacktrace;
pub use self::errno::*;
pub use self::errno::Errno::*;
pub use self::error::{Error, ErrorLocation};
pub use self::result::{Result, ResultExt};
pub use self::to_errno::ToErrno;
#[macro_export]
macro_rules! errno {
($errno_expr: expr, $error_msg: expr) => {{
let inner_error = {
let errno: Errno = $errno_expr;
let msg: &'static str = $error_msg;
(errno, msg)
};
let error =
$crate::Error::embedded(inner_error, Some($crate::ErrorLocation::new(file!(), line!())));
error
}};
($error_expr: expr) => {{
let inner_error = $error_expr;
let error = $crate::Error::boxed(inner_error, Some($crate::ErrorLocation::new(file!(), line!())));
error
}};
}
#[macro_export]
macro_rules! return_errno {
($errno_expr: expr, $error_msg: expr) => {{
return Err(errno!($errno_expr, $error_msg));
}};
($error_expr: expr) => {{
return Err(errno!($error_expr));
}};
}
// return Err(errno) if libc return -1
#[macro_export]
macro_rules! try_libc {
($ret: expr) => {{
let ret = unsafe { $ret };
if ret < 0 {
let errno = unsafe { libc::errno() };
return_errno!(Errno::from(errno as u32), "libc error");
}
ret
}};
}
// return Err(errno) if libc return -1
// raise SIGPIPE if errno == EPIPE
#[macro_export]
macro_rules! try_libc_may_epipe {
($ret: expr) => {{
let ret = unsafe { $ret };
if ret < 0 {
let errno = unsafe { libc::errno() };
if errno == Errno::EPIPE as i32 {
crate::signal::do_tkill(current!().tid(), crate::signal::SIGPIPE.as_u8() as i32);
}
return_errno!(Errno::from(errno as u32), "libc error");
}
ret
}};
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn convert_std_io_error() -> Result<()> {
use std::io::{BufWriter, Write};
let mut buf_writer = BufWriter::new(Vec::<u8>::new());
// std::io::Error can be converted crate::Error implicitly
buf_writer.write("foo".as_bytes())?;
Ok(())
}
#[test]
fn convert_std_ffi_nul_error() -> Result<()> {
use std::ffi::CString;
// std::ffi::NulError can be converted crate::Error implicitly
let _ = CString::new(b"foo".to_vec())?;
Ok(())
}
}

@ -0,0 +1,3 @@
pub use crate::{
errno, return_errno, Errno, Errno::*, Error, ErrorLocation, Result, ResultExt, ToErrno,
};

@ -0,0 +1,34 @@
use super::{Errno, Error};
pub type Result<T> = core::result::Result<T, Error>;
/// Extending `Result` with extra functionalities.
pub trait ResultExt<T> {
fn cause_err<F>(self, f: F) -> Result<T>
where
F: FnOnce(&Error) -> Error;
fn errno(&self) -> Option<Errno>;
fn has_errno(&self, errno: Errno) -> bool;
}
impl<T> ResultExt<T> for Result<T> {
fn cause_err<F>(self, f: F) -> Result<T>
where
F: FnOnce(&Error) -> Error,
{
self.map_err(|old_e| old_e.cause_err(f))
}
fn errno(&self) -> Option<Errno> {
match self {
Ok(_) => None,
Err(e) => Some(e.errno()),
}
}
fn has_errno(&self, errno: Errno) -> bool {
self.errno() == Some(errno)
}
}

@ -0,0 +1,138 @@
use core::fmt;
use super::{Errno, Error};
pub trait ToErrno: fmt::Display + fmt::Debug {
fn errno(&self) -> Errno;
}
impl<T> From<T> for Error
where
T: ToErrno + Send + 'static,
{
fn from(t: T) -> Error {
Error::boxed(t, None)
}
}
impl ToErrno for Errno {
fn errno(&self) -> Errno {
*self
}
}
impl ToErrno for core::alloc::AllocError {
fn errno(&self) -> Errno {
Errno::ENOMEM
}
}
impl ToErrno for core::alloc::LayoutError {
fn errno(&self) -> Errno {
Errno::EINVAL
}
}
impl ToErrno for core::num::ParseIntError {
fn errno(&self) -> Errno {
Errno::EINVAL
}
}
#[cfg(any(feature = "std", feature = "sgx", test, doctest))]
mod if_std {
use super::*;
impl From<std::io::ErrorKind> for Errno {
fn from(kind: std::io::ErrorKind) -> Errno {
use std::io::ErrorKind::*;
use Errno::*;
match kind {
NotFound => ENOENT,
PermissionDenied => EPERM,
ConnectionRefused => ECONNREFUSED,
ConnectionReset => ECONNRESET,
ConnectionAborted => ECONNABORTED,
NotConnected => ENOTCONN,
AddrInUse => EADDRINUSE,
AddrNotAvailable => EADDRNOTAVAIL,
BrokenPipe => EPIPE,
AlreadyExists => EEXIST,
WouldBlock => Errno::EWOULDBLOCK,
InvalidInput => EINVAL,
InvalidData => EBADMSG, /* TODO: correct? */
TimedOut => ETIMEDOUT,
Interrupted => EINTR,
WriteZero => EINVAL,
UnexpectedEof => EIO,
Other => EIO,
_ => EIO,
}
}
}
impl ToErrno for std::io::Error {
fn errno(&self) -> Errno {
Errno::from(self.kind())
}
}
impl ToErrno for std::ffi::NulError {
fn errno(&self) -> Errno {
Errno::EINVAL
}
}
}
#[cfg(feature = "occlum")]
mod if_occlum {
use rcore_fs::dev::DevError;
use rcore_fs::vfs::FsError;
use super::*;
impl ToErrno for serde_json::Error {
fn errno(&self) -> Errno {
Errno::EINVAL
}
}
impl ToErrno for FsError {
fn errno(&self) -> Errno {
use Errno::*;
match *self {
FsError::NotSupported => ENOSYS,
FsError::NotFile => EISDIR,
FsError::IsDir => EISDIR,
FsError::NotDir => ENOTDIR,
FsError::EntryNotFound => ENOENT,
FsError::EntryExist => EEXIST,
FsError::NotSameFs => EXDEV,
FsError::InvalidParam => EINVAL,
FsError::NoDeviceSpace => ENOMEM,
FsError::DirRemoved => ENOENT,
FsError::DirNotEmpty => ENOTEMPTY,
FsError::WrongFs => EINVAL,
FsError::DeviceError(_err) => EIO,
FsError::SymLoop => ELOOP,
FsError::NoDevice => ENXIO,
FsError::IOCTLError => EINVAL,
FsError::Again => EAGAIN,
FsError::Busy => EBUSY,
FsError::WrProtected => EROFS,
FsError::NoIntegrity => EIO,
FsError::PermError => EPERM,
FsError::NameTooLong => ENAMETOOLONG,
FsError::FileTooBig => EFBIG,
FsError::OpNotSupported => EOPNOTSUPP,
FsError::NotMountPoint => EINVAL,
}
}
}
impl From<Error> for DevError {
fn from(e: Error) -> Self {
DevError(e.errno() as i32)
}
}
}

10
src/libos/crates/vdso-time/.gitignore vendored Normal file

@ -0,0 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

@ -0,0 +1,32 @@
[package]
name = "vdso-time"
version = "0.1.0"
authors = ["Shuocheng Wang <shuocheng.wsc@antgroup.com>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["std"]
std = ["libc"]
sgx = ["sgx_types", "sgx_tstd", "sgx_libc", "sgx_trts"]
[dependencies]
cfg-if = "1.0"
errno = { path = "../errno" }
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
libc = { version = "0.2", optional = true }
log = "0.4"
sgx_types = { path = "../../../../deps/rust-sgx-sdk/sgx_types", optional = true }
sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true, features = ["backtrace"] }
sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true }
sgx_trts = { path = "../../../../deps/rust-sgx-sdk/sgx_trts", optional = true }
[dev-dependencies]
criterion = "0.3"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
ctor = "0.1"
[[bench]]
name = "bench"
harness = false

@ -0,0 +1,40 @@
# vdso-time
A rust crate for getting time using vDSO. This crate can support host and SGX (based on Rust-SGX-SDK).
## Getting Started
Add the following dependency to your Cargo manifest:
```
vdso-time = { path = "yourpath/vdso-time" }
```
If you want to use in SGX environment, add the following dependency to your Cargo manifest:
```
vdso-time = { path = "yourpath/vdso-time", default-features = false, features = ["sgx"] }
```
## API examples
```
use vdso_time::ClockId;
let time = vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso_time::clock_gettime: {:?}", time);
let res = vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso_time::clock_getres: {:?}", res);
```
```
use vdso_time::{Vdso, ClockId};
let vdso = Vdso::new().unwrap();
let time = vdso.clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso.clock_gettime: {:?}", time);
let res = vdso.clock_getres(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso.clock_getres: {:?}", res);
}
```

@ -0,0 +1,42 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use vdso_time::ClockId;
fn libc_clock_gettime() -> libc::timespec {
let mut tp = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe {
libc::clock_gettime(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _);
}
tp
}
fn libc_clock_getres() -> libc::timespec {
let mut tp = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe {
libc::clock_getres(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _);
}
tp
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("libc clock_gettime", |b| {
b.iter(|| black_box(libc_clock_gettime()))
});
c.bench_function("vdso clock_gettime", |b| {
b.iter(|| black_box(vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap()))
});
c.bench_function("libc clock_getres", |b| {
b.iter(|| black_box(libc_clock_getres()))
});
c.bench_function("vdso clock_getres", |b| {
b.iter(|| black_box(vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap()))
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

@ -0,0 +1,33 @@
include!("common/bench.rs");
fn libc_clock_gettime() -> Duration {
let mut tp = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe {
libc::clock_gettime(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _);
}
Duration::new(tp.tv_sec as u64, tp.tv_nsec as u32)
}
fn libc_clock_getres() -> Duration {
let mut tp = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe {
libc::clock_getres(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _);
}
Duration::new(tp.tv_sec as u64, tp.tv_nsec as u32)
}
fn libc_benchmarks() {
benchmark("Libc clock_gettime()", libc_clock_gettime);
benchmark("Libc clock_getres()", libc_clock_getres);
}
fn main() {
libc_benchmarks();
vdso_benchmarks();
}

@ -0,0 +1,31 @@
use std::time::Duration;
use vdso_time::{ClockId, clock_getres, clock_gettime};
/// from criterion crate:
/// A function that is opaque to the optimizer, used to prevent the compiler from
/// optimizing away computations in a benchmark.
///
/// This variant is stable-compatible, but it may cause some performance overhead
/// or fail to prevent code from being eliminated.
fn black_box<T>(dummy: T) -> T {
unsafe {
let ret = std::ptr::read_volatile(&dummy);
std::mem::forget(dummy);
ret
}
}
fn benchmark(name: &str, func: impl Fn() -> Duration) {
let start = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
let loops = 1000000;
for _ in 0..loops {
black_box(func());
}
let end = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
println!("[{}] avg_time: {:?} ns", name, (end - start).as_nanos() / loops);
}
fn vdso_benchmarks() {
benchmark("vdso clock_gettime()", || clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap());
benchmark("vdso clock_getres()", || clock_getres(ClockId::CLOCK_MONOTONIC).unwrap());
}

@ -0,0 +1,26 @@
fn first_example() {
use vdso_time::ClockId;
let time = vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso_time::clock_gettime: {:?}", time);
let res = vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso_time::clock_getres: {:?}", res);
}
fn second_example() {
use vdso_time::{Vdso, ClockId};
let vdso = Vdso::new().unwrap();
let time = vdso.clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso.clock_gettime: {:?}", time);
let res = vdso.clock_getres(ClockId::CLOCK_MONOTONIC).unwrap();
println!("vdso.clock_getres: {:?}", res);
}
fn example() {
first_example();
second_example();
}

@ -0,0 +1,5 @@
include!("common/example.rs");
fn main() {
example();
}

@ -0,0 +1,11 @@
Cargo.lock
Enclave_u.c
Enclave_u.h
Enclave_t.c
Enclave_t.h
app/target
enclave/target
bin/app
*.o
*.a
*.so

@ -0,0 +1,167 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
######## SGX SDK Settings ########
SGX_SDK ?= /opt/sgxsdk
SGX_MODE ?= HW
SGX_ARCH ?= x64
include ../../../common.mk
include $(RUST_SGX_SDK_DIR)/buildenv.mk
OCALLS_DIR := ../../ocalls
ifeq ($(shell getconf LONG_BIT), 32)
SGX_ARCH := x86
else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32)
SGX_ARCH := x86
endif
ifeq ($(SGX_ARCH), x86)
SGX_COMMON_CFLAGS := -m32
SGX_LIBRARY_PATH := $(SGX_SDK)/lib
SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign
SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r
else
SGX_COMMON_CFLAGS := -m64
SGX_LIBRARY_PATH := $(SGX_SDK)/lib64
SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign
SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r
endif
ifeq ($(SGX_DEBUG), 1)
ifeq ($(SGX_PRERELEASE), 1)
$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!)
endif
endif
ifeq ($(SGX_DEBUG), 1)
SGX_COMMON_CFLAGS += -O0 -g
else
SGX_COMMON_CFLAGS += -O2
endif
SGX_COMMON_CFLAGS += -fstack-protector
######## CUSTOM Settings ########
CUSTOM_LIBRARY_PATH := ./lib
CUSTOM_BIN_PATH := ./bin
CUSTOM_EDL_PATH := $(RUST_SGX_SDK_DIR)/edl
CUSTOM_COMMON_PATH := $(RUST_SGX_SDK_DIR)/common
######## EDL Settings ########
Enclave_EDL_Files := enclave/Enclave_t.c enclave/Enclave_t.h app/Enclave_u.c app/Enclave_u.h
######## APP Settings ########
App_Rust_Flags := --release
App_SRC_Files := $(shell find app/ -type f -name '*.rs') $(shell find app/ -type f -name 'Cargo.toml')
App_Include_Paths := -I ./app -I./include -I$(SGX_SDK)/include -I$(CUSTOM_EDL_PATH)
App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(App_Include_Paths)
App_Rust_Path := ./app/target/release
App_Enclave_u_Object := lib/libEnclave_u.a
App_Ocall_Object_Name := libvdso_time_ocalls.a
App_Ocall_Object := lib/$(App_Ocall_Object_Name)
App_Name := bin/app
######## Enclave Settings ########
ifneq ($(SGX_MODE), HW)
Trts_Library_Name := sgx_trts_sim
Service_Library_Name := sgx_tservice_sim
else
Trts_Library_Name := sgx_trts
Service_Library_Name := sgx_tservice
endif
Crypto_Library_Name := sgx_tcrypto
KeyExchange_Library_Name := sgx_tkey_exchange
ProtectedFs_Library_Name := sgx_tprotected_fs
RustEnclave_C_Files := $(wildcard ./enclave/*.c)
RustEnclave_C_Objects := $(RustEnclave_C_Files:.c=.o)
RustEnclave_Include_Paths := -I$(CUSTOM_COMMON_PATH)/inc -I$(CUSTOM_EDL_PATH) -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/stlport -I$(SGX_SDK)/include/epid -I ./enclave -I./include
RustEnclave_Link_Libs := -L$(CUSTOM_LIBRARY_PATH) -lenclave
RustEnclave_Compile_Flags := $(SGX_COMMON_CFLAGS) $(ENCLAVE_CFLAGS) $(RustEnclave_Include_Paths)
RustEnclave_Link_Flags := -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \
-Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \
-Wl,--start-group -lsgx_tstdc -l$(Service_Library_Name) -l$(Crypto_Library_Name) $(RustEnclave_Link_Libs) -Wl,--end-group \
-Wl,--version-script=enclave/Enclave.lds \
$(ENCLAVE_LDFLAGS)
RustEnclave_Name := enclave/enclave.so
Signed_RustEnclave_Name := bin/enclave.signed.so
.PHONY: all
all: $(App_Name) $(Signed_RustEnclave_Name)
######## EDL Objects ########
$(Enclave_EDL_Files): $(SGX_EDGER8R) enclave/Enclave.edl
$(SGX_EDGER8R) --trusted enclave/Enclave.edl --search-path $(OCALLS_DIR) --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --trusted-dir enclave
$(SGX_EDGER8R) --untrusted enclave/Enclave.edl --search-path $(OCALLS_DIR) --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --untrusted-dir app
@echo "GEN => $(Enclave_EDL_Files)"
######## App Objects ########
app/Enclave_u.o: $(Enclave_EDL_Files)
@$(CC) $(App_C_Flags) -c app/Enclave_u.c -o $@
@echo "CC <= $<"
$(App_Enclave_u_Object): app/Enclave_u.o
$(AR) rcsD $@ $^
$(App_Ocall_Object):
@$(MAKE) -C $(OCALLS_DIR)
@cp $(OCALLS_DIR)/$(App_Ocall_Object_Name) $@
$(App_Name): $(App_Ocall_Object) $(App_Enclave_u_Object) $(App_SRC_Files)
@cd app && SGX_SDK=$(SGX_SDK) cargo build $(App_Rust_Flags)
@echo "Cargo => $@"
mkdir -p bin
cp $(App_Rust_Path)/app ./bin
######## Enclave Objects ########
enclave/Enclave_t.o: $(Enclave_EDL_Files)
@$(CC) $(RustEnclave_Compile_Flags) -c enclave/Enclave_t.c -o $@
@echo "CC <= $<"
$(RustEnclave_Name): enclave enclave/Enclave_t.o
@$(CXX) enclave/Enclave_t.o -o $@ $(RustEnclave_Link_Flags)
@echo "LINK => $@"
$(Signed_RustEnclave_Name): $(RustEnclave_Name)
mkdir -p bin
@$(SGX_ENCLAVE_SIGNER) sign -key enclave/Enclave_private.pem -enclave $(RustEnclave_Name) -out $@ -config enclave/Enclave.config.xml
@echo "SIGN => $@"
.PHONY: enclave
enclave:
$(MAKE) -C ./enclave/
.PHONY: clean
clean:
@rm -f $(App_Name) $(RustEnclave_Name) $(Signed_RustEnclave_Name) enclave/*_t.* app/*_u.* lib/*.a
@cd enclave && cargo clean && rm -f Cargo.lock
@cd app && cargo clean && rm -f Cargo.lock

@ -0,0 +1,11 @@
## example for SGX
This is an example of using vdso-time in SGX.
This example combines vdso-time example of io_uring and hello-rust example of incubator-teaclave-sgx-sdk.
- ./app : untrusted code
- ./bin : executable program
- ./enclave : trusted code
- ./lib : library
### run example in SGX
1. ```make```
2. ```cd bin && ./app```

@ -0,0 +1,10 @@
[package]
name = "app"
version = "1.0.0"
build = "build.rs"
[dependencies]
sgx_types = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_types" }
sgx_urts = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_urts" }
[workspace]

@ -0,0 +1,34 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License..
use std::env;
fn main() {
let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/sgxsdk".to_string());
let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string());
println!("cargo:rustc-link-search=native=../lib");
println!("cargo:rustc-link-lib=static=Enclave_u");
println!("cargo:rustc-link-lib=static=vdso_time_ocalls");
println!("cargo:rustc-link-search=native={}/lib64", sdk_dir);
match is_sim.as_ref() {
"SW" => println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"),
"HW" => println!("cargo:rustc-link-lib=dylib=sgx_urts"),
_ => println!("cargo:rustc-link-lib=dylib=sgx_urts"), // Treat undefined as HW
}
}

@ -0,0 +1 @@
nightly-2020-10-25

@ -0,0 +1,78 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License..
extern crate sgx_types;
extern crate sgx_urts;
use sgx_types::*;
use sgx_urts::SgxEnclave;
static ENCLAVE_FILE: &'static str = "enclave.signed.so";
extern "C" {
fn run_sgx_example(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t;
}
fn init_enclave() -> SgxResult<SgxEnclave> {
let mut launch_token: sgx_launch_token_t = [0; 1024];
let mut launch_token_updated: i32 = 0;
// call sgx_create_enclave to initialize an enclave instance
// Debug Support: set 2nd parameter to 1
let debug = 1;
let mut misc_attr = sgx_misc_attribute_t {
secs_attr: sgx_attributes_t { flags: 0, xfrm: 0 },
misc_select: 0,
};
SgxEnclave::create(
ENCLAVE_FILE,
debug,
&mut launch_token,
&mut launch_token_updated,
&mut misc_attr,
)
}
fn main() {
let enclave = match init_enclave() {
Ok(r) => {
println!("[+] Init Enclave Successful {}!", r.geteid());
r
}
Err(x) => {
println!("[-] Init Enclave Failed {}!", x.as_str());
return;
}
};
let mut retval = sgx_status_t::SGX_SUCCESS;
let result = unsafe { run_sgx_example(enclave.geteid(), &mut retval) };
match result {
sgx_status_t::SGX_SUCCESS => {}
_ => {
println!("[-] ECALL Enclave Failed {}!", result.as_str());
return;
}
}
match retval {
sgx_status_t::SGX_SUCCESS => {}
_ => {
println!("[-] ECALL Returned Error {}!", retval.as_str());
return;
}
}
println!("[+] run_sgx_example success...");
enclave.destroy();
}

@ -0,0 +1 @@
bin

@ -0,0 +1,22 @@
[package]
name = "Helloworldsampleenclave"
version = "1.0.0"
[lib]
name = "helloworldsampleenclave"
crate-type = ["staticlib"]
[features]
default = []
[dependencies]
vdso-time = { path = "../../../../vdso-time", default-features = false, features = ["sgx"] }
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
[target.'cfg(not(target_env = "sgx"))'.dependencies]
sgx_types = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_types" }
sgx_tstd = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace", "thread"] }
sgx_trts = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_trts" }
sgx_libc = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_libc" }
[workspace]

@ -0,0 +1,13 @@
<!-- Please refer to User's Guide for the explanation of each field -->
<EnclaveConfiguration>
<ProdID>0</ProdID>
<ISVSVN>0</ISVSVN>
<StackMaxSize>0x40000</StackMaxSize>
<HeapMaxSize>0x400000</HeapMaxSize>
<TCSNum>1</TCSNum>
<TCSMaxNum>1</TCSMaxNum>
<TCSPolicy>0</TCSPolicy>
<DisableDebug>0</DisableDebug>
<MiscSelect>0</MiscSelect>
<MiscMask>0xFFFFFFFF</MiscMask>
</EnclaveConfiguration>

@ -0,0 +1,36 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
enclave {
from "sgx_tstd.edl" import *;
from "sgx_stdio.edl" import *;
from "sgx_backtrace.edl" import *;
from "sgx_tstdc.edl" import *;
from "sgx_net.edl" import *;
from "sgx_thread.edl" import *;
from "sgx_vdso_time_ocalls.edl" import *;
trusted {
/* define ECALLs here. */
public sgx_status_t run_sgx_example();
};
untrusted {
/* define OCALLs here. */
};
};

@ -0,0 +1,9 @@
enclave.so
{
global:
g_global_data_sim;
g_global_data;
enclave_entry;
local:
*;
};

@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
MIIG4gIBAAKCAYEAroOogvsj/fZDZY8XFdkl6dJmky0lRvnWMmpeH41Bla6U1qLZ
AmZuyIF+mQC/cgojIsrBMzBxb1kKqzATF4+XwPwgKz7fmiddmHyYz2WDJfAjIveJ
ZjdMjM4+EytGlkkJ52T8V8ds0/L2qKexJ+NBLxkeQLfV8n1mIk7zX7jguwbCG1Pr
nEMdJ3Sew20vnje+RsngAzdPChoJpVsWi/K7cettX/tbnre1DL02GXc5qJoQYk7b
3zkmhz31TgFrd9VVtmUGyFXAysuSAb3EN+5VnHGr0xKkeg8utErea2FNtNIgua8H
ONfm9Eiyaav1SVKzPHlyqLtcdxH3I8Wg7yqMsaprZ1n5A1v/levxnL8+It02KseD
5HqV4rf/cImSlCt3lpRg8U5E1pyFQ2IVEC/XTDMiI3c+AR+w2jSRB3Bwn9zJtFlW
KHG3m1xGI4ck+Lci1JvWWLXQagQSPtZTsubxTQNx1gsgZhgv1JHVZMdbVlAbbRMC
1nSuJNl7KPAS/VfzAgEDAoIBgHRXxaynbVP5gkO0ug6Qw/E27wzIw4SmjsxG6Wpe
K7kfDeRskKxESdsA/xCrKkwGwhcx1iIgS5+Qscd1Yg+1D9X9asd/P7waPmWoZd+Z
AhlKwhdPsO7PiF3e1AzHhGQwsUTt/Y/aSI1MpHBvy2/s1h9mFCslOUxTmWw0oj/Q
ldIEgWeNR72CE2+jFIJIyml6ftnb6qzPiga8Bm48ubKh0kvySOqnkmnPzgh+JBD6
JnBmtZbfPT97bwTT+N6rnPqOOApvfHPf15kWI8yDbprG1l4OCUaIUH1AszxLd826
5IPM+8gINLRDP1MA6azECPjTyHXhtnSIBZCyWSVkc05vYmNXYUNiXWMajcxW9M02
wKzFELO8NCEAkaTPxwo4SCyIjUxiK1LbQ9h8PSy4c1+gGP4LAMR8xqP4QKg6zdu9
osUGG/xRe/uufgTBFkcjqBHtK5L5VI0jeNIUAgW/6iNbYXjBMJ0GfauLs+g1VsOm
WfdgXzsb9DYdMa0OXXHypmV4GwKBwQDUwQj8RKJ6c8cT4vcWCoJvJF00+RFL+P3i
Gx2DLERxRrDa8AVGfqaCjsR+3vLgG8V/py+z+dxZYSqeB80Qeo6PDITcRKoeAYh9
xlT3LJOS+k1cJcEmlbbO2IjLkTmzSwa80fWexKu8/Xv6vv15gpqYl1ngYoqJM3pd
vzmTIOi7MKSZ0WmEQavrZj8zK4endE3v0eAEeQ55j1GImbypSf7Idh7wOXtjZ7WD
Dg6yWDrri+AP/L3gClMj8wsAxMV4ZR8CgcEA0fzDHkFa6raVOxWnObmRoDhAtE0a
cjUj976NM5yyfdf2MrKy4/RhdTiPZ6b08/lBC/+xRfV3xKVGzacm6QjqjZrUpgHC
0LKiZaMtccCJjLtPwQd0jGQEnKfMFaPsnhOc5y8qVkCzVOSthY5qhz0XNotHHFmJ
gffVgB0iqrMTvSL7IA2yqqpOqNRlhaYhNl8TiFP3gIeMtVa9rZy31JPgT2uJ+kfo
gV7sdTPEjPWZd7OshGxWpT6QfVDj/T9T7L6tAoHBAI3WBf2DFvxNL2KXT2QHAZ9t
k3imC4f7U+wSE6zILaDZyzygA4RUbwG0gv8/TJVn2P/Eynf76DuWHGlaiLWnCbSz
Az2DHBQBBaku409zDQym3j1ugMRjzzSQWzJg0SIyBH3hTmnYcn3+Uqcp/lEBvGW6
O+rsXFt3pukqJmIV8HzLGGaLm62BHUeZf3dyWm+i3p/hQAL7Xvu04QW70xuGqdr5
afV7p5eaeQIJXyGQJ0eylV/90+qxjMKiB1XYg6WYvwKBwQCL/ddpgOdHJGN8uRom
e7Zq0Csi3hGheMKlKbN3vcxT5U7MdyHtTZZOJbTvxKNNUNYH/8uD+PqDGNneb29G
BfGzvI3EASyLIcGZF3OhKwZd0jUrWk2y7Vhob91jwp2+t73vdMbkKyI4mHOuXvGv
fg95si9oO7EBT+Oqvhccd2J+F1IVXncccYnF4u5ZGWt5lLewN/pVr7MjjykeaHqN
t+rfnQam2psA6fL4zS2zTmZPzR2tnY8Y1GBTi0Ko1OKd1HMCgcAb5cB/7/AQlhP9
yQa04PLH9ygQkKKptZp7dy5WcWRx0K/hAHRoi2aw1wZqfm7VBNu2SLcs90kCCCxp
6C5sfJi6b8NpNbIPC+sc9wsFr7pGo9SFzQ78UlcWYK2Gu2FxlMjonhka5hvo4zvg
WxlpXKEkaFt3gLd92m/dMqBrHfafH7VwOJY2zT3WIpjwuk0ZzmRg5p0pG/svVQEH
NZmwRwlopysbR69B/n1nefJ84UO50fLh5s5Zr3gBRwbWNZyzhXk=
-----END RSA PRIVATE KEY-----

@ -0,0 +1,38 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
Rust_Enclave_Name := libenclave.a
Rust_Enclave_Files := $(wildcard src/*.rs)
Rust_Target_Path := $(CURDIR)/../../../../../../deps/rust-sgx-sdk/xargo
ifeq ($(MITIGATION-CVE-2020-0551), LOAD)
export MITIGATION_CVE_2020_0551=LOAD
else ifeq ($(MITIGATION-CVE-2020-0551), CF)
export MITIGATION_CVE_2020_0551=CF
endif
.PHONY: all
all: $(Rust_Enclave_Name)
$(Rust_Enclave_Name): $(Rust_Enclave_Files)
ifeq ($(XARGO_SGX), 1)
RUST_TARGET_PATH=$(Rust_Target_Path) xargo build --target x86_64-unknown-linux-sgx --release
cp ./target/x86_64-unknown-linux-sgx/release/libhelloworldsampleenclave.a ../lib/libenclave.a
else
cargo build --release
cp ./target/release/libhelloworldsampleenclave.a ../lib/libenclave.a
endif

@ -0,0 +1 @@
nightly-2020-10-25

@ -0,0 +1,49 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License..
#![crate_name = "helloworldsampleenclave"]
#![crate_type = "staticlib"]
#![cfg_attr(not(target_env = "sgx"), no_std)]
#![cfg_attr(target_env = "sgx", feature(rustc_private))]
extern crate sgx_trts;
extern crate sgx_types;
#[cfg(not(target_env = "sgx"))]
#[macro_use]
extern crate sgx_tstd as std;
extern crate sgx_libc as libc;
extern crate vdso_time;
extern crate lazy_static;
use sgx_types::*;
use std::prelude::v1::*;
include!("../../../common/example.rs");
include!("../../../common/bench.rs");
#[no_mangle]
pub extern "C" fn run_sgx_example() -> sgx_status_t {
// std::backtrace::enable_backtrace("enclave.signed.so", std::backtrace::PrintFormat::Full);
println!("[ECALL] run_sgx_example");
example();
vdso_benchmarks();
sgx_status_t::SGX_SUCCESS
}

@ -0,0 +1,31 @@
{
"arch": "x86_64",
"cpu": "x86-64",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"dynamic-linking": true,
"env": "sgx",
"exe-allocation-crate": "alloc_system",
"executables": true,
"has-elf-tls": true,
"has-rpath": true,
"linker-flavor": "gcc",
"linker-is-gnu": true,
"llvm-target": "x86_64-unknown-linux-gnu",
"max-atomic-width": 64,
"os": "linux",
"position-independent-executables": true,
"pre-link-args": {
"gcc": [
"-Wl,--as-needed",
"-Wl,-z,noexecstack",
"-m64"
]
},
"relro-level": "full",
"stack-probes": true,
"target-c-int-width": "32",
"target-endian": "little",
"target-family": "unix",
"target-pointer-width": "64",
"vendor": "mesalock"
}

@ -0,0 +1 @@
lib

@ -0,0 +1,3 @@
*.o
*.a
*.so

@ -0,0 +1,12 @@
all: libs
libs: libvdso_time_ocalls.a
libvdso_time_ocalls.a: vdso_time_ocalls.o
ar rcs $@ $^
vdso_time_ocalls.o: vdso-time-ocalls.c
gcc -O3 -c -o $@ $<
clean:
rm -f *.o *.a

@ -0,0 +1,14 @@
enclave {
include "time.h"
untrusted {
int vdso_ocall_get_vdso_info(
[out] unsigned long* vdso_addr,
[out, size = release_len] char* release,
int release_len
);
int vdso_ocall_clock_gettime(int clockid, [out] struct timespec* ts);
int vdso_ocall_clock_getres(int clockid, [out] struct timespec* res);
};
};

@ -0,0 +1,30 @@
#include <sys/auxv.h>
#include <sys/utsname.h>
#include <time.h>
#include <string.h>
int vdso_ocall_get_vdso_info(
unsigned long *vdso_addr,
char *release,
int release_len) {
// If AT_SYSINFO_EHDR isn't found, getauxval will return 0.
*vdso_addr = getauxval(AT_SYSINFO_EHDR);
struct utsname buf;
int ret = uname(&buf);
// uname should always succeed here, since uname only fails when buf is not invalid.
if (ret != 0) { return -1; }
strncpy(release, buf.release, release_len);
release[release_len - 1] = '\0';
return 0;
}
int vdso_ocall_clock_gettime(int clockid, struct timespec *tp) {
return clock_gettime(clockid, tp);
}
int vdso_ocall_clock_getres(int clockid, struct timespec *res) {
return clock_getres(clockid, res);
}

@ -0,0 +1,616 @@
#![cfg_attr(feature = "sgx", no_std)]
#[cfg(feature = "sgx")]
extern crate sgx_types;
#[cfg(feature = "sgx")]
#[macro_use]
extern crate sgx_tstd as std;
#[cfg(feature = "sgx")]
extern crate sgx_libc as libc;
#[cfg(feature = "sgx")]
extern crate sgx_trts;
mod sys;
use errno::prelude::*;
use lazy_static::lazy_static;
use log::trace;
use std::convert::TryFrom;
use std::time::Duration;
use std::{hint, str};
use sys::*;
pub const NANOS_PER_SEC: u32 = 1_000_000_000;
pub const NANOS_PER_MILLI: u32 = 1_000_000;
pub const NANOS_PER_MICRO: u32 = 1_000;
pub const MILLIS_PER_SEC: u64 = 1_000;
pub const MICROS_PER_SEC: u64 = 1_000_000;
/// Clocks supported by the linux kernel, corresponding to clockid_t in Linux.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum ClockId {
CLOCK_REALTIME = 0,
CLOCK_MONOTONIC = 1,
// vDSO doesn't support CLOCK_PROCESS_CPUTIME_ID.
CLOCK_PROCESS_CPUTIME_ID = 2,
// vDSO doesn't support CLOCK_THREAD_CPUTIME_ID.
CLOCK_THREAD_CPUTIME_ID = 3,
CLOCK_MONOTONIC_RAW = 4,
CLOCK_REALTIME_COARSE = 5,
CLOCK_MONOTONIC_COARSE = 6,
CLOCK_BOOTTIME = 7,
}
impl TryFrom<i32> for ClockId {
type Error = Error;
fn try_from(clockid: i32) -> Result<Self> {
Ok(match clockid {
0 => ClockId::CLOCK_REALTIME,
1 => ClockId::CLOCK_MONOTONIC,
2 => ClockId::CLOCK_PROCESS_CPUTIME_ID,
3 => ClockId::CLOCK_THREAD_CPUTIME_ID,
4 => ClockId::CLOCK_MONOTONIC_RAW,
5 => ClockId::CLOCK_REALTIME_COARSE,
6 => ClockId::CLOCK_MONOTONIC_COARSE,
7 => ClockId::CLOCK_BOOTTIME,
_ => return_errno!(EINVAL, "Unsupported clockid"),
})
}
}
/// An abstraction of Linux vDSO provides the clock and time interface through Linux vDSO.
pub struct Vdso {
vdso_data_ptr: VdsoDataPtr,
// hres resolution for clock_getres
hres_resolution: Option<Duration>,
// coarse resolution for clock_getres
coarse_resolution: Option<Duration>,
}
impl Vdso {
/// Try to create a new Vdso by libc or SGX OCALL.
///
/// # Examples
///
/// ```
/// use vdso_time::Vdso;
/// let vdso = Vdso::new().unwrap();
/// ```
pub fn new() -> Result<Self> {
let vdso_data_ptr = Self::get_vdso_data_ptr_from_host()?;
let hres_resolution = clock_getres_slow(ClockId::CLOCK_MONOTONIC).ok();
let coarse_resolution = clock_getres_slow(ClockId::CLOCK_MONOTONIC_COARSE).ok();
let vdso = Self {
vdso_data_ptr,
hres_resolution,
coarse_resolution,
};
vdso.check_accuracy()?;
Ok(vdso)
}
#[cfg(feature = "sgx")]
fn get_vdso_data_ptr_from_host() -> Result<VdsoDataPtr> {
extern "C" {
fn vdso_ocall_get_vdso_info(
ret: *mut libc::c_int,
vdso_addr: *mut libc::c_ulong,
release: *mut libc::c_char,
release_len: libc::c_int,
) -> sgx_types::sgx_status_t;
}
let mut vdso_addr: libc::c_ulong = 0;
let mut release = [0 as libc::c_char; 65];
let mut ret: libc::c_int = 0;
unsafe {
vdso_ocall_get_vdso_info(
&mut ret as *mut _,
&mut vdso_addr as *mut _,
release.as_mut_ptr(),
release.len() as _,
);
}
if ret != 0 {
return_errno!(EINVAL, "Vdso vdso_ocall_get_vdso_info() failed")
}
Self::match_kernel_version(vdso_addr, &release)
}
#[cfg(not(feature = "sgx"))]
fn get_vdso_data_ptr_from_host() -> Result<VdsoDataPtr> {
const AT_SYSINFO_EHDR: u64 = 33;
let vdso_addr = unsafe { libc::getauxval(AT_SYSINFO_EHDR) };
let mut utsname: libc::utsname = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::uname(&mut utsname as *mut _) };
if ret != 0 {
return_errno!(EINVAL, "Vdso get utsname failed");
}
let release = utsname.release;
Self::match_kernel_version(vdso_addr, &release)
}
fn check_vdso_addr(vdso_addr: &u64) -> Result<()> {
let vdso_addr = *vdso_addr;
if vdso_addr == 0 {
return_errno!(EFAULT, "Vdso vdso_addr is 0")
}
const VDSO_DATA_MAX_SIZE: u64 = 4 * PAGE_SIZE;
if vdso_addr < VDSO_DATA_MAX_SIZE {
return_errno!(EFAULT, "Vdso vdso_addr is less than vdso data size");
}
#[cfg(feature = "sgx")]
if !sgx_trts::trts::rsgx_raw_is_outside_enclave(
(vdso_addr - VDSO_DATA_MAX_SIZE) as *const u8,
VDSO_DATA_MAX_SIZE as _,
) {
return_errno!(EFAULT, "Vdso vdso_addr we got is not outside enclave")
}
Ok(())
}
fn match_kernel_version(vdso_addr: u64, release: &[libc::c_char]) -> Result<VdsoDataPtr> {
Self::check_vdso_addr(&vdso_addr)?;
// release, e.g., "5.9.6-050906-generic"
let release = unsafe { &*(release as *const [i8] as *const [u8]) };
let release = str::from_utf8(release);
if release.is_err() {
return_errno!(EINVAL, "Vdso get kernel release failed")
}
let mut release = release.unwrap().split(&['-', '.', ' '][..]);
let version_big: u8 = release
.next()
.ok_or(errno!(EINVAL, "Vdso get kernel big version failed"))?
.parse()?;
let version_little: u8 = release
.next()
.ok_or(errno!(EINVAL, "Vdso get kernel little version failed"))?
.parse()?;
Ok(match (version_big, version_little) {
(4, 0..=4) | (4, 7..=11) => VdsoDataPtr::V4_0(vdso_data_v4_0::vdsodata_ptr(vdso_addr)),
(4, 5..=6) | (4, 12..=19) => VdsoDataPtr::V4_5(vdso_data_v4_5::vdsodata_ptr(vdso_addr)),
(5, 0..=2) => VdsoDataPtr::V5_0(vdso_data_v5_0::vdsodata_ptr(vdso_addr)),
(5, 3..=5) => VdsoDataPtr::V5_3(vdso_data_v5_3::vdsodata_ptr(vdso_addr)),
(5, 6..=8) => VdsoDataPtr::V5_6(vdso_data_v5_6::vdsodata_ptr(vdso_addr)),
(5, 9..=19) | (6, 0..=2) => VdsoDataPtr::V5_9(vdso_data_v5_9::vdsodata_ptr(vdso_addr)),
(_, _) => return_errno!(EINVAL, "Vdso match kernel release failed"),
})
}
/// Compare the results of Linux syscall and vdso to check whether vdso can support the clockid correctly.
fn check_accuracy(&self) -> Result<()> {
let vdso_supported_clockids = [
ClockId::CLOCK_REALTIME,
ClockId::CLOCK_MONOTONIC,
ClockId::CLOCK_MONOTONIC_RAW,
ClockId::CLOCK_REALTIME_COARSE,
ClockId::CLOCK_MONOTONIC_COARSE,
ClockId::CLOCK_BOOTTIME,
];
const MAX_INACCURACY: Duration = Duration::from_millis(1);
const MAX_RETRY_NUM: u32 = 3;
for &clockid in vdso_supported_clockids.iter() {
for retry_num in 0..MAX_RETRY_NUM {
let time = match self.do_clock_gettime(clockid) {
Ok(time) => time,
Err(_) => break,
};
let host_time = match clock_gettime_slow(clockid) {
Ok(host_time) => host_time,
Err(_) => break,
};
let estimated_inaccuracy = match host_time.checked_sub(time) {
Some(diff) => diff,
None => return_errno!(EOPNOTSUPP, "Vdso can not provide valid time"),
};
if estimated_inaccuracy > MAX_INACCURACY {
if retry_num == MAX_RETRY_NUM - 1 {
return_errno!(EOPNOTSUPP, "Vdso reached max retry number");
}
continue;
}
break;
}
}
Ok(())
}
/// Try to get time according to ClockId.
/// Firstly try to get time through vDSO, if failed, then try fallback.
///
/// # Examples
///
/// ```
/// use vdso_time::{Vdso, ClockId};
/// let vdso = Vdso::new().unwrap();
/// let time = vdso.clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
/// println!("{:?}", time);
/// ```
pub fn clock_gettime(&self, clockid: ClockId) -> Result<Duration> {
self.do_clock_gettime(clockid)
.or_else(|_| clock_gettime_slow(clockid))
}
/// Try to get time resolution according to ClockId.
/// Firstly try to return resolution inside self, if failed, then try fallback.
///
/// # Examples
///
/// ```
/// use vdso_time::{Vdso, ClockId};
/// let vdso = Vdso::new().unwrap();
/// let res = vdso.clock_getres(ClockId::CLOCK_MONOTONIC).unwrap();
/// println!("{:?}", res);
/// ```
pub fn clock_getres(&self, clockid: ClockId) -> Result<Duration> {
self.do_clock_getres(clockid)
.or_else(|_| clock_getres_slow(clockid))
}
fn do_clock_gettime(&self, clockid: ClockId) -> Result<Duration> {
match clockid {
ClockId::CLOCK_REALTIME | ClockId::CLOCK_MONOTONIC | ClockId::CLOCK_BOOTTIME => {
self.do_hres(ClockSource::CS_HRES_COARSE, clockid)
}
ClockId::CLOCK_MONOTONIC_RAW => self.do_hres(ClockSource::CS_RAW, clockid),
ClockId::CLOCK_REALTIME_COARSE | ClockId::CLOCK_MONOTONIC_COARSE => {
self.do_coarse(ClockSource::CS_HRES_COARSE, clockid)
}
// TODO: support CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID.
_ => return_errno!(EINVAL, "Unsupported clockid in do_clock_gettime()"),
}
}
fn do_clock_getres(&self, clockid: ClockId) -> Result<Duration> {
match clockid {
ClockId::CLOCK_REALTIME
| ClockId::CLOCK_MONOTONIC
| ClockId::CLOCK_BOOTTIME
| ClockId::CLOCK_MONOTONIC_RAW => self
.hres_resolution
.ok_or(errno!(EOPNOTSUPP, "hres_resolution is none")),
ClockId::CLOCK_REALTIME_COARSE | ClockId::CLOCK_MONOTONIC_COARSE => self
.coarse_resolution
.ok_or(errno!(EOPNOTSUPP, "coarse_resolution is none")),
// TODO: support CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID.
_ => return_errno!(EINVAL, "Unsupported clockid in do_clock_getres()"),
}
}
fn vdso_data(&self, cs: ClockSource) -> &'static dyn VdsoData {
match self.vdso_data_ptr {
VdsoDataPtr::V4_0(ptr) => unsafe { &*(ptr) },
VdsoDataPtr::V4_5(ptr) => unsafe { &*(ptr) },
VdsoDataPtr::V5_0(ptr) => unsafe { &*(ptr) },
VdsoDataPtr::V5_3(ptr) => unsafe { &*(ptr.add(cs as _)) },
VdsoDataPtr::V5_6(ptr) => unsafe { &*(ptr.add(cs as _)) },
VdsoDataPtr::V5_9(ptr) => unsafe { &*(ptr.add(cs as _)) },
}
}
fn do_hres(&self, cs: ClockSource, clockid: ClockId) -> Result<Duration> {
let vdso_data = self.vdso_data(cs);
loop {
let seq = vdso_data.seq();
// if seq is odd, it might means that a concurrent update is in progress.
// Hence, we do some instructions to spin waiting for seq to become even again.
if seq & 1 != 0 {
hint::spin_loop();
continue;
}
// Make sure that all prior load-from-memory instructions have completed locally,
// and no later instruction begins execution until LFENCE completes.
// We want to make sure the execution order as followning:
// seq -> [cycles, cycle_last, mult, shift, sec, secs] -> seq
// This LFENCE can ensure that the first seq is before [cycles, cycle_last, mult, shift, sec, secs]
lfence();
// Get hardware counter according to vdso_data's clock_mode.
let cycles = Self::get_hw_counter(vdso_data)?;
let cycle_last = vdso_data.cycle_last();
let mult = vdso_data.mult();
let shift = vdso_data.shift();
let secs = vdso_data.sec(clockid as _)?;
let mut nanos = vdso_data.nsec(clockid as _)?;
if !Self::vdso_read_retry(vdso_data, seq) {
// On x86 arch, the TSC can be slightly off across sockets,
// which might cause cycles < cycle_last. Since they are u64 type,
// cycles - cycle_last will panic in this case.
// Hence we need to verify that cycles is greater than cycle_last.
// If not then just use cycle_last, which is the base time of the
// current conversion period.
// And the vdso mask is always u64_MAX on x86, we don't need use mask.
if cycles > cycle_last {
nanos += (cycles - cycle_last) * mult as u64
}
nanos = nanos >> shift;
return Ok(Duration::new(secs, nanos as u32));
}
}
}
fn do_coarse(&self, cs: ClockSource, clockid: ClockId) -> Result<Duration> {
let vdso_data = self.vdso_data(cs);
loop {
let seq = vdso_data.seq();
// see comments in do_hres
if seq & 1 != 0 {
hint::spin_loop();
continue;
}
// see comments in do_hres
lfence();
let secs = vdso_data.sec(clockid as _)?;
let nanos = vdso_data.nsec(clockid as _)?;
if !Self::vdso_read_retry(vdso_data, seq) {
return Ok(Duration::new(secs, nanos as u32));
}
}
}
fn vdso_read_retry(vdso_data: &dyn VdsoData, old_seq: u32) -> bool {
// Make sure that all prior load-from-memory instructions have completed locally,
// and no later instruction begins execution until LFENCE completes
lfence();
old_seq != vdso_data.seq()
}
fn get_hw_counter(vdso_data: &dyn VdsoData) -> Result<u64> {
let clock_mode = vdso_data.clock_mode();
if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_TSC as i32 {
return Ok(rdtsc_ordered());
} else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_PVCLOCK as i32 {
// TODO: support pvclock
return_errno!(
EOPNOTSUPP,
"VDSO_CLOCKMODE_PVCLOCK support is not implemented"
);
} else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_HVCLOCK as i32 {
// TODO: support hvclock
return_errno!(
EOPNOTSUPP,
"VDSO_CLOCKMODE_HVCLOCK support is not implemented"
);
} else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_TIMENS as i32 {
// TODO: support timens
return_errno!(
EOPNOTSUPP,
"VDSO_CLOCKMODE_TIMENS support is not implemented"
);
} else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_NONE as i32 {
// In x86 Linux, the clock_mode will never be VDSO_CLOCKMODE_NONE.
return_errno!(EINVAL, "The clock_mode must not be VDSO_CLOCKMODE_NONE");
}
return_errno!(EINVAL, "Unsupported clock_mode");
}
}
unsafe impl Sync for Vdso {}
unsafe impl Send for Vdso {}
lazy_static! {
static ref VDSO: Option<Vdso> = Vdso::new().ok();
}
/// Try to get time according to ClockId.
/// Firstly try to get time through vDSO, if failed, then try fallback.
///
/// # Examples
///
/// ```
/// use vdso_time::ClockId;
///
/// let time = vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
/// println!("{:?}", time);
/// ```
pub fn clock_gettime(clockid: ClockId) -> Result<Duration> {
if VDSO.is_none() {
clock_gettime_slow(clockid)
} else {
VDSO.as_ref().unwrap().clock_gettime(clockid)
}
}
/// Try to get time resolution according to ClockId.
/// Firstly try to get time through vDSO, if failed, then try fallback.
///
/// # Examples
///
/// ```
/// use vdso_time::ClockId;
///
/// let time = vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap();
/// println!("{:?}", time);
/// ```
pub fn clock_getres(clockid: ClockId) -> Result<Duration> {
if VDSO.is_none() {
clock_getres_slow(clockid)
} else {
VDSO.as_ref().unwrap().clock_getres(clockid)
}
}
pub fn clock_gettime_slow(clockid: ClockId) -> Result<Duration> {
let mut ts = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
cfg_if::cfg_if! {
if #[cfg(feature = "sgx")] {
extern "C" {
fn vdso_ocall_clock_gettime(
ret: *mut libc::c_int,
clockid: libc::c_int,
ts: *mut libc::timespec,
) -> sgx_types::sgx_status_t;
}
let mut ret: libc::c_int = 0;
unsafe {
vdso_ocall_clock_gettime(&mut ret as *mut _, clockid as _, &mut ts as *mut _);
}
} else {
let ret = unsafe { libc::clock_gettime(clockid as _, &mut ts as *mut _) };
}
}
if ret == 0 {
Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32))
} else {
return_errno!(EINVAL, "clock_gettime_slow failed")
}
}
pub fn clock_getres_slow(clockid: ClockId) -> Result<Duration> {
let mut res = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
cfg_if::cfg_if! {
if #[cfg(feature = "sgx")] {
extern "C" {
fn vdso_ocall_clock_getres(
ret: *mut libc::c_int,
clockid: libc::c_int,
res: *mut libc::timespec,
) -> sgx_types::sgx_status_t;
}
let mut ret: libc::c_int = 0;
unsafe {
vdso_ocall_clock_getres(&mut ret as *mut _, clockid as _, &mut res as *mut _);
}
} else {
let ret = unsafe { libc::clock_getres(clockid as _, &mut res as *mut _) };
}
}
if ret == 0 {
Ok(Duration::new(res.tv_sec as u64, res.tv_nsec as u32))
} else {
return_errno!(EINVAL, "clock_getres_slow failed")
}
}
// All unit tests
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
use std::thread;
const LOOPS: usize = 3;
const SLEEP_DURATION: u64 = 10;
const HRES_MAX_DIFF_NANOS: u64 = 50_000;
const COARSE_MAX_DIFF_NANOS: u64 = 4_000_000;
#[test]
fn test_clock_gettime() {
test_single_clock_gettime(ClockId::CLOCK_REALTIME_COARSE, COARSE_MAX_DIFF_NANOS);
test_single_clock_gettime(ClockId::CLOCK_MONOTONIC_COARSE, COARSE_MAX_DIFF_NANOS);
test_single_clock_gettime(ClockId::CLOCK_REALTIME, HRES_MAX_DIFF_NANOS);
test_single_clock_gettime(ClockId::CLOCK_MONOTONIC, HRES_MAX_DIFF_NANOS);
test_single_clock_gettime(ClockId::CLOCK_BOOTTIME, HRES_MAX_DIFF_NANOS);
test_single_clock_gettime(ClockId::CLOCK_MONOTONIC_RAW, HRES_MAX_DIFF_NANOS);
}
fn test_single_clock_gettime(clockid: ClockId, max_diff_nanos: u64) {
for _ in 0..LOOPS {
let mut libc_tp = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe { libc::clock_gettime(clockid as _, &mut libc_tp as *mut _) };
let libc_time = Duration::new(libc_tp.tv_sec as u64, libc_tp.tv_nsec as u32);
let vdso_time = clock_gettime(clockid).unwrap();
assert!(vdso_time - libc_time <= Duration::from_nanos(max_diff_nanos));
thread::sleep(Duration::from_millis(SLEEP_DURATION));
}
}
#[test]
fn test_clock_getres() {
test_single_clock_getres(ClockId::CLOCK_REALTIME_COARSE);
test_single_clock_getres(ClockId::CLOCK_MONOTONIC_COARSE);
test_single_clock_getres(ClockId::CLOCK_REALTIME);
test_single_clock_getres(ClockId::CLOCK_MONOTONIC);
test_single_clock_getres(ClockId::CLOCK_BOOTTIME);
test_single_clock_getres(ClockId::CLOCK_MONOTONIC_RAW);
}
fn test_single_clock_getres(clockid: ClockId) {
for _ in 0..LOOPS {
let mut libc_tp = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe { libc::clock_getres(clockid as _, &mut libc_tp as *mut _) };
let res = clock_getres(clockid).unwrap();
assert_eq!(res.as_secs(), libc_tp.tv_sec as u64);
assert_eq!(res.subsec_nanos(), libc_tp.tv_nsec as u32);
}
}
#[test]
fn test_monotonic() {
let mut last_now = Duration::new(0, 0);
for _ in 0..1_000_000 {
let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
assert!(now >= last_now);
last_now = now;
}
}
mod logger {
use log::{Level, LevelFilter, Metadata, Record};
#[ctor::ctor]
fn auto_init() {
log::set_logger(&LOGGER)
.map(|()| log::set_max_level(LevelFilter::Trace))
.expect("failed to init the logger");
}
static LOGGER: SimpleLogger = SimpleLogger;
struct SimpleLogger;
impl log::Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Trace
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("[{}] {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
}
}

@ -0,0 +1,595 @@
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
pub const PAGE_SIZE: u64 = 4096;
pub const CLOCK_TAI: usize = 11;
pub const VDSO_BASES: usize = CLOCK_TAI + 1;
#[cfg(not(any(arget_arch = "x86", target_arch = "x86_64")))]
compile_error!("Only support x86 or x86_64 architecture now.");
/// Reads the current value of the processors time-stamp counter.
///
/// The processor monotonically increments the time-stamp counter MSR every clock cycle
/// and resets it to 0 whenever the processor is reset.
///
/// The RDTSC instruction is not a serializing instruction. It does not necessarily
/// wait until all previous instructions have been executed before reading the counter.
/// Similarly, subsequent instructions may begin execution before the read operation is performed.
pub fn rdtsc() -> u64 {
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
unsafe { core::arch::x86_64::_rdtsc() as u64 }
} else if #[cfg(target_arch = "x86")] {
unsafe { core::arch::x86::_rdtsc() as u64 }
}
}
}
/// Reads the current value of the processors time-stamp counter.
///
/// The processor monotonically increments the time-stamp counter MSR every clock cycle
/// and resets it to 0 whenever the processor is reset.
/// The RDTSCP instruction waits until all previous instructions have been executed before
/// reading the counter. However, subsequent instructions may begin execution before
/// the read operation is performed.
#[allow(dead_code)]
pub fn rdtscp() -> u64 {
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
let mut aux: u32 = 0;
unsafe { core::arch::x86_64::__rdtscp(&mut aux) as u64 }
} else if #[cfg(target_arch = "x86")] {
let mut aux: u32 = 0;
unsafe { core::arch::x86::__rdtscp(&mut aux) as u64 }
}
}
}
/// Performs a serializing operation on all load-from-memory instructions
/// that were issued prior to this instruction.
///
/// Guarantees that every load instruction that precedes, in program order,
/// is globally visible before any load instruction which follows the fence in program order.
pub fn lfence() {
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
unsafe { core::arch::x86_64::_mm_lfence() }
} else if #[cfg(target_arch = "x86")] {
unsafe { core::arch::x86::_mm_lfence() }
}
}
}
/// Read the current TSC in program order.
///
/// The RDTSC instruction might not be ordered relative to memory access.
/// But an RDTSC immediately after an appropriate barrier appears to be ordered as a normal load.
/// Hence, we could use a barrier before RDTSC to get ordered TSC.
///
/// We also can just use RDTSCP, which is also ordered.
pub fn rdtsc_ordered() -> u64 {
lfence();
rdtsc()
}
/// The timers is divided in 3 sets (HRES, COARSE, RAW) since Linux v5.3.
/// CS_HRES_COARSE refers to the first two and CS_RAW to the third.
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum ClockSource {
CS_HRES_COARSE = 0,
CS_RAW = 1,
}
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum VdsoClockMode {
VDSO_CLOCKMODE_NONE = 0,
VDSO_CLOCKMODE_TSC = 1,
VDSO_CLOCKMODE_PVCLOCK = 2,
VDSO_CLOCKMODE_HVCLOCK = 3,
VDSO_CLOCKMODE_TIMENS = i32::MAX as isize,
}
// Struct VdsoDataPtr must impl this trait to unify vdso_data interface of different linux verisons.
pub trait VdsoData {
fn sec(&self, clockid: ClockId) -> Result<u64>;
fn nsec(&self, clockid: ClockId) -> Result<u64>;
fn seq(&self) -> u32;
fn clock_mode(&self) -> i32;
fn cycle_last(&self) -> u64;
fn mask(&self) -> u64;
fn mult(&self) -> u32;
fn shift(&self) -> u32;
fn tz_minuteswest(&self) -> i32;
fn tz_dsttime(&self) -> i32;
fn vdsodata_ptr(vdso_addr: u64) -> *const Self
where
Self: Sized;
}
pub enum VdsoDataPtr {
// === Linux 4.0 - 4.4, 4.7 - 4.11 ===
V4_0(*const vdso_data_v4_0),
// === Linux 4.5 - 4.6, 4.12 - 4.19 ===
V4_5(*const vdso_data_v4_5),
// === Linux 5.0 - 5.2 ===
V5_0(*const vdso_data_v5_0),
// === Linux 5.3 - 5.5 ===
V5_3(*const vdso_data_v5_3),
// === Linux 5.6 - 5.8 ===
V5_6(*const vdso_data_v5_6),
// === Linux 5.9 - 6.2 ===
V5_9(*const vdso_data_v5_9),
}
// === Linux 4.0 - 4.4, 4.7 - 4.11 ===
// struct vsyscall_gtod_data
#[repr(C)]
pub struct vdso_data_v4_0 {
pub seq: AtomicU32,
pub vclock_mode: i32,
pub cycle_last: u64,
pub mask: u64,
pub mult: u32,
pub shift: u32,
pub wall_time_snsec: u64,
pub wall_time_sec: u64,
pub monotonic_time_sec: u64,
pub monotonic_time_snsec: u64,
pub wall_time_coarse_sec: u64,
pub wall_time_coarse_nsec: u64,
pub monotonic_time_coarse_sec: u64,
pub monotonic_time_coarse_nsec: u64,
pub tz_minuteswest: i32,
pub tz_dsttime: i32,
}
impl VdsoData for vdso_data_v4_0 {
fn vdsodata_ptr(vdso_addr: u64) -> *const Self {
(vdso_addr - 2 * PAGE_SIZE + 128) as *const Self
}
fn sec(&self, clockid: ClockId) -> Result<u64> {
match clockid {
ClockId::CLOCK_REALTIME => Ok(self.wall_time_sec),
ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_sec),
ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_sec),
ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_sec),
_ => return_errno!(EINVAL, "Unsupported clockid in sec()"),
}
}
fn nsec(&self, clockid: ClockId) -> Result<u64> {
match clockid {
ClockId::CLOCK_REALTIME => Ok(self.wall_time_snsec),
ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_snsec),
ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_nsec),
ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_nsec),
_ => return_errno!(EINVAL, "Unsupported clockid in nsec()"),
}
}
fn seq(&self) -> u32 {
self.seq.load(Ordering::Relaxed)
}
fn clock_mode(&self) -> i32 {
self.vclock_mode
}
fn cycle_last(&self) -> u64 {
self.cycle_last
}
fn mask(&self) -> u64 {
self.mask
}
fn mult(&self) -> u32 {
self.mult
}
fn shift(&self) -> u32 {
self.shift
}
fn tz_minuteswest(&self) -> i32 {
self.tz_minuteswest
}
fn tz_dsttime(&self) -> i32 {
self.tz_dsttime
}
}
// === Linux 4.5 - 4.6, 4.12 - 4.19 ===
// struct vsyscall_gtod_data
#[repr(C)]
pub struct vdso_data_v4_5 {
pub seq: AtomicU32,
pub vclock_mode: i32,
pub cycle_last: u64,
pub mask: u64,
pub mult: u32,
pub shift: u32,
pub wall_time_snsec: u64,
pub wall_time_sec: u64,
pub monotonic_time_sec: u64,
pub monotonic_time_snsec: u64,
pub wall_time_coarse_sec: u64,
pub wall_time_coarse_nsec: u64,
pub monotonic_time_coarse_sec: u64,
pub monotonic_time_coarse_nsec: u64,
pub tz_minuteswest: i32,
pub tz_dsttime: i32,
}
impl VdsoData for vdso_data_v4_5 {
fn vdsodata_ptr(vdso_addr: u64) -> *const Self {
(vdso_addr - 3 * PAGE_SIZE + 128) as *const Self
}
fn sec(&self, clockid: ClockId) -> Result<u64> {
match clockid {
ClockId::CLOCK_REALTIME => Ok(self.wall_time_sec),
ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_sec),
ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_sec),
ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_sec),
_ => return_errno!(EINVAL, "Unsupported clockid in sec()"),
}
}
fn nsec(&self, clockid: ClockId) -> Result<u64> {
match clockid {
ClockId::CLOCK_REALTIME => Ok(self.wall_time_snsec),
ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_snsec),
ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_nsec),
ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_nsec),
_ => return_errno!(EINVAL, "Unsupported clockid in nsec()"),
}
}
fn seq(&self) -> u32 {
self.seq.load(Ordering::Relaxed)
}
fn clock_mode(&self) -> i32 {
self.vclock_mode
}
fn cycle_last(&self) -> u64 {
self.cycle_last
}
fn mask(&self) -> u64 {
self.mask
}
fn mult(&self) -> u32 {
self.mult
}
fn shift(&self) -> u32 {
self.shift
}
fn tz_minuteswest(&self) -> i32 {
self.tz_minuteswest
}
fn tz_dsttime(&self) -> i32 {
self.tz_dsttime
}
}
// === Linux 5.0 - 5.2 ===
// struct vsyscall_gtod_data
#[repr(C)]
pub struct vdso_data_v5_0 {
pub seq: AtomicU32,
pub vclock_mode: i32,
pub cycle_last: u64,
pub mask: u64,
pub mult: u32,
pub shift: u32,
pub basetime: [vgtod_ts; VDSO_BASES],
pub tz_minuteswest: i32,
pub tz_dsttime: i32,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct vgtod_ts {
pub sec: u64,
pub nsec: u64,
}
impl VdsoData for vdso_data_v5_0 {
fn vdsodata_ptr(vdso_addr: u64) -> *const Self {
(vdso_addr - 3 * PAGE_SIZE + 128) as *const Self
}
fn sec(&self, clockid: ClockId) -> Result<u64> {
Ok(self.basetime[clockid as usize].sec)
}
fn nsec(&self, clockid: ClockId) -> Result<u64> {
Ok(self.basetime[clockid as usize].nsec)
}
fn seq(&self) -> u32 {
self.seq.load(Ordering::Relaxed)
}
fn clock_mode(&self) -> i32 {
self.vclock_mode
}
fn cycle_last(&self) -> u64 {
self.cycle_last
}
fn mask(&self) -> u64 {
self.mask
}
fn mult(&self) -> u32 {
self.mult
}
fn shift(&self) -> u32 {
self.shift
}
fn tz_minuteswest(&self) -> i32 {
self.tz_minuteswest
}
fn tz_dsttime(&self) -> i32 {
self.tz_dsttime
}
}
// === Linux 5.3 - 5.5 ===
// struct vdso_data
#[repr(C)]
pub struct vdso_data_v5_3 {
pub seq: AtomicU32,
pub clock_mode: i32,
pub cycle_last: u64,
pub mask: u64,
pub mult: u32,
pub shift: u32,
pub basetime: [vdso_timestamp; VDSO_BASES],
pub tz_minuteswest: i32,
pub tz_dsttime: i32,
pub hrtimer_res: u32,
pub __unused: u32,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct vdso_timestamp {
pub sec: u64,
pub nsec: u64,
}
impl VdsoData for vdso_data_v5_3 {
fn vdsodata_ptr(vdso_addr: u64) -> *const Self {
(vdso_addr - 3 * PAGE_SIZE + 128) as *const Self
}
fn sec(&self, clockid: ClockId) -> Result<u64> {
Ok(self.basetime[clockid as usize].sec)
}
fn nsec(&self, clockid: ClockId) -> Result<u64> {
Ok(self.basetime[clockid as usize].nsec)
}
fn seq(&self) -> u32 {
self.seq.load(Ordering::Relaxed)
}
fn clock_mode(&self) -> i32 {
self.clock_mode
}
fn cycle_last(&self) -> u64 {
self.cycle_last
}
fn mask(&self) -> u64 {
self.mask
}
fn mult(&self) -> u32 {
self.mult
}
fn shift(&self) -> u32 {
self.shift
}
fn tz_minuteswest(&self) -> i32 {
self.tz_minuteswest
}
fn tz_dsttime(&self) -> i32 {
self.tz_dsttime
}
}
// === Linux 5.6 - 5.8 ===
// struct vdso_data
#[repr(C)]
pub struct vdso_data_v5_6 {
pub seq: AtomicU32,
pub clock_mode: i32,
pub cycle_last: u64,
pub mask: u64,
pub mult: u32,
pub shift: u32,
pub union_1: vdso_data_v5_6_union_1,
pub tz_minuteswest: i32,
pub tz_dsttime: i32,
pub hrtimer_res: u32,
pub __unused: u32,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct timens_offset {
pub sec: i64,
pub nsec: u64,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union vdso_data_v5_6_union_1 {
pub basetime: [vdso_timestamp; VDSO_BASES],
pub offset: [timens_offset; VDSO_BASES],
}
impl VdsoData for vdso_data_v5_6 {
fn vdsodata_ptr(vdso_addr: u64) -> *const Self {
(vdso_addr - 4 * PAGE_SIZE + 128) as *const Self
}
fn sec(&self, clockid: ClockId) -> Result<u64> {
unsafe { Ok(self.union_1.basetime[clockid as usize].sec) }
}
fn nsec(&self, clockid: ClockId) -> Result<u64> {
unsafe { Ok(self.union_1.basetime[clockid as usize].nsec) }
}
fn seq(&self) -> u32 {
self.seq.load(Ordering::Relaxed)
}
fn clock_mode(&self) -> i32 {
self.clock_mode
}
fn cycle_last(&self) -> u64 {
self.cycle_last
}
fn mask(&self) -> u64 {
self.mask
}
fn mult(&self) -> u32 {
self.mult
}
fn shift(&self) -> u32 {
self.shift
}
fn tz_minuteswest(&self) -> i32 {
self.tz_minuteswest
}
fn tz_dsttime(&self) -> i32 {
self.tz_dsttime
}
}
// === Linux 5.9 - 5.19, 6.0 - 6.2 ===
// struct vdso_data
#[repr(C)]
pub struct vdso_data_v5_9 {
pub seq: AtomicU32,
pub clock_mode: i32,
pub cycle_last: u64,
pub mask: u64,
pub mult: u32,
pub shift: u32,
pub union_1: vdso_data_v5_6_union_1,
pub tz_minuteswest: i32,
pub tz_dsttime: i32,
pub hrtimer_res: u32,
pub __unused: u32,
pub arch_data: arch_vdso_data,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct arch_vdso_data {}
impl VdsoData for vdso_data_v5_9 {
fn vdsodata_ptr(vdso_addr: u64) -> *const Self {
(vdso_addr - 4 * PAGE_SIZE + 128) as *const Self
}
fn sec(&self, clockid: ClockId) -> Result<u64> {
unsafe { Ok(self.union_1.basetime[clockid as usize].sec) }
}
fn nsec(&self, clockid: ClockId) -> Result<u64> {
unsafe { Ok(self.union_1.basetime[clockid as usize].nsec) }
}
fn seq(&self) -> u32 {
self.seq.load(Ordering::Relaxed)
}
fn clock_mode(&self) -> i32 {
self.clock_mode
}
fn cycle_last(&self) -> u64 {
self.cycle_last
}
fn mask(&self) -> u64 {
self.mask
}
fn mult(&self) -> u32 {
self.mult
}
fn shift(&self) -> u32 {
self.shift
}
fn tz_minuteswest(&self) -> i32 {
self.tz_minuteswest
}
fn tz_dsttime(&self) -> i32 {
self.tz_dsttime
}
}

@ -10,7 +10,7 @@ use crate::interrupt;
use crate::process::idle_reap_zombie_children;
use crate::process::{ProcessFilter, SpawnAttr};
use crate::signal::SigNum;
use crate::time::up_time::init;
use crate::time::init;
use crate::util::host_file_util::{host_file_buffer, parse_host_file, write_host_file, HostFile};
use crate::util::log::LevelFilter;
use crate::util::mem_util::from_untrusted::*;
@ -96,8 +96,8 @@ pub extern "C" fn occlum_ecall_init(
interrupt::init();
// Init boot up time stamp here.
time::up_time::init();
// Init vdso and boot up time stamp here.
time::init();
vm::init_user_space();

@ -1,69 +1 @@
use super::*;
use std::fmt;
mod backtrace;
mod errno;
mod error;
mod to_errno;
pub use self::backtrace::{ErrorBacktrace, ResultExt};
pub use self::errno::Errno;
pub use self::errno::Errno::*;
pub use self::error::{Error, ErrorLocation};
pub use self::to_errno::ToErrno;
pub type Result<T> = std::result::Result<T, Error>;
macro_rules! errno {
($errno_expr: expr, $error_msg: expr) => {{
let inner_error = {
let errno: Errno = $errno_expr;
let msg: &'static str = $error_msg;
(errno, msg)
};
let error = Error::embedded(inner_error, Some(ErrorLocation::new(file!(), line!())));
error
}};
($error_expr: expr) => {{
let inner_error = $error_expr;
let error = Error::boxed(inner_error, Some(ErrorLocation::new(file!(), line!())));
error
}};
}
macro_rules! return_errno {
($errno_expr: expr, $error_msg: expr) => {{
return Err(errno!($errno_expr, $error_msg));
}};
($error_expr: expr) => {{
return Err(errno!($error_expr));
}};
}
// return Err(errno) if libc return -1
macro_rules! try_libc {
($ret: expr) => {{
let ret = unsafe { $ret };
if ret < 0 {
let errno = unsafe { libc::errno() };
return_errno!(Errno::from(errno as u32), "libc error");
}
ret
}};
}
// return Err(errno) if libc return -1
// raise SIGPIPE if errno == EPIPE
macro_rules! try_libc_may_epipe {
($ret: expr) => {{
let ret = unsafe { $ret };
if ret < 0 {
let errno = unsafe { libc::errno() };
if errno == Errno::EPIPE as i32 {
crate::signal::do_tkill(current!().tid(), crate::signal::SIGPIPE.as_u8() as i32);
}
return_errno!(Errno::from(errno as u32), "libc error");
}
ret
}};
}
pub use errno::{Errno::*, *};

@ -1,116 +0,0 @@
use super::*;
pub trait ToErrno: fmt::Display + fmt::Debug {
fn errno(&self) -> Errno;
}
impl ToErrno for Errno {
fn errno(&self) -> Errno {
*self
}
}
impl<T> From<T> for Error
where
T: ToErrno + 'static,
{
fn from(t: T) -> Error {
Error::boxed(t, None)
}
}
impl From<std::io::ErrorKind> for Errno {
fn from(kind: std::io::ErrorKind) -> Errno {
use std::io::ErrorKind::*;
match kind {
NotFound => ENOENT,
PermissionDenied => EPERM,
ConnectionRefused => ECONNREFUSED,
ConnectionReset => ECONNRESET,
ConnectionAborted => ECONNABORTED,
NotConnected => ENOTCONN,
AddrInUse => EADDRINUSE,
AddrNotAvailable => EADDRNOTAVAIL,
BrokenPipe => EPIPE,
AlreadyExists => EEXIST,
WouldBlock => EWOULDBLOCK,
InvalidInput => EINVAL,
InvalidData => EBADMSG, /* TODO: correct? */
TimedOut => ETIMEDOUT,
Interrupted => EINTR,
WriteZero => EINVAL,
UnexpectedEof => EIO,
Other => EIO,
_ => EIO,
}
}
}
impl ToErrno for std::io::Error {
fn errno(&self) -> Errno {
Errno::from(self.kind())
}
}
impl ToErrno for std::ffi::NulError {
fn errno(&self) -> Errno {
EINVAL
}
}
impl ToErrno for std::num::ParseIntError {
fn errno(&self) -> Errno {
EINVAL
}
}
impl ToErrno for serde_json::Error {
fn errno(&self) -> Errno {
EINVAL
}
}
impl ToErrno for rcore_fs::vfs::FsError {
fn errno(&self) -> Errno {
use rcore_fs::vfs::FsError;
match *self {
FsError::NotSupported => ENOSYS,
FsError::NotFile => EISDIR,
FsError::IsDir => EISDIR,
FsError::NotDir => ENOTDIR,
FsError::EntryNotFound => ENOENT,
FsError::EntryExist => EEXIST,
FsError::NotSameFs => EXDEV,
FsError::InvalidParam => EINVAL,
FsError::NoDeviceSpace => ENOMEM,
FsError::DirRemoved => ENOENT,
FsError::DirNotEmpty => ENOTEMPTY,
FsError::WrongFs => EINVAL,
FsError::DeviceError(err) => EIO,
FsError::SymLoop => ELOOP,
FsError::NoDevice => ENXIO,
FsError::IOCTLError => EINVAL,
FsError::Again => EAGAIN,
FsError::Busy => EBUSY,
FsError::WrProtected => EROFS,
FsError::NoIntegrity => EIO,
FsError::PermError => EPERM,
FsError::NameTooLong => ENAMETOOLONG,
FsError::FileTooBig => EFBIG,
FsError::OpNotSupported => EOPNOTSUPP,
FsError::NotMountPoint => EINVAL,
}
}
}
impl ToErrno for std::alloc::AllocError {
fn errno(&self) -> Errno {
ENOMEM
}
}
impl ToErrno for std::alloc::LayoutError {
fn errno(&self) -> Errno {
EINVAL
}
}

@ -18,7 +18,7 @@ struct CpuIdInput {
#[repr(C)]
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
struct CpuIdResult {
pub struct CpuIdResult {
eax: u32,
ebx: u32,
ecx: u32,
@ -218,6 +218,15 @@ impl CpuId {
};
cpuid_result
}
pub fn support_sgx2(&self) -> bool {
const SGX_CPUID: u32 = 0x12;
let cpuid = self.get_cpuid_info(SGX_CPUID, 0);
// The 0th bit set to 1 in `cpuid.eax` indicates that the SGX feature is enabled.
// The 1st bit set to 1 in `cpuid.eax` indicates that the SGX2 feature is enabled.
(cpuid.eax & 0b11) == 0b11
}
}
lazy_static! {
@ -247,6 +256,14 @@ fn get_cpuid_info_via_ocall(cpuid_input: CpuIdInput) -> CpuIdResult {
cpuid_result
}
pub fn is_cpu_support_sgx2() -> bool {
CPUID.support_sgx2()
}
pub fn get_cpuid_info(leaf: u32, subleaf: u32) -> CpuIdResult {
CPUID.get_cpuid_info(leaf, subleaf)
}
pub fn setup_cpuid_info() {
// Make lazy_static to be executed at runtime in order to be initialized
let max_basic_leaf = CPUID.get_max_basic_leaf();

@ -11,6 +11,8 @@ use crate::vm::{enclave_page_fault_handler, is_page_committed, VMRange, USER_SPA
use sgx_types::*;
use sgx_types::{sgx_exception_type_t, sgx_exception_vector_t};
pub use self::cpuid::{get_cpuid_info, is_cpu_support_sgx2};
const ENCLU: u32 = 0xd7010f;
const EACCEPT: u32 = 0x5;
const EACCEPTCOPY: u32 = 0x7;

@ -1,4 +1,5 @@
use super::*;
use crate::util::kernel_alloc::KernelAlloc;
use crate::vm::USER_SPACE_VM_MANAGER;
pub struct MemInfoINode;
@ -15,13 +16,26 @@ impl ProcINode for MemInfoINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
let total_ram = USER_SPACE_VM_MANAGER.get_total_size();
let free_ram = USER_SPACE_VM_MANAGER.get_precise_free_size();
let kernel_heap_total = KernelAlloc::get_kernel_heap_config();
let kernel_heap_peak_used = KernelAlloc::get_kernel_heap_peak_used();
let kernel_heap_in_use = if let Some(bytes) = KernelAlloc::get_kernel_mem_size() {
format!("{} kB", bytes / KB)
} else {
"Feature not enabled".to_string()
};
Ok(format!(
"MemTotal: {} kB\n\
MemFree: {} kB\n\
MemAvailable: {} kB\n",
MemAvailable: {} kB\n\
KernelHeapTotal: {} kB\n\
KernelHeapPeakUsed: {} kB\n\
KernelHeapInUse: {}\n",
total_ram / KB,
free_ram / KB,
free_ram / KB,
kernel_heap_total / KB,
kernel_heap_peak_used / KB,
kernel_heap_in_use,
)
.into_bytes())
}

@ -321,10 +321,3 @@ impl File for LockedFile {
Ok(SefsMac(file.get_mac().unwrap()))
}
}
impl From<Error> for DevError {
fn from(e: Error) -> Self {
error!("SGX protected file I/O error: {}", e.backtrace());
DevError(e.errno() as i32)
}
}

@ -6,7 +6,7 @@ use super::file_ops::{
};
use super::fs_ops;
use super::fs_ops::{MountFlags, MountOptions, UmountFlags};
use super::time::{clockid_t, itimerspec_t, timespec_t, timeval_t, ClockID};
use super::time::{clockid_t, itimerspec_t, timespec_t, timeval_t, ClockId};
use super::timer_file::{TimerCreationFlags, TimerSetFlags};
use super::*;
use crate::config::{user_rootfs_config, ConfigApp, ConfigMountFsType};
@ -42,9 +42,9 @@ pub fn do_eventfd2(init_val: u32, flags: i32) -> Result<isize> {
pub fn do_timerfd_create(clockid: clockid_t, flags: i32) -> Result<isize> {
debug!("timerfd: clockid {}, flags {} ", clockid, flags);
let clockid = ClockID::from_raw(clockid)?;
let clockid = ClockId::try_from(clockid)?;
match clockid {
ClockID::CLOCK_REALTIME | ClockID::CLOCK_MONOTONIC => {}
ClockId::CLOCK_REALTIME | ClockId::CLOCK_MONOTONIC => {}
_ => {
return_errno!(EINVAL, "invalid clockid");
}

@ -1,6 +1,6 @@
use super::*;
use crate::time::{clockid_t, itimerspec_t, timespec_t, ClockID};
use crate::time::{clockid_t, itimerspec_t, timespec_t, ClockId};
use atomic::{Atomic, Ordering};
use std::time::Duration;
@ -13,7 +13,7 @@ pub struct TimerFile {
}
impl TimerFile {
pub fn new(clockid: ClockID, flags: TimerCreationFlags) -> Result<Self> {
pub fn new(clockid: ClockId, flags: TimerCreationFlags) -> Result<Self> {
let raw_host_fd = try_libc!({
let mut ret: i32 = 0;
let status = occlum_ocall_timerfd_create(&mut ret, clockid as clockid_t, flags.bits());

@ -66,6 +66,7 @@ extern crate intrusive_collections;
extern crate itertools;
extern crate modular_bitfield;
extern crate resolv_conf;
extern crate vdso_time;
use sgx_trts::libc;
use sgx_types::*;

@ -102,12 +102,12 @@ pub fn do_exec(
// Blocking wait until there is only one thread in the calling process
fn wait_for_other_threads_to_exit(current_ref: &ThreadRef) {
use super::do_futex::{self, FutexTimeout};
use crate::time::{timespec_t, ClockID};
use crate::time::{timespec_t, ClockId};
use core::time::Duration;
// Set timeout to 50ms
let timeout = FutexTimeout::new(
ClockID::CLOCK_MONOTONIC,
ClockId::CLOCK_MONOTONIC,
timespec_t::from(Duration::from_millis(50)),
false,
);

@ -4,7 +4,7 @@ use std::intrinsics::atomic_load_seqcst;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::prelude::*;
use crate::time::{timespec_t, ClockID};
use crate::time::{timespec_t, ClockId};
/// `FutexOp`, `FutexFlags`, and `futex_op_and_flags_from_u32` are helper types and
/// functions for handling the versatile commands and arguments of futex system
@ -75,13 +75,13 @@ const FUTEX_BITSET_MATCH_ANY: u32 = 0xFFFF_FFFF;
#[derive(Debug, Copy, Clone)]
pub struct FutexTimeout {
clock_id: ClockID,
clock_id: ClockId,
ts: timespec_t,
absolute_time: bool,
}
impl FutexTimeout {
pub fn new(clock_id: ClockID, ts: timespec_t, absolute_time: bool) -> Self {
pub fn new(clock_id: ClockId, ts: timespec_t, absolute_time: bool) -> Self {
Self {
clock_id,
ts,
@ -89,7 +89,7 @@ impl FutexTimeout {
}
}
pub fn clock_id(&self) -> &ClockID {
pub fn clock_id(&self) -> &ClockId {
&self.clock_id
}
@ -502,7 +502,7 @@ fn wait_event_timeout(thread: *const c_void, timeout: &Option<FutexTimeout>) ->
.as_ref()
.map(|timeout| {
let clockbit = match timeout.clock_id() {
ClockID::CLOCK_REALTIME => FutexFlags::FUTEX_CLOCK_REALTIME.bits() as i32,
ClockId::CLOCK_REALTIME => FutexFlags::FUTEX_CLOCK_REALTIME.bits() as i32,
_ => 0,
};
(

@ -11,7 +11,7 @@ use super::process::ProcessFilter;
use super::spawn_attribute::{clone_spawn_atrributes_safely, posix_spawnattr_t, SpawnAttr};
use crate::prelude::*;
use crate::syscall::CpuContext;
use crate::time::{timespec_t, ClockID};
use crate::time::{timespec_t, ClockId};
use crate::util::mem_util::from_user::*;
use std::ptr::NonNull;
@ -290,9 +290,9 @@ pub fn do_futex(
"FUTEX_CLOCK_REALTIME with this futex operation is not supported"
);
}
ClockID::CLOCK_REALTIME
ClockId::CLOCK_REALTIME
} else {
ClockID::CLOCK_MONOTONIC
ClockId::CLOCK_MONOTONIC
};
// From futex man page:

@ -932,7 +932,7 @@ fn do_gettimeofday(tv_u: *mut timeval_t) -> Result<isize> {
fn do_clock_gettime(clockid: clockid_t, ts_u: *mut timespec_t) -> Result<isize> {
check_mut_ptr(ts_u)?;
let clockid = time::ClockID::from_raw(clockid)?;
let clockid = time::ClockId::try_from(clockid)?;
let ts = time::do_clock_gettime(clockid)?;
unsafe {
*ts_u = ts;
@ -941,7 +941,7 @@ fn do_clock_gettime(clockid: clockid_t, ts_u: *mut timespec_t) -> Result<isize>
}
fn do_time(tloc_u: *mut time_t) -> Result<isize> {
let ts = time::do_clock_gettime(time::ClockID::CLOCK_REALTIME)?;
let ts = time::do_clock_gettime(time::ClockId::CLOCK_REALTIME)?;
if !tloc_u.is_null() {
check_mut_ptr(tloc_u)?;
unsafe {
@ -952,7 +952,7 @@ fn do_time(tloc_u: *mut time_t) -> Result<isize> {
}
fn do_clock_getres(clockid: clockid_t, res_u: *mut timespec_t) -> Result<isize> {
let clockid = time::ClockID::from_raw(clockid)?;
let clockid = time::ClockId::try_from(clockid)?;
if res_u.is_null() {
return Ok(0);
}
@ -981,7 +981,7 @@ fn do_clock_nanosleep(
} else {
None
};
let clockid = time::ClockID::from_raw(clockid)?;
let clockid = time::ClockId::try_from(clockid)?;
time::do_clock_nanosleep(clockid, flags, &req, rem)?;
Ok(0)
}

@ -1,9 +1,12 @@
use self::timer_slack::*;
use super::*;
use crate::exception::is_cpu_support_sgx2;
use core::convert::TryFrom;
use process::pid_t;
use rcore_fs::dev::TimeProvider;
use rcore_fs::vfs::Timespec;
use sgx_trts::enclave::{rsgx_get_enclave_mode, EnclaveMode};
use spin::Once;
use std::time::Duration;
use std::{fmt, u64};
use syscall::SyscallNum;
@ -14,6 +17,7 @@ pub mod up_time;
pub use profiler::ThreadProfiler;
pub use timer_slack::TIMERSLACK;
pub use vdso_time::ClockId;
#[allow(non_camel_case_types)]
pub type time_t = i64;
@ -27,6 +31,26 @@ pub type clock_t = i64;
/// Clock ticks per second
pub const SC_CLK_TCK: u64 = 100;
static IS_ENABLE_VDSO: Once<bool> = Once::new();
pub fn init() {
init_vdso();
up_time::init();
}
fn init_vdso() {
IS_ENABLE_VDSO.call_once(|| match rsgx_get_enclave_mode() {
EnclaveMode::Hw if is_cpu_support_sgx2() => true,
EnclaveMode::Sim => true,
_ => false,
});
}
#[inline(always)]
fn is_enable_vdso() -> bool {
IS_ENABLE_VDSO.get().map_or(false, |is_enable| *is_enable)
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
#[allow(non_camel_case_types)]
@ -74,15 +98,16 @@ impl From<Duration> for timeval_t {
}
pub fn do_gettimeofday() -> timeval_t {
extern "C" {
fn occlum_ocall_gettimeofday(tv: *mut timeval_t) -> sgx_status_t;
}
let duration = if is_enable_vdso() {
vdso_time::clock_gettime(ClockId::CLOCK_REALTIME).unwrap()
} else {
// SGX1 Hardware doesn't support rdtsc instruction
vdso_time::clock_gettime_slow(ClockId::CLOCK_REALTIME).unwrap()
};
let mut tv: timeval_t = Default::default();
unsafe {
occlum_ocall_gettimeofday(&mut tv as *mut timeval_t);
}
tv.validate().expect("ocall returned invalid timeval_t");
let tv = timeval_t::from(duration);
tv.validate()
.expect("gettimeofday returned invalid timeval_t");
tv
}
@ -149,58 +174,29 @@ impl timespec_t {
#[allow(non_camel_case_types)]
pub type clockid_t = i32;
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum ClockID {
CLOCK_REALTIME = 0,
CLOCK_MONOTONIC = 1,
CLOCK_PROCESS_CPUTIME_ID = 2,
CLOCK_THREAD_CPUTIME_ID = 3,
CLOCK_MONOTONIC_RAW = 4,
CLOCK_REALTIME_COARSE = 5,
CLOCK_MONOTONIC_COARSE = 6,
CLOCK_BOOTTIME = 7,
}
pub fn do_clock_gettime(clockid: ClockId) -> Result<timespec_t> {
let duration = if is_enable_vdso() {
vdso_time::clock_gettime(clockid).unwrap()
} else {
// SGX1 Hardware doesn't support rdtsc instruction
vdso_time::clock_gettime_slow(clockid).unwrap()
};
impl ClockID {
#[deny(unreachable_patterns)]
pub fn from_raw(clockid: clockid_t) -> Result<ClockID> {
Ok(match clockid as i32 {
0 => ClockID::CLOCK_REALTIME,
1 => ClockID::CLOCK_MONOTONIC,
2 => ClockID::CLOCK_PROCESS_CPUTIME_ID,
3 => ClockID::CLOCK_THREAD_CPUTIME_ID,
4 => ClockID::CLOCK_MONOTONIC_RAW,
5 => ClockID::CLOCK_REALTIME_COARSE,
6 => ClockID::CLOCK_MONOTONIC_COARSE,
7 => ClockID::CLOCK_BOOTTIME,
_ => return_errno!(EINVAL, "invalid command"),
})
}
}
pub fn do_clock_gettime(clockid: ClockID) -> Result<timespec_t> {
extern "C" {
fn occlum_ocall_clock_gettime(clockid: clockid_t, tp: *mut timespec_t) -> sgx_status_t;
}
let mut tv: timespec_t = Default::default();
unsafe {
occlum_ocall_clock_gettime(clockid as clockid_t, &mut tv as *mut timespec_t);
}
tv.validate().expect("ocall returned invalid timespec");
let tv = timespec_t::from(duration);
tv.validate()
.expect("clock_gettime returned invalid timespec");
Ok(tv)
}
pub fn do_clock_getres(clockid: ClockID) -> Result<timespec_t> {
extern "C" {
fn occlum_ocall_clock_getres(clockid: clockid_t, res: *mut timespec_t) -> sgx_status_t;
}
pub fn do_clock_getres(clockid: ClockId) -> Result<timespec_t> {
let duration = if is_enable_vdso() {
vdso_time::clock_getres(clockid).unwrap()
} else {
// SGX1 Hardware doesn't support rdtsc instruction
vdso_time::clock_getres_slow(clockid).unwrap()
};
let mut res: timespec_t = Default::default();
unsafe {
occlum_ocall_clock_getres(clockid as clockid_t, &mut res as *mut timespec_t);
}
let res = timespec_t::from(duration);
let validate_resolution = |res: &timespec_t| -> Result<()> {
// The resolution can be ranged from 1 nanosecond to a few milliseconds
if res.sec == 0 && res.nsec > 0 && res.nsec < 1_000_000_000 {
@ -210,14 +206,14 @@ pub fn do_clock_getres(clockid: ClockID) -> Result<timespec_t> {
}
};
// do sanity check
validate_resolution(&res).expect("ocall returned invalid resolution");
validate_resolution(&res).expect("clock_getres returned invalid resolution");
Ok(res)
}
const TIMER_ABSTIME: i32 = 0x01;
pub fn do_clock_nanosleep(
clockid: ClockID,
clockid: ClockId,
flags: i32,
req: &timespec_t,
rem: Option<&mut timespec_t>,
@ -235,11 +231,11 @@ pub fn do_clock_nanosleep(
let mut ret = 0;
let mut u_rem: timespec_t = timespec_t { sec: 0, nsec: 0 };
match clockid {
ClockID::CLOCK_REALTIME
| ClockID::CLOCK_MONOTONIC
| ClockID::CLOCK_BOOTTIME
| ClockID::CLOCK_PROCESS_CPUTIME_ID => {}
ClockID::CLOCK_THREAD_CPUTIME_ID => {
ClockId::CLOCK_REALTIME
| ClockId::CLOCK_MONOTONIC
| ClockId::CLOCK_BOOTTIME
| ClockId::CLOCK_PROCESS_CPUTIME_ID => {}
ClockId::CLOCK_THREAD_CPUTIME_ID => {
return_errno!(EINVAL, "CLOCK_THREAD_CPUTIME_ID is not a permitted value");
}
_ => {
@ -269,7 +265,7 @@ pub fn do_nanosleep(req: &timespec_t, rem: Option<&mut timespec_t>) -> Result<()
// the CLOCK_REALTIME clock. However, Linux measures the time using
// the CLOCK_MONOTONIC clock.
// Here we follow the POSIX.1
let clock_id = ClockID::CLOCK_REALTIME;
let clock_id = ClockId::CLOCK_REALTIME;
return do_clock_nanosleep(clock_id, 0, req, rem);
}

@ -1,16 +1,16 @@
use super::{do_clock_gettime, ClockID};
use super::{do_clock_gettime, ClockId};
use std::time::Duration;
lazy_static! {
static ref BOOT_TIME_STAMP: Duration = do_clock_gettime(ClockID::CLOCK_MONOTONIC_RAW)
static ref BOOT_TIME_STAMP: Duration = do_clock_gettime(ClockId::CLOCK_MONOTONIC_RAW)
.unwrap()
.as_duration();
static ref BOOT_TIME_STAMP_SINCE_EPOCH: Duration = do_clock_gettime(ClockID::CLOCK_REALTIME)
static ref BOOT_TIME_STAMP_SINCE_EPOCH: Duration = do_clock_gettime(ClockId::CLOCK_REALTIME)
.unwrap()
.as_duration();
}
pub fn init() {
pub(super) fn init() {
*BOOT_TIME_STAMP;
*BOOT_TIME_STAMP_SINCE_EPOCH;
}
@ -20,7 +20,7 @@ pub fn boot_time_since_epoch() -> Duration {
}
pub fn get() -> Option<Duration> {
do_clock_gettime(ClockID::CLOCK_MONOTONIC_RAW)
do_clock_gettime(ClockId::CLOCK_MONOTONIC_RAW)
.unwrap()
.as_duration()
.checked_sub(*BOOT_TIME_STAMP)

@ -0,0 +1,81 @@
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};
// This file provides "KernelAlloc", a wrapper for sgx_std "System" allocator, is used as
// the global allocator for Occlum kernel. Currently, this can provides the ability to
// monitor the kernel heap usage.
pub struct KernelAlloc {
size: AtomicUsize,
}
impl KernelAlloc {
pub fn get_kernel_mem_size() -> Option<usize> {
cfg_if! {
if #[cfg(feature = "kernel_heap_monitor")] {
Some(ALLOC.size.load(Ordering::Relaxed))
} else {
None
}
}
}
pub fn get_kernel_heap_config() -> usize {
std::enclave::get_heap_size()
}
pub fn get_kernel_heap_peak_used() -> usize {
sgx_trts::enclave::rsgx_get_peak_heap_used()
}
}
unsafe impl GlobalAlloc for KernelAlloc {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = System.alloc(layout);
if !ptr.is_null() {
self.size.fetch_add(layout.size(), Ordering::Relaxed);
}
ptr
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let ptr = System.alloc_zeroed(layout);
if !ptr.is_null() {
self.size.fetch_add(layout.size(), Ordering::Relaxed);
}
ptr
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout);
self.size.fetch_sub(layout.size(), Ordering::Relaxed);
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let ptr = System.realloc(ptr, layout, new_size);
if !ptr.is_null() {
let old_size = layout.size();
if new_size > old_size {
// grow
self.size.fetch_add(new_size - old_size, Ordering::Relaxed);
} else if new_size < old_size {
// shrink
self.size.fetch_sub(old_size - new_size, Ordering::Relaxed);
}
}
ptr
}
}
#[cfg(feature = "kernel_heap_monitor")]
#[global_allocator]
static ALLOC: KernelAlloc = KernelAlloc {
size: AtomicUsize::new(0),
};

@ -3,6 +3,7 @@ use super::*;
pub mod dirty;
pub mod host_file_util;
pub mod hosts_parser_util;
pub mod kernel_alloc;
pub mod log;
pub mod mem_util;
pub mod mpx_util;

@ -21,6 +21,8 @@ C_SRCS := $(sort $(wildcard src/*.c src/*/*.c))
CXX_SRCS := $(sort $(wildcard src/*.cpp src/*/*.cpp))
C_OBJS := $(addprefix $(OBJ_DIR)/pal/,$(C_SRCS:.c=.o))
CXX_OBJS := $(addprefix $(OBJ_DIR)/pal/,$(CXX_SRCS:.cpp=.o))
VDSO_SRCS := $(CRATES_DIR)/vdso-time/ocalls/vdso-time-ocalls.c
VDSO_OBJS := $(addprefix $(OBJ_DIR)/pal/external_ocalls,$(VDSO_SRCS:.c=.o))
# Object files for simulation mode are stored in pal/src_sim
ifeq ($(SGX_MODE), SIM)
@ -52,7 +54,7 @@ LINK_FLAGS += -lsgx_quote_ex_sim
endif
endif
ALL_BUILD_SUBDIRS := $(sort $(patsubst %/,%,$(dir $(LIBOCCLUM_PAL_SO_REAL) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS))))
ALL_BUILD_SUBDIRS := $(sort $(patsubst %/,%,$(dir $(LIBOCCLUM_PAL_SO_REAL) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS) $(VDSO_OBJS))))
.PHONY: all format format-check clean
@ -61,7 +63,7 @@ all: $(ALL_BUILD_SUBDIRS) $(LIBOCCLUM_PAL_SO_REAL)
$(ALL_BUILD_SUBDIRS):
@mkdir -p $@
$(LIBOCCLUM_PAL_SO_REAL): $(LIBSGX_USTDC_A) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS)
$(LIBOCCLUM_PAL_SO_REAL): $(LIBSGX_USTDC_A) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS) $(VDSO_OBJS)
@$(CXX) $^ -o $@ $(LINK_FLAGS) -Wl,-soname=$(LIBOCCLUM_PAL_SONAME)
@# Create symbolic files because occlum run and exec will need it when linking.
@cd $(BUILD_DIR)/lib && ln -sf $(notdir $(LIBOCCLUM_PAL_SO_REAL)) $(notdir $(LIBOCCLUM_PAL_SONAME)) && \
@ -76,7 +78,8 @@ $(OBJ_DIR)/pal/$(SRC_OBJ)/Enclave_u.c: $(SGX_EDGER8R) ../Enclave.edl
@cd $(OBJ_DIR)/pal/$(SRC_OBJ) && \
$(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --untrusted $(CUR_DIR)/../Enclave.edl \
--search-path $(SGX_SDK)/include \
--search-path $(RUST_SGX_SDK_DIR)/edl/
--search-path $(RUST_SGX_SDK_DIR)/edl/ \
--search-path $(CRATES_DIR)/vdso-time/ocalls
@echo "GEN <= $@"
$(OBJ_DIR)/pal/$(SRC_OBJ)/%.o: src/%.c
@ -87,6 +90,10 @@ $(OBJ_DIR)/pal/$(SRC_OBJ)/%.o: src/%.cpp
@$(CXX) $(CXX_FLAGS) -c $< -o $@
@echo "CXX <= $@"
$(VDSO_OBJS): $(VDSO_SRCS)
@$(CC) $(C_FLAGS) -c $< -o $@
@echo "CC <= $@"
$(LIBSGX_USTDC_A):
@$(MAKE) --no-print-directory -C $(RUST_SGX_SDK_DIR)/sgx_ustdc/ > /dev/null
@cp $(RUST_SGX_SDK_DIR)/sgx_ustdc/libsgx_ustdc.a $(LIBSGX_USTDC_A)

@ -3,10 +3,10 @@
#ifndef _OCCLUM_VERSION_H_
#define _OCCLUM_VERSION_H_
// Version = 0.30.0
// Version = 0.30.1
#define OCCLUM_MAJOR_VERSION 0
#define OCCLUM_MINOR_VERSION 30
#define OCCLUM_PATCH_VERSION 0
#define OCCLUM_PATCH_VERSION 1
#define STRINGIZE_PRE(X) #X
#define STRINGIZE(X) STRINGIZE_PRE(X)

@ -4,18 +4,6 @@
#include <sys/prctl.h>
#include "ocalls.h"
void occlum_ocall_gettimeofday(struct timeval *tv) {
gettimeofday(tv, NULL);
}
void occlum_ocall_clock_gettime(int clockid, struct timespec *tp) {
clock_gettime(clockid, tp);
}
void occlum_ocall_clock_getres(int clockid, struct timespec *res) {
clock_getres(clockid, res);
}
int occlum_ocall_clock_nanosleep(clockid_t clockid, int flags, const struct timespec *req,
struct timespec *rem) {
return clock_nanosleep(clockid, flags, req, rem);

@ -2,6 +2,7 @@ MAIN_MAKEFILE := $(firstword $(MAKEFILE_LIST))
INCLUDE_MAKEFILE := $(lastword $(MAKEFILE_LIST))
CUR_DIR := $(shell dirname $(realpath $(MAIN_MAKEFILE)))
PROJECT_DIR := $(realpath $(CUR_DIR)/../../)
CRATES_DIR := $(realpath $(CUR_DIR)/../libos/crates)
SHELL := /bin/bash

@ -2,9 +2,12 @@
SRC_DIR=/tmp/glibc/glibc
BUILD_DIR=/tmp/glibc/glibc_build
INSTALL_DIR=/opt/occlum/glibc
GLIBC_BRANCH=${1:-"occlum-glibc-2.31"}
# GCC 9/10 introduces many new checkings and will cause the build to fail.
if [ "$(gcc -dumpversion)" = "9" -o "$(gcc -dumpversion)" = "10" ]; then
if [ "$(gcc -dumpversion)" = "9" ] || \
[ "$(gcc -dumpversion)" = "10" ] || \
[ "$(gcc -dumpversion)" = "11" ]; then
EXTRA_CFLAGS=-fcommon
EXTRA_CONFIG_OPTION="--disable-werror"
fi
@ -20,7 +23,7 @@ rm -rf ${INSTALL_DIR}
mkdir -p ${SRC_DIR}
cd ${SRC_DIR}
# Download glibc
git clone -b occlum-glibc-2.31 https://github.com/occlum/glibc .
git clone -b ${GLIBC_BRANCH} https://github.com/occlum/glibc .
mkdir -p ${BUILD_DIR}
cd ${BUILD_DIR}