You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@teaclave.apache.org by sh...@apache.org on 2022/11/11 05:17:51 UTC

[incubator-teaclave-java-tee-sdk] 11/48: [Enc] Add native image level framework

This is an automated email from the ASF dual-hosted git repository.

shaojunwang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-java-tee-sdk.git

commit edc95be328bb832a63c72c47c0365f6dbfaa6b5c
Author: cengfeng.lzy <ce...@alibaba-inc.com>
AuthorDate: Wed Mar 16 18:01:13 2022 +0800

    [Enc] Add native image level framework
    
    Summary: Create native image level framework that provides the following
    functions:
      - Library invocation entrypointss that follow the SVM protocals
      - Data structure definition
      - Data serialization and converting from WordBase to Object base
      - Service state maintainance
    
    Test Plan: all tests pass
    
    Reviewers: lei.yul, jeffery.wsj, sanhong.lsh
    
    Issue: https://aone.alibaba-inc.com/task/40646790
    
    CR:
    https://code.aone.alibaba-inc.com/java-tee/JavaEnclave/codereview/8303453
---
 .gitignore                                         |  19 ++
 build.sh                                           |  20 +-
 .../common/annotations/EnclaveMethod.java          |  45 ++++
 .../common/annotations/EnclaveService.java         |  52 ++++
 sdk/enclave/pom.xml                                | 107 ++++++++-
 .../enclave/EnclaveEntry.java                      |  79 ++++++
 .../enclave/EnclaveFeature.java                    | 130 ++++++++++
 .../enclave/EnclavePrologue.java                   |  22 ++
 .../enclave/InvocationWrapper.java                 |  69 ++++++
 .../enclave/c/EnclaveEnvironment.java              | 126 ++++++++++
 .../META-INF/native-image/reflect-config.json      |  20 ++
 .../native-image/serialization-config.json         |  44 ++++
 .../src/main/resources/native/enc_environment.h    |  17 ++
 .../enclave/AroundNativeTest.java                  |  49 ++++
 .../enclave/EnclaveTestHelper.java                 | 107 +++++++++
 .../enclave/NativeImageTest.java                   | 264 +++++++++++++++++++++
 .../enclave/NativeImageTestable.java               |  12 +
 .../enclave/RunWithNativeImageTest.java            | 166 +++++++++++++
 .../enclave/SVMSimpleEnclaveCallTest.java          | 123 ++++++++++
 .../confidentialcomputing/enclave/TestTarget.java  |  12 +
 .../framework/ServiceMethodInvokerTest.java        |   8 +
 .../enclave/framework/ServiceOperationTest.java    |   8 +
 .../enclave/testservice/MathService.java           |   3 +
 .../enclave/testservice/Point.java                 |  11 +-
 ...nfidentialcomputing_enclave_EnclaveTestHelper.h |  53 +++++
 .../test/resources/native/enc_invoke_entry_test.c  |  76 ++++++
 tools/cicd/Dockerfile                              |   5 +-
 tools/cicd/make.sh                                 |   7 +-
 28 files changed, 1633 insertions(+), 21 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8e5dc50
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+
+# virtual machine crash logs
+hs_err_pid*
+
+# IDE config
+*.iml
+.idea/
+.classpath
+.project
+.settings/
+
+# Maven compiled directory
+target/
diff --git a/build.sh b/build.sh
index f9c39ec..b4dbae1 100644
--- a/build.sh
+++ b/build.sh
@@ -8,23 +8,19 @@ cd "${SHELL_FOLDER}"
 # workspace dir is the same as build.sh path location.
 WORKDIR="$PWD"
 
-# The necessary GraalVM jars are compiled from git@gitlab.alibaba-inc.com:graal/SGXGraalVM.git. When the patches are
-# accepted by the community, these jars will be gradually replaced by the official jars.
+# The necessary GraalVM jars are compiled from git@gitlab.alibaba-inc.com:graal/SGXGraalVM.git.
+# When the patches are accepted by the community, these jars will be gradually replaced by the official jars.
 VERSION="enclave-22.0.0"
 mkdir jartmp
 pushd jartmp > /dev/null
-wget https://graal.oss-cn-beijing.aliyuncs.com/graal-enclave/JDK11-22.0.0/graal-processor-22.0.0.jar
 wget https://graal.oss-cn-beijing.aliyuncs.com/graal-enclave/JDK11-22.0.0/graal-sdk-enclave-22.0.0.jar
-wget https://graal.oss-cn-beijing.aliyuncs.com/graal-enclave/JDK11-22.0.0/native-image-base-enclave-22.0.0.jar
-wget https://graal.oss-cn-beijing.aliyuncs.com/graal-enclave/JDK11-22.0.0/objectfile-enclave-22.0.0.jar
-wget https://graal.oss-cn-beijing.aliyuncs.com/graal-enclave/JDK11-22.0.0/pointsto-enclave-22.0.0.jar
-wget https://graal.oss-cn-beijing.aliyuncs.com/graal-enclave/JDK11-22.0.0/svm-enclave-22.0.0.jar
-mvn install:install-file -Dfile=graal-processor-22.0.0.jar -DgroupId=org.graalvm.compiler -DartifactId=graal-processor -Dversion=$VERSION -Dpackaging=jar
+
+mvn install:install-file -Dfile=$GRAALVM_HOME/lib/graal/graal-processor.jar -DgroupId=org.graalvm.compiler -DartifactId=graal-processor -Dversion=$VERSION -Dpackaging=jar
 mvn install:install-file -Dfile=graal-sdk-enclave-22.0.0.jar -DgroupId=org.graalvm.sdk -DartifactId=graal-sdk -Dversion=$VERSION -Dpackaging=jar
-mvn install:install-file -Dfile=svm-enclave-22.0.0.jar -DgroupId=org.graalvm.nativeimage -DartifactId=svm -Dversion=$VERSION -Dpackaging=jar
-mvn install:install-file -Dfile=objectfile-enclave-22.0.0.jar -DgroupId=org.graalvm.nativeimage -DartifactId=objectfile -Dversion=$VERSION -Dpackaging=jar
-mvn install:install-file -Dfile=pointsto-enclave-22.0.0.jar -DgroupId=org.graalvm.nativeimage -DartifactId=pointsto -Dversion=$VERSION -Dpackaging=jar
-mvn install:install-file -Dfile=native-image-base-enclave-22.0.0.jar -DgroupId=org.graalvm.nativeimage -DartifactId=native-image-base -Dversion=$VERSION -Dpackaging=jar
+mvn install:install-file -Dfile=$GRAALVM_HOME/lib/svm/builder/svm.jar -DgroupId=org.graalvm.nativeimage -DartifactId=svm -Dversion=$VERSION -Dpackaging=jar
+mvn install:install-file -Dfile=$GRAALVM_HOME/lib/svm/builder/objectfile.jar -DgroupId=org.graalvm.nativeimage -DartifactId=objectfile -Dversion=$VERSION -Dpackaging=jar
+mvn install:install-file -Dfile=$GRAALVM_HOME/lib/svm/builder/pointsto.jar -DgroupId=org.graalvm.nativeimage -DartifactId=pointsto -Dversion=$VERSION -Dpackaging=jar
+mvn install:install-file -Dfile=$GRAALVM_HOME/lib/svm/builder/native-image-base.jar -DgroupId=org.graalvm.nativeimage -DartifactId=native-image-base -Dversion=$VERSION -Dpackaging=jar
 
 popd > /dev/null
 rm -rf jartmp
diff --git a/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/annotations/EnclaveMethod.java b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/annotations/EnclaveMethod.java
new file mode 100644
index 0000000..7a5fa94
--- /dev/null
+++ b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/annotations/EnclaveMethod.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2021, Alibaba Group Holding Limited. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.alibaba.confidentialcomputing.common.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark a method is running inside the Enclave, but can be directly invoked from the Host.
+ * So its parameters and returned value types are required to get serialized.
+ * If a service provider's interface has been marked with {@link EnclaveService}, there is no need to mark its methods with
+ * this annotation.
+ * <p>
+ * Please refer {@link EnclaveService} for the details about automatic serialization type registration in native image scenario.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnclaveMethod {
+}
diff --git a/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/annotations/EnclaveService.java b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/annotations/EnclaveService.java
new file mode 100644
index 0000000..0b193a8
--- /dev/null
+++ b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/annotations/EnclaveService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2021, Alibaba Group Holding Limited. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.alibaba.confidentialcomputing.common.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark an interface is used as SPI service for Enclave.
+ * All its providers' public non-static methods are {@link EnclaveMethod}s.
+ * When the TEE side is native image (SVM), {@link EnclaveMethod}'s parameter and returned types may be automatically
+ * registered for reflection and serialization at native image build time. But a class can be automatically registered
+ * for serialization only when it:
+ * <ul>
+ * <li>Doesn't have {@code writeObject} method. The {@code writeObject} customizes the serialization rule, preventing
+ * native image generator automatically inferring the associated serialization types. A typical example is {@link java.util.ArrayList}. </li>
+ * <li>Is effective final, i.e. doesn't have subclasses.</li>
+ * </ul>
+ * Native image generator issues a warning when the parameter and returned value type don't obey the above two rules.
+ * To solve the problem, user can choose another class for replacement if possible, e.g. using array instead of {@link java.util.ArrayList};
+ * or generating the serialization configuration by <a href="https://github.com/ziyilin/ziyi-forked-graal/blob/master/docs/reference-manual/native-image/Agent.md">native-image-agent</a>.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnclaveService {
+}
diff --git a/sdk/enclave/pom.xml b/sdk/enclave/pom.xml
index 8c79f06..8b66556 100644
--- a/sdk/enclave/pom.xml
+++ b/sdk/enclave/pom.xml
@@ -12,6 +12,57 @@
     <packaging>jar</packaging>
     <name>JavaEnclave-Enclave</name>
     <url></url>
+    <properties>
+        <graal.version>enclave-22.0.0</graal.version>
+        <svm.maven.version>0.9.10</svm.maven.version>
+    </properties>
+    <profiles>
+        <profile>
+            <id>native</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.graalvm.buildtools</groupId>
+                        <artifactId>native-maven-plugin</artifactId>
+                        <version>${svm.maven.version}</version>
+                        <extensions>true</extensions>
+                        <executions>
+                            <execution>
+                                <id>build-native</id>
+                                <goals>
+                                    <goal>build</goal>
+                                </goals>
+                                <configuration>
+                                    <imageName>libsvm_enclave_sdk</imageName>
+                                    <buildArgs>
+                                        <buildArg>--shared</buildArg>
+                                        <buildArg>--no-fallback</buildArg>
+                                        <buildArg>-H:+RunInEnclave</buildArg>
+                                        <buildArg>-H:OutputRelocatableImage=.</buildArg>
+                                        <buildArg>-H:Path=svm-output</buildArg>
+                                    </buildArgs>
+                                </configuration>
+                                <phase>package</phase>
+                            </execution>
+                            <execution>
+                                <id>test-native</id>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <buildArgs>
+                                        <buildArg>--no-fallback</buildArg>
+                                        <buildArg>-H:+RunInEnclave</buildArg>
+                                    </buildArgs>
+                                </configuration>
+                                <phase>test</phase>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
     <build>
         <plugins>
             <plugin>
@@ -27,6 +78,17 @@
                         <arg>--add-exports</arg>
                         <arg>jdk.internal.vm.ci/jdk.vm.ci.meta=ALL-UNNAMED</arg>
                     </compilerArgs>
+                    <!--OptionProcessor can automatically generate OptionDescriptor classes at javac time-->
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>org.graalvm.compiler</groupId>
+                            <artifactId>graal-processor</artifactId>
+                            <version>${graal.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                    <annotationProcessors>
+                        <annotationProcessor>org.graalvm.compiler.options.processor.OptionProcessor</annotationProcessor>
+                    </annotationProcessors>
                 </configuration>
             </plugin>
             <plugin>
@@ -93,16 +155,47 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>3.0.0</version>
+                <executions>
+                    <execution>
+                        <id>preTest</id>
+                        <phase>test-compile</phase>
+                        <goals>
+                            <goal>java</goal>
+                        </goals>
+                        <configuration>
+                            <mainClass>com.alibaba.confidentialcomputing.enclave.AroundNativeTest$PreTest</mainClass>
+                            <classpathScope>test</classpathScope>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>postTest</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>java</goal>
+                        </goals>
+                        <configuration>
+                            <mainClass>com.alibaba.confidentialcomputing.enclave.AroundNativeTest$PostTest</mainClass>
+                            <classpathScope>test</classpathScope>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.22.1</version>
+                <version>3.0.0-M5</version>
+                <configuration>
+                    <environmentVariables>
+                        <LD_LIBRARY_PATH>/tmp/javaenclavetest-native-libs</LD_LIBRARY_PATH>
+                    </environmentVariables>
+                </configuration>
             </plugin>
         </plugins>
     </build>
-    <properties>
-        <graal.version>enclave-22.0.0</graal.version>
-    </properties>
     <dependencies>
         <dependency>
             <groupId>org.graalvm.sdk</groupId>
@@ -129,6 +222,12 @@
             <artifactId>junit-jupiter-engine</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.graalvm.buildtools</groupId>
+            <artifactId>junit-platform-native</artifactId>
+            <version>${svm.maven.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.alibaba.confidentialcomputing</groupId>
             <artifactId>common</artifactId>
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclaveEntry.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclaveEntry.java
new file mode 100644
index 0000000..04d091c
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclaveEntry.java
@@ -0,0 +1,79 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import com.alibaba.confidentialcomputing.enclave.c.EnclaveEnvironment.CallBacks;
+import com.alibaba.confidentialcomputing.enclave.c.EnclaveEnvironment.EncData;
+import com.alibaba.confidentialcomputing.enclave.framework.LoadServiceInvoker;
+import com.alibaba.confidentialcomputing.enclave.framework.ServiceMethodInvoker;
+import com.alibaba.confidentialcomputing.enclave.framework.UnloadServiceInvoker;
+import com.oracle.svm.core.c.function.CEntryPointOptions;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.Isolate;
+import org.graalvm.nativeimage.c.function.CEntryPoint;
+import org.graalvm.nativeimage.c.type.CTypeConversion;
+
+/**
+ * This class defines the entry points for native image (shared library) deployed in TEE enclave.
+ */
+public class EnclaveEntry {
+    private static CallBacks callBackMethods;
+
+    @SuppressWarnings("unused")
+    @CEntryPoint(name = "java_loadservice_invoke")
+    @CEntryPointOptions(prologue = EnclavePrologue.class)
+    public static int loadService(Isolate isolate, EncData input, EncData result, CallBacks callBacks) {
+        callBackMethods = callBacks;
+        int retCode = 0;
+        try {
+            InvocationWrapper.invoke(input, result, callBacks, ImageSingletons.lookup(LoadServiceInvoker.class));
+        } catch (Throwable t) {
+            retCode = handleFrameworkException(t);
+        }
+        return retCode;
+    }
+
+    @SuppressWarnings("unused")
+    @CEntryPoint(name = "java_unloadservice_invoke")
+    @CEntryPointOptions(prologue = EnclavePrologue.class)
+    public static int unloadService(Isolate isolate, EncData input, EncData result, CallBacks callBacks) {
+        callBackMethods = callBacks;
+        int retCode = 0;
+        try {
+            InvocationWrapper.invoke(input, result, callBacks, ImageSingletons.lookup(UnloadServiceInvoker.class));
+        } catch (Throwable t) {
+            retCode = handleFrameworkException(t);
+        }
+        return retCode;
+    }
+
+    @SuppressWarnings("unused")
+    @CEntryPoint(name = "java_enclave_invoke")
+    @CEntryPointOptions(prologue = EnclavePrologue.class)
+    public static int javaEnclaveInvoke(Isolate isolate, EncData input, EncData result, CallBacks callBacks) {
+        callBackMethods = callBacks;
+        int retCode = 0;
+        try {
+            InvocationWrapper.invoke(input, result, callBacks, ImageSingletons.lookup(ServiceMethodInvoker.class));
+        } catch (Throwable t) {
+            retCode = handleFrameworkException(t);
+        }
+        return retCode;
+    }
+
+    private static int handleFrameworkException(Throwable t) {
+        if (callBackMethods.isNonNull() && callBackMethods.getExceptionHandler().isNonNull()) {
+            StringBuilder stacktraceSB = new StringBuilder();
+            for (StackTraceElement se : t.getStackTrace()) {
+                stacktraceSB.append(se.toString()).append("\n");
+            }
+            try (
+                    CTypeConversion.CCharPointerHolder stacktrace = CTypeConversion.toCString(stacktraceSB.toString());
+                    CTypeConversion.CCharPointerHolder errMsg = CTypeConversion.toCString(t.getMessage());
+                    CTypeConversion.CCharPointerHolder exception = CTypeConversion.toCString(t.getClass().toString())) {
+                callBackMethods.getExceptionHandler().invoke(errMsg.get(), stacktrace.get(), exception.get());
+            }
+        } else {
+            t.printStackTrace();
+        }
+        return 1;
+    }
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclaveFeature.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclaveFeature.java
new file mode 100644
index 0000000..fe36075
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclaveFeature.java
@@ -0,0 +1,130 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import com.alibaba.confidentialcomputing.common.annotations.EnclaveMethod;
+import com.alibaba.confidentialcomputing.common.annotations.EnclaveService;
+import com.alibaba.confidentialcomputing.common.exception.ConfidentialComputingException;
+import com.alibaba.confidentialcomputing.enclave.framework.LoadServiceInvoker;
+import com.alibaba.confidentialcomputing.enclave.framework.ServiceMethodInvoker;
+import com.alibaba.confidentialcomputing.enclave.framework.UnloadServiceInvoker;
+import com.oracle.svm.core.annotate.AutomaticFeature;
+import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemUtil;
+import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.hosted.FeatureImpl;
+import com.oracle.svm.hosted.ImageClassLoader;
+import com.oracle.svm.hosted.ServiceLoaderFeature;
+import com.oracle.svm.reflect.hosted.ReflectionFeature;
+import com.oracle.svm.reflect.serialize.hosted.SerializationFeature;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.hosted.Feature;
+import org.graalvm.nativeimage.hosted.RuntimeReflection;
+import org.graalvm.nativeimage.hosted.RuntimeSerialization;
+import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+@AutomaticFeature
+public class EnclaveFeature implements Feature {
+
+    private ImageClassLoader imageClassLoader;
+    private final Map<Class<?>, Boolean> serializationCandidateTypes = new HashMap<>();
+    private final Map<Class<?>, Boolean> reflectionCandidateTypes = new HashMap<>();
+    private final Map<Method, Boolean> reflectionCandidateMethods = new HashMap<>();
+
+    @Override
+    public List<Class<? extends Feature>> getRequiredFeatures() {
+        return Arrays.asList(ReflectionFeature.class, SerializationFeature.class, ServiceLoaderFeature.class);
+    }
+
+    @Override
+    public void duringSetup(DuringSetupAccess access) {
+        ImageSingletons.add(ServiceMethodInvoker.class, new ServiceMethodInvoker());
+        ImageSingletons.add(LoadServiceInvoker.class, new LoadServiceInvoker());
+        ImageSingletons.add(UnloadServiceInvoker.class, new UnloadServiceInvoker());
+        ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime("com.alibaba.confidentialcomputing.enclave.EnclavePrologue",
+                "Prologue class should be initialize at build time.");
+
+        FeatureImpl.DuringSetupAccessImpl config = (FeatureImpl.DuringSetupAccessImpl) access;
+        RuntimeSerialization.register(ConfidentialComputingException.class, RuntimeException.class,
+                ReflectiveOperationException.class, ClassNotFoundException.class);
+        RuntimeSerialization.registerAllAssociatedClasses(Collections.EMPTY_LIST.getClass());
+        imageClassLoader = config.getImageClassLoader();
+    }
+
+    /**
+     * Collect reflection and serialization configurations from {@link EnclaveService} marked interfaces.
+     */
+    @Override
+    public void duringAnalysis(DuringAnalysisAccess access) {
+        List<Class<?>> enclaveServices = imageClassLoader.findAnnotatedClasses(EnclaveService.class, true);
+        enclaveServices.forEach(serviceClazz -> {
+            reflectionCandidateTypes.putIfAbsent(serviceClazz, false);
+            byte[] serviceConfig = NativeImageResourceFileSystemUtil.getBytes("META-INF/services/" + serviceClazz.getName(), true);
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(serviceConfig), StandardCharsets.UTF_8))) {
+                while (true) {
+                    String line = reader.readLine();
+                    if (line == null) {
+                        break;
+                    }
+                    Class<?> implementation = imageClassLoader.findClass(line).get();
+                    collectConfigs(implementation, Arrays.stream(implementation.getMethods()).filter(method ->
+                            serviceClazz.isAssignableFrom(method.getDeclaringClass())
+                    ).collect(Collectors.toList()));
+                }
+            } catch (IOException e) {
+                VMError.shouldNotReachHere(e);
+            }
+        });
+        List<Method> extraEnclaveMethods = imageClassLoader.findAnnotatedMethods(EnclaveMethod.class);
+        extraEnclaveMethods.forEach(method -> collectConfigs(method.getDeclaringClass(), List.of(method)));
+
+        // Register all newly collected configures
+        if (registerCollectedConfigs()) {
+            access.requireAnalysisIteration();
+        }
+    }
+
+
+    private void collectConfigs(Class<?> clazz, List<Method> methods) {
+        reflectionCandidateTypes.putIfAbsent(clazz, false);
+        methods.stream().filter(m -> !Modifier.isStatic(m.getModifiers())).forEach(
+                method -> {
+                    for (Class<?> pType : method.getParameterTypes()) {
+                        serializationCandidateTypes.putIfAbsent(pType, false);
+                    }
+                    serializationCandidateTypes.putIfAbsent(method.getReturnType(), false);
+                    reflectionCandidateMethods.putIfAbsent(method, false);
+                }
+        );
+    }
+
+    private boolean registerCollectedConfigs() {
+        boolean registeredNewSerializations = registerCollectedConfigs(serializationCandidateTypes, RuntimeSerialization::registerAllAssociatedClasses);
+        boolean registeredNewReflectionTypes = registerCollectedConfigs(reflectionCandidateTypes, RuntimeReflection::register);
+        boolean registeredNewReflectionMethods = registerCollectedConfigs(reflectionCandidateMethods, RuntimeReflection::register);
+        return registeredNewSerializations || registeredNewReflectionTypes || registeredNewReflectionMethods;
+    }
+
+    private <T> boolean registerCollectedConfigs(Map<T, Boolean> configs, Consumer<T> registerAction) {
+        boolean needRegisterNew = configs.entrySet().stream().anyMatch(entry -> !entry.getValue());
+        configs.entrySet().stream().filter(entry -> !entry.getValue()).map(Map.Entry::getKey).forEach(
+                key -> {
+                    registerAction.accept(key);
+                    configs.put(key, true);
+                }
+        );
+        return needRegisterNew;
+    }
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclavePrologue.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclavePrologue.java
new file mode 100644
index 0000000..596010a
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/EnclavePrologue.java
@@ -0,0 +1,22 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import com.oracle.svm.core.annotate.Uninterruptible;
+import com.oracle.svm.core.c.CGlobalData;
+import com.oracle.svm.core.c.CGlobalDataFactory;
+import com.oracle.svm.core.c.function.CEntryPointActions;
+import com.oracle.svm.core.c.function.CEntryPointOptions;
+import org.graalvm.nativeimage.Isolate;
+import org.graalvm.nativeimage.c.type.CCharPointer;
+
+public class EnclavePrologue implements CEntryPointOptions.Prologue {
+    private static final CGlobalData<CCharPointer> errorMessage = CGlobalDataFactory.createCString("Failed to enter (or attach to) the global isolate in the current thread.");
+
+    @Uninterruptible(reason = "prologue")
+    static void enter(Isolate isolate) {
+
+        int code = CEntryPointActions.enterAttachThread(isolate, true);
+        if (code != 0) {
+            CEntryPointActions.failFatally(code, errorMessage.get());
+        }
+    }
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/InvocationWrapper.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/InvocationWrapper.java
new file mode 100644
index 0000000..c732f7c
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/InvocationWrapper.java
@@ -0,0 +1,69 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+import com.alibaba.confidentialcomputing.common.SerializationHelper;
+import com.alibaba.confidentialcomputing.common.exception.ConfidentialComputingException;
+import com.alibaba.confidentialcomputing.enclave.c.EnclaveEnvironment.CallBacks;
+import com.alibaba.confidentialcomputing.enclave.c.EnclaveEnvironment.EncData;
+import com.alibaba.confidentialcomputing.enclave.framework.EnclaveMethodInvoker;
+import org.graalvm.nativeimage.c.type.CCharPointer;
+import org.graalvm.nativeimage.c.type.CTypeConversion;
+import org.graalvm.word.PointerBase;
+
+import java.io.IOException;
+
+/**
+ * This class deals with the whole method invocation process from native entry point to the actual Java invocation target.
+ * It is taken out in 3 steps:<p>
+ * <li>Transform the input data from C {@link PointerBase} to Java byte[] and then deserialize to get the actual input.</li>
+ * <li>Make the method invocation.</li>
+ * <li>Collect the returned value, serialize it to the Java byte[], and wrap it back to C {@link PointerBase}</li>
+ * </p>
+ */
+public class InvocationWrapper {
+
+    public static <T> void invoke(EncData input, EncData result, CallBacks callBacks, EnclaveMethodInvoker<T> invoker) throws IOException {
+        byte[] data = transformInput(input);
+        EnclaveInvocationResult ret;
+        try {
+            ret = invoker.callMethod((T) SerializationHelper.deserialize(data));
+        } catch (Throwable t) {
+            ret = new EnclaveInvocationResult(null, new ConfidentialComputingException(t));
+        }
+        // Set method returned value to result parameter
+        wrapReturnValue(result, callBacks, ret);
+    }
+
+    private static void wrapReturnValue(EncData result, CallBacks callBacks, EnclaveInvocationResult ret) throws IOException {
+        byte[] returnedValBytes;
+        returnedValBytes = SerializationHelper.serialize(ret);
+        int returnedValLen = returnedValBytes.length;
+        /*
+         * Data returned to C world should be allocated by the callback function in the C world. The memory hold by
+         * returnedValBytes shall be freed in the explicit finally clause.
+         */
+        try (CTypeConversion.CCharPointerHolder byteHolder = CTypeConversion.toCBytes(returnedValBytes)) {
+            CCharPointer returned;
+            if (callBacks.isNonNull() && callBacks.getMemCpyCCharPointerFunctionPointer().isNonNull()) {
+                returned = callBacks.getMemCpyCCharPointerFunctionPointer().invoke(byteHolder.get(), returnedValLen);
+
+            } else {
+                returned = byteHolder.get();
+                System.out.println("Warning: Not calling call backs in native, there is memory leak risk.");
+                //throw new RuntimeException("Function pointer memcpy_char_pointer is not set");
+            }
+            result.setData(returned);
+            result.setLen(returnedValLen);
+        }
+    }
+
+    /**
+     * Transform input data from WordBase type to Java type.
+     */
+    private static byte[] transformInput(EncData input) {
+        int len = input.getLen();
+        byte[] data = new byte[len];
+        CTypeConversion.asByteBuffer(input.getData(), len).get(data);
+        return data;
+    }
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/c/EnclaveEnvironment.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/c/EnclaveEnvironment.java
new file mode 100644
index 0000000..922ab29
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/c/EnclaveEnvironment.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2021, Alibaba Group Holding Limited. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.alibaba.confidentialcomputing.enclave.c;
+
+import com.oracle.svm.core.c.ProjectHeaderFile;
+import com.oracle.svm.core.c.libc.TemporaryBuildDirectoryProvider;
+import com.oracle.svm.core.util.VMError;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.c.CContext;
+import org.graalvm.nativeimage.c.function.CFunctionPointer;
+import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer;
+import org.graalvm.nativeimage.c.struct.CField;
+import org.graalvm.nativeimage.c.struct.CStruct;
+import org.graalvm.nativeimage.c.type.CCharPointer;
+import org.graalvm.word.PointerBase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+@CContext(EnclaveEnvironment.EnclaveDirectives.class)
+public class EnclaveEnvironment {
+
+    static class EnclaveDirectives implements CContext.Directives {
+
+        private static final String HEADER_FILE = "native/enc_environment.h";
+
+        @Override
+        public List<String> getHeaderFiles() {
+            // Register additional resolver to resolve header file from jar file as resource stream
+            ProjectHeaderFile.HeaderResolversRegistry.registerAdditionalResolver((projectName, headerFile) -> {
+                try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+                    InputStream headerInputStream = EnclaveDirectives.class.getClassLoader().getResourceAsStream(HEADER_FILE);
+                    if (headerInputStream == null) {
+                        VMError.shouldNotReachHere("Can't find " + HEADER_FILE + " from classpath as resource");
+                    }
+                    headerInputStream.transferTo(os);
+                    Path headerPath = ImageSingletons.lookup(TemporaryBuildDirectoryProvider.class).getTemporaryBuildDirectory().resolve(HEADER_FILE);
+                    Path nativeDir = headerPath.getParent();
+                    if (Files.notExists(nativeDir)) {
+                        Files.createDirectory(nativeDir);
+                    }
+                    File tmpHeaderFile = Files.createFile(headerPath).toFile();
+                    try (FileOutputStream fos = new FileOutputStream(tmpHeaderFile)) {
+                        fos.write(os.toByteArray());
+                        return new ProjectHeaderFile.HeaderSearchResult(Optional.of("\"" + tmpHeaderFile.getAbsolutePath() + "\""), tmpHeaderFile.getAbsolutePath());
+                    }
+                } catch (IOException e) {
+                    VMError.shouldNotReachHere(e);
+                }
+                return null;
+            });
+            return Collections.singletonList(ProjectHeaderFile.resolve("enclave", HEADER_FILE));
+        }
+    }
+
+    @CStruct("enc_data")
+    public interface EncData extends PointerBase {
+        @CField("data_len")
+        int getLen();
+
+        @CField("data_len")
+        void setLen(int len);
+
+        @CField("data")
+        CCharPointer getData();
+
+        @CField("data")
+        void setData(CCharPointer data);
+    }
+
+    @CStruct("callbacks")
+    public interface CallBacks extends PointerBase {
+        @CField("exception_handler")
+        ExceptionHandleFunctionPointer getExceptionHandler();
+
+        @CField("exception_handler")
+        void setExceptionHandler(ExceptionHandleFunctionPointer functionPointer);
+
+        @CField("memcpy_char_pointer")
+        MemCpyCCharPointerFunctionPointer getMemCpyCCharPointerFunctionPointer();
+
+        @CField("memcpy_char_pointer")
+        void setMemCpyCCharPointerFunctionPointer(MemCpyCCharPointerFunctionPointer functionPointer);
+    }
+
+    public interface ExceptionHandleFunctionPointer extends CFunctionPointer {
+        @InvokeCFunctionPointer
+        void invoke(CCharPointer errorMsg, CCharPointer stackTrace, CCharPointer exception);
+    }
+
+    public interface MemCpyCCharPointerFunctionPointer extends CFunctionPointer {
+        @InvokeCFunctionPointer
+        CCharPointer invoke(CCharPointer source, int length);
+    }
+}
diff --git a/sdk/enclave/src/main/resources/META-INF/native-image/reflect-config.json b/sdk/enclave/src/main/resources/META-INF/native-image/reflect-config.json
new file mode 100644
index 0000000..1c5dc62
--- /dev/null
+++ b/sdk/enclave/src/main/resources/META-INF/native-image/reflect-config.json
@@ -0,0 +1,20 @@
+[
+  {
+    "name": "java.security.MessageDigestSpi"
+  },
+  {
+    "name": "[Ljava.lang.Object;"
+  },
+  {
+    "name": "[Ljava.lang.String;"
+  },
+  {
+    "name": "sun.security.provider.SHA",
+    "methods": [
+      {
+        "name": "<init>",
+        "parameterTypes": []
+      }
+    ]
+  }
+]
diff --git a/sdk/enclave/src/main/resources/META-INF/native-image/serialization-config.json b/sdk/enclave/src/main/resources/META-INF/native-image/serialization-config.json
new file mode 100644
index 0000000..45dfb55
--- /dev/null
+++ b/sdk/enclave/src/main/resources/META-INF/native-image/serialization-config.json
@@ -0,0 +1,44 @@
+[
+  {
+  "name":"com.alibaba.confidentialcomputing.common.EnclaveInvocationContext"
+  },
+  {
+    "name":"com.alibaba.confidentialcomputing.common.EnclaveInvocationResult"
+  },
+  {
+  "name":"com.alibaba.confidentialcomputing.common.ServiceHandler"
+  },
+  {
+    "name": "[Lcom.alibaba.confidentialcomputing.common.ServiceHandler;"
+  },
+  {
+  "name":"java.lang.String"
+  },
+  {
+    "name":"java.util.ArrayList"
+  },
+  {
+    "name": "java.util.Arrays$ArrayList"
+  },
+  {
+  "name":"java.lang.Throwable"
+  },
+  {
+  "name":"java.lang.Exception"
+  },
+  {
+  "name":"java.io.IOException"
+  },
+  {
+  "name":"java.io.ObjectStreamException"
+  },
+  {
+  "name":"java.io.NotSerializableException"
+  },
+  {
+  "name":"java.lang.StackTraceElement"
+  },
+  {
+    "name":"[Ljava.lang.StackTraceElement;"
+  }
+]
diff --git a/sdk/enclave/src/main/resources/native/enc_environment.h b/sdk/enclave/src/main/resources/native/enc_environment.h
new file mode 100644
index 0000000..dc6f683
--- /dev/null
+++ b/sdk/enclave/src/main/resources/native/enc_environment.h
@@ -0,0 +1,17 @@
+ typedef struct enc_data_struct{
+    //char array is used as byte array to store serialized data
+    char* data;
+    int data_len;
+ }enc_data;
+
+typedef struct callback_functions_struct{
+     /*
+      * This method is invoked inside java_enclave_invoke method's exception catch
+      * section, when the execution is aborted by exceptions. The caller side can
+      * decide what to do with the exception.
+      * Exception details are passed back with parameters.
+     */
+    void (*exception_handler)(char* err_msg, char* stack_trace, char* exception_name);
+
+    char* (*memcpy_char_pointer)(char* src, int len);
+}callbacks;
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/AroundNativeTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/AroundNativeTest.java
new file mode 100644
index 0000000..ec28e4e
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/AroundNativeTest.java
@@ -0,0 +1,49 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+/**
+ * This class holds 2 main classes that are executed before and after maven surefire test by <a href="https://www.mojohaus.org/exec-maven-plugin/">exec-maven-plugin</a>.
+ */
+public class AroundNativeTest {
+    public static final Path tmpTestNativeLibsDir = Paths.get("/tmp/javaenclavetest-native-libs");
+
+    /**
+     * Before test starts, create the temporary directory to hold the dynamic native libraries that will be created
+     * during test. But the directory must be created beforehand, so that the {@code export LD_LIBRARY_PATH} action
+     * taken by surefire plugin can take effect.
+     */
+    public static class PreTest {
+        public static void main(String[] args) throws IOException {
+            if (Files.notExists(tmpTestNativeLibsDir)) {
+                Files.createDirectories(tmpTestNativeLibsDir);
+            }
+        }
+    }
+
+    public static class PostTest {
+        public static void main(String[] args) throws IOException {
+            if (Files.exists(tmpTestNativeLibsDir)) {
+                Files.walkFileTree(tmpTestNativeLibsDir, new SimpleFileVisitor<>() {
+                    @Override
+                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                        Files.delete(file);
+                        return super.visitFile(file, attrs);
+                    }
+
+                    @Override
+                    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                        Files.delete(dir);
+                        return super.postVisitDirectory(dir, exc);
+                    }
+                });
+            }
+        }
+    }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/EnclaveTestHelper.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/EnclaveTestHelper.java
index f47d64b..c51c90f 100644
--- a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/EnclaveTestHelper.java
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/EnclaveTestHelper.java
@@ -1,12 +1,119 @@
 package com.alibaba.confidentialcomputing.enclave;
 
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationContext;
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+import com.alibaba.confidentialcomputing.common.SerializationHelper;
+import com.alibaba.confidentialcomputing.common.ServiceHandler;
+import com.alibaba.confidentialcomputing.enclave.testservice.IntegerMath;
 import com.alibaba.confidentialcomputing.enclave.testservice.MathService;
 import com.alibaba.confidentialcomputing.enclave.testservice.NumericMath;
+import com.alibaba.confidentialcomputing.enclave.testservice.PointMath;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.function.Function;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.fail;
 
 public class EnclaveTestHelper {
     public static final String MATH_SERVICE = MathService.class.getName();
     public static final String NUMERIC_MATH = NumericMath.class.getName();
+    public static final String INTEGER_MATH = IntegerMath.class.getName();
+    public static final String POINT_MATH = PointMath.class.getName();
     public static final String[] MATH_ADD_PARAM_TYPES = {"java.lang.Number", "java.lang.Number"};
+    public static final String[] POINT_MATH_ADD_PARAM_TYPES = {"com.alibaba.confidentialcomputing.enclave.testservice.Point",
+            "com.alibaba.confidentialcomputing.enclave.testservice.Point"};
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+    public static final String VM_NAME = System.getProperty("java.vm.name");
+
+    public static native byte[] invokeEnclave(byte[] data);
+
+    public static native byte[] loadService(byte[] data);
+
+    public static native byte[] unloadService(byte[] data);
+
+    public static native void createIsolate();
+
+    public static native void destroyIsolate();
+
+    public static boolean isInNativeImage() {
+        return VM_NAME.equals("Substrate VM");
+    }
+
+    public static ServiceHandler[] callLoadService(String serviceName) throws IOException, ClassNotFoundException {
+        EnclaveInvocationResult ret = callEnclaveJNI(serviceName,
+                input -> loadService(input));
+        if (ret.getException() != null) {
+            ret.getException().printStackTrace();
+            fail();
+        }
+        return (ServiceHandler[]) ret.getResult();
+    }
+
+    public static EnclaveInvocationResult callEnclaveMethod(String service, String impl, String identity, String method, String[] paramTypes, Object[] values) throws IOException, ClassNotFoundException {
+        EnclaveInvocationContext enclaveInvocationContext = new EnclaveInvocationContext(new ServiceHandler(service, impl
+                , identity), method, paramTypes, values);
+        return callEnclaveJNI(enclaveInvocationContext,
+                input -> invokeEnclave(input));
+    }
+
+    private static EnclaveInvocationResult callEnclaveJNI(Object input, Function<byte[], byte[]> function) throws IOException, ClassNotFoundException {
+        byte[] data = SerializationHelper.serialize(input);
+        byte[] ret = function.apply(data);
+        return (EnclaveInvocationResult) SerializationHelper.deserialize(ret);
+    }
+
+    public static Path createTestTmpDir(Path root) {
+        Path tmpDir;
+        try {
+            tmpDir = Files.createTempDirectory(root, "native-test-");
+        } catch (IOException e) {
+            e.printStackTrace();
+            tmpDir = null;
+        }
+        return tmpDir;
+    }
+
+    public static String loadAndGetService(String serviceName, String implementation, int expectedServiceNum) {
+        ServiceHandler[] serviceHandlers = new ServiceHandler[0];
+        try {
+            serviceHandlers = callLoadService(serviceName);
+        } catch (IOException | ClassNotFoundException e) {
+            fail(e);
+        }
+        assertEquals(expectedServiceNum, serviceHandlers.length);
+        // SVM doesn't guarantee the service order
+        for (ServiceHandler serviceHandler : serviceHandlers) {
+            if (serviceHandler.getServiceImplClassName().equals(implementation)) {
+                return serviceHandler.getInstanceIdentity();
+            }
+        }
+        fail("Should not reach here");
+        return null;
+    }
+
+    public static Object call(String id, String serviceName, String className, String methodName, String[] paramTypes, Object[] paramValues) {
+        try {
+            EnclaveInvocationResult result = callEnclaveMethod(serviceName,
+                    className, id,
+                    methodName,
+                    paramTypes,
+                    paramValues);
+            Object wrappedResult = result.getResult();
+            if (result.getException() != null) {
+                result.getException().printStackTrace();
+            }
+            assertNotNull(wrappedResult, "Expect to have non-null result from invoking service method call.");
+            assertNull(result.getException());
+            return wrappedResult;
+        } catch (IOException | ClassNotFoundException e) {
+            fail(e);
+            return 0;
+        }
+    }
 }
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/NativeImageTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/NativeImageTest.java
new file mode 100644
index 0000000..ed676da
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/NativeImageTest.java
@@ -0,0 +1,264 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.SequenceInputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class NativeImageTest implements NativeImageTestable {
+    private static final String JNI_LIB_NAME = "encinvokeentrytest";
+    public static final Path GRAALVM_HOME = Paths.get(System.getenv("GRAALVM_HOME"));
+    public static final Path MVN_BUILD_DIR = Paths.get("target");
+    private static final String SVM_OUT = "svm-out";
+    private static final String SVM_ENCLAVE_LIB = "svm_enclave_sdk";
+
+    static {
+        if (!GRAALVM_HOME.toFile().exists()) {
+            throw new RuntimeException("System environment variable GRAALVM_HOME is set to " + GRAALVM_HOME
+                    + ", but the directory does not exist!");
+        }
+        if (!MVN_BUILD_DIR.toFile().exists()) {
+            throw new RuntimeException("Maven default build directory " + MVN_BUILD_DIR.toAbsolutePath() + " doesn't exist." +
+                    " Please check your maven's ${project.build.directory} property and make sure it's \"target\".");
+        }
+    }
+
+    protected String testName;
+    protected Path workingDir;
+    protected Path configRootDir;
+    protected Path configPathDir;
+    protected Path svmEncSDKClassDir;
+    protected Path serviceDir;
+    protected Path svmOutputDir;
+    protected Path testClassesDir;
+    protected Path svmCompileClassesDir;
+
+    static class SVMCompileElements {
+        private final List<String> serviceConfigs = new ArrayList<>();
+        private final List<String> svmConfigs = new ArrayList<>();
+        private final List<String> otherResources = new ArrayList<>();
+        private final List<Class<?>> classes = new ArrayList<>();
+
+        public void addServices(String... services) {
+            serviceConfigs.addAll(Arrays.asList(services));
+        }
+
+        public void addSvmConfigs(String... configs) {
+            svmConfigs.addAll(Arrays.asList(configs));
+        }
+
+        public void addResources(String... resources) {
+            otherResources.addAll(Arrays.asList(resources));
+        }
+
+        public void addClasses(Class<?>... classes2Compile) {
+            classes.addAll(Arrays.asList(classes2Compile));
+        }
+    }
+
+    public NativeImageTest() {
+        TestTarget targetTest = this.getClass().getAnnotation(TestTarget.class);
+        if (targetTest == null) {
+            throw new RuntimeException("The subclasses of NativeImageTest must use @TestTarget specify the test target class."
+                    + " But class " + this.getClass().getName() + " doesn't have one.");
+        }
+        String targetTestClassName = targetTest.value().getName();
+        int lastDot = targetTestClassName.lastIndexOf('.');
+        testName = targetTestClassName.substring(lastDot + 1).toLowerCase();
+
+        workingDir = MVN_BUILD_DIR.resolve("native-work-dir-" + testName);
+        configRootDir = workingDir.resolve("config");
+        configPathDir = configRootDir.resolve("META-INF/native-image");
+        svmEncSDKClassDir = MVN_BUILD_DIR.resolve("classes");
+        serviceDir = configRootDir.resolve("META-INF/services");
+        svmOutputDir = workingDir.resolve(SVM_OUT);
+        svmCompileClassesDir = workingDir.resolve("bin");
+        List<Path> dirsToCreate = new ArrayList<>();
+        dirsToCreate.add(workingDir);
+        dirsToCreate.add(configRootDir);
+        dirsToCreate.add(configPathDir);
+        dirsToCreate.add(serviceDir);
+        dirsToCreate.add(svmCompileClassesDir);
+        dirsToCreate.forEach(p -> createDirs(p, "Can't create directory " + p + " at test preparation time"));
+        testClassesDir = MVN_BUILD_DIR.resolve("test-classes");
+    }
+
+    public void prepareNativeLibraries() {
+        collectSVMCompileItems();
+        runWithNativeImageAgent();
+        beforeSVMCompile();
+        svmCompile();
+        afterSVMCompile();
+        compileJNILibrary();
+        Path so1 = workingDir.resolve("lib" + JNI_LIB_NAME + ".so");
+        Path so2 = workingDir.resolve("lib" + SVM_ENCLAVE_LIB + ".so");
+        copyFile(so1, AroundNativeTest.tmpTestNativeLibsDir.resolve(so1.getFileName()), null);
+        copyFile(so2, AroundNativeTest.tmpTestNativeLibsDir.resolve(so2.getFileName()), null);
+        System.loadLibrary(JNI_LIB_NAME);
+    }
+
+    private void collectSVMCompileItems() {
+        SVMCompileElements items = specifyTestClasses();
+        if (items == null) {
+            throw new RuntimeException("Must specify the elements to be compiled by native-image for testing.");
+        }
+
+        if (!items.svmConfigs.isEmpty()) {
+            items.svmConfigs.stream().map(s -> testClassesDir.resolve(s).toAbsolutePath()).
+                    forEach(p -> copyFile(p, configPathDir.resolve(p.getFileName()), "Fail to copy configuration file."));
+        }
+
+        if (items.classes.isEmpty()) {
+            throw new RuntimeException("Must specify the classes to be compiled by native-image for testing.");
+        } else {
+            items.classes.forEach(c ->
+            {
+                String classLocation = getClassFileName(c);
+                Path destPath = svmCompileClassesDir.resolve(classLocation);
+                createDirs(destPath.getParent(), "Can't create class directories for native-image compilation.");
+                copyFile(testClassesDir.resolve(classLocation).toAbsolutePath(), destPath, "Can't copy class file.");
+            });
+        }
+
+        if (!items.serviceConfigs.isEmpty()) {
+            items.serviceConfigs.stream().map(s -> testClassesDir.resolve(s).toAbsolutePath()).
+                    forEach(p -> copyFile(p, serviceDir.resolve(p.getFileName()), null));
+        }
+
+        if (!items.otherResources.isEmpty()) {
+            items.otherResources.forEach(s -> {
+                Path destPath = svmCompileClassesDir.resolve(s);
+                createDirs(destPath.getParent(), "Can't create resource directories for native-image compilation.");
+                copyFile(testClassesDir.resolve(s).toAbsolutePath(), destPath, "Can't copy resource file.");
+            });
+        }
+    }
+
+    abstract SVMCompileElements specifyTestClasses();
+
+    protected void svmCompile() {
+        List<String> command = new ArrayList<>();
+        command.add(0, GRAALVM_HOME.resolve("bin/native-image").toString());
+        command.add("-cp");
+        StringBuilder sb = new StringBuilder();
+        List<Path> svmBinFiles = Arrays.asList(svmCompileClassesDir,
+                configRootDir,
+                svmEncSDKClassDir,
+                MVN_BUILD_DIR.toAbsolutePath().getParent().getParent().resolve("common/target/classes")
+        );
+        svmBinFiles.stream().map(p -> p.normalize().toAbsolutePath()).forEach(p -> {
+            if (Files.notExists(p)) {
+                throw new RuntimeException("File " + p + " on native-image class file doesn't exist.");
+            }
+            sb.append(p.toString()).append(File.pathSeparator);
+        });
+        command.add(sb.deleteCharAt(sb.length() - 1).toString());
+        command.add("--shared");
+        command.add("--no-fallback");
+        command.add("-H:OutputRelocatableImage=.");
+        command.add("-H:Path=" + SVM_OUT);
+        command.add("-H:+AllowIncompleteClasspath");
+        command.add("-H:+ReportExceptionStackTraces");
+        command.add("-H:Name=lib" + SVM_ENCLAVE_LIB);
+        command.add("-H:-DeleteLocalSymbols");
+        List<String> extraOptions = extraSVMOptions();
+        if (extraOptions != null && !extraOptions.isEmpty()) {
+            command.addAll(extraOptions);
+        }
+        NativeImageTest.executeNewProcess(command, workingDir);
+    }
+
+    private void compileJNILibrary() {
+        System.out.println("###Prepare JNI library ...###");
+        List<Path> requiredFilePaths = new ArrayList<>();
+        requiredFilePaths.add(testClassesDir.resolve("native/com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper.h"));
+        requiredFilePaths.add(testClassesDir.resolve("native/enc_invoke_entry_test.c"));
+        requiredFilePaths.add(svmOutputDir.resolve("lib" + SVM_ENCLAVE_LIB + ".h"));
+        requiredFilePaths.add(svmOutputDir.resolve("graal_isolate.h"));
+        requiredFilePaths.add(svmOutputDir.resolve("enc_environment.h"));
+        requiredFilePaths.add(svmOutputDir.resolve("lib" + SVM_ENCLAVE_LIB + ".so"));
+        requiredFilePaths.forEach(p -> copyFile(p, workingDir.resolve(p.getFileName()), null));
+
+        List<String> command = new ArrayList<>();
+        command.add("gcc");
+        command.add("-fPIC");
+        command.add("-I" + GRAALVM_HOME.toAbsolutePath() + "/include");
+        command.add("-I" + GRAALVM_HOME.toAbsolutePath() + "/include/linux");
+        command.add("enc_invoke_entry_test.c");
+        command.add("-I.");
+        command.add("-L.");
+        command.add("-std=c99");
+        command.add("-l" + SVM_ENCLAVE_LIB);
+        command.add("-lc");
+        command.add("-shared");
+        command.add("-o");
+        command.add("lib" + JNI_LIB_NAME + ".so");
+        executeNewProcess(command, workingDir);
+    }
+
+    public static int executeNewProcess(List<String> command, Path workDir) {
+        if (command == null || command.isEmpty()) {
+            throw new RuntimeException("Didn't provide any execution command.");
+        }
+        ProcessBuilder pb = new ProcessBuilder(command).directory(workDir.toFile());
+        String oneLineCommand = command.stream().reduce((e1, e2) -> e1 + " " + e2).orElse("");
+        System.out.println(oneLineCommand);
+        Process p = null;
+        try {
+            p = pb.start();
+            SequenceInputStream sis = new SequenceInputStream(p.getInputStream(), p.getErrorStream());
+            InputStreamReader inst = new InputStreamReader(sis, StandardCharsets.UTF_8);
+            StringBuilder sb = new StringBuilder();
+            try (BufferedReader br = new BufferedReader(inst)) {
+                String res;
+                while ((res = br.readLine()) != null) {
+                    sb.append(res).append("\n");
+                }
+            }
+            System.out.println(sb);
+            int exitCode = p.waitFor();
+            if (exitCode != 0) {
+                throw new RuntimeException("Failed to execute command:\n " + oneLineCommand +
+                        "\n Working directory is :" + workDir.toString() + "\n The exit code is " + exitCode);
+            }
+            return 0;
+        } catch (IOException | InterruptedException e) {
+            throw new RuntimeException("Failed to execute command:\n " + oneLineCommand, e);
+        } finally {
+            if (p != null) {
+                p.destroy();
+            }
+        }
+    }
+
+    public static void copyFile(Path source, Path dest, String errMSg) {
+        try {
+            Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);
+        } catch (IOException e) {
+            throw new RuntimeException(errMSg, e);
+        }
+    }
+
+    private static void createDirs(Path p, String errMsg) {
+        if (Files.notExists(p)) {
+            try {
+                Files.createDirectories(p);
+            } catch (IOException e) {
+                throw new RuntimeException(errMsg, e);
+            }
+        }
+    }
+
+    public static String getClassFileName(Class<?> clazz) {
+        return clazz.getName().replace('.', '/') + ".class";
+    }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/NativeImageTestable.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/NativeImageTestable.java
new file mode 100644
index 0000000..7afe107
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/NativeImageTestable.java
@@ -0,0 +1,12 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import java.util.List;
+
+public interface NativeImageTestable {
+    default void runWithNativeImageAgent(){}
+    default void beforeSVMCompile(){}
+    default void afterSVMCompile(){}
+    default List<String> extraSVMOptions() {
+        return null;
+    }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/RunWithNativeImageTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/RunWithNativeImageTest.java
new file mode 100644
index 0000000..44ad76e
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/RunWithNativeImageTest.java
@@ -0,0 +1,166 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import com.alibaba.confidentialcomputing.enclave.testservice.IntegerMath;
+import com.alibaba.confidentialcomputing.enclave.testservice.MathService;
+import com.alibaba.confidentialcomputing.enclave.testservice.NumericMath;
+import com.alibaba.confidentialcomputing.enclave.testservice.Point;
+import com.alibaba.confidentialcomputing.enclave.testservice.PointMath;
+import com.oracle.svm.core.annotate.AutomaticFeature;
+import org.graalvm.nativeimage.hosted.Feature;
+import org.graalvm.nativeimage.hosted.RuntimeSerialization;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.security.NoSuchAlgorithmException;
+
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.EMPTY_OBJECT_ARRAY;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.EMPTY_STRING_ARRAY;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.INTEGER_MATH;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_ADD_PARAM_TYPES;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_SERVICE;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.NUMERIC_MATH;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.POINT_MATH;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.POINT_MATH_ADD_PARAM_TYPES;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class RunWithNativeImageTest {
+
+    @TestTarget(RunWithNativeImageTest.class)
+    static
+    class SimpleRunPreparation extends NativeImageTest {
+
+        @Override
+        public SVMCompileElements specifyTestClasses() {
+            SVMCompileElements ret = new SVMCompileElements();
+            // Specify the service file
+            ret.addServices("META-INF/services/com.alibaba.confidentialcomputing.enclave.testservice.MathService");
+
+            // Specify the classes need to be statically compiled into native image for this test
+            ret.addClasses(
+                    UTFeature.class, MathService.class, NumericMath.class, PointMath.class, Point.class, IntegerMath.class
+            );
+            return ret;
+        }
+    }
+
+    static private NativeImageTest nativeImageTest;
+
+    @BeforeAll
+    public static void prepareLibraries() {
+        nativeImageTest = new SimpleRunPreparation();
+        nativeImageTest.prepareNativeLibraries();
+    }
+
+    @BeforeEach
+    public void setup() {
+        EnclaveTestHelper.createIsolate();
+    }
+
+    @AfterEach
+    public void teardown() {
+        EnclaveTestHelper.destroyIsolate();
+    }
+
+    /**
+     * Test calling a method with primitive parameters and returned value.
+     */
+    @Test
+    public void testSimpleRun() {
+        String identity = loadAndGetService(NUMERIC_MATH);
+        assertEquals(3, callIntAdd(identity, 1, 2));
+    }
+
+    /**
+     * Test a multithreading case.
+     */
+    @Test
+    public void testMultiThreadRun() {
+        String identity1 = loadAndGetService(NUMERIC_MATH);
+        String identity2 = loadAndGetService(NUMERIC_MATH);
+
+        Thread t1 = new Thread(() -> {
+            try {
+                assertEquals(3, callIntAdd(identity1, 1, 2));
+            } catch (Exception e) {
+                fail(e);
+            }
+        });
+        Thread t2 = new Thread(() -> {
+            try {
+                assertEquals(9, callIntAdd(identity1, 4, 5));
+            } catch (Exception e) {
+                fail(e);
+            }
+        });
+        Thread t3 = new Thread(() -> {
+            try {
+                assertEquals(9, callIntAdd(identity2, 4, 5));
+            } catch (Exception e) {
+                fail(e);
+            }
+        });
+        t1.start();
+        t2.start();
+        t3.start();
+        try {
+            t1.join();
+            t2.join();
+            t3.join();
+        } catch (InterruptedException e) {
+            fail(e);
+        }
+        assertEquals(2, callGetCounter(identity1));
+        assertEquals(1, callGetCounter(identity2));
+    }
+
+    /**
+     * Test calling an interface default method.
+     */
+    @Test
+    public void testServiceDefaultMethod() {
+        String identity = loadAndGetService(INTEGER_MATH);
+        int ret = (Integer) call(identity, INTEGER_MATH, "getConstant", EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY);
+        assertEquals(100, ret);
+    }
+
+    /**
+     * Test calling a method with referenced type of parameters and returned values.
+     */
+    @Test
+    public void testPointAdd() {
+        String id = loadAndGetService(POINT_MATH);
+        Point ret = (Point) call(id, POINT_MATH, "add", POINT_MATH_ADD_PARAM_TYPES,
+                new Object[]{new Point(1, 1), new Point(2, 2)});
+        assertEquals(3, ret.x);
+        assertEquals(3, ret.y);
+    }
+
+    private static String loadAndGetService(String implementation) {
+        return EnclaveTestHelper.loadAndGetService(MATH_SERVICE, implementation, 3);
+    }
+
+    private static int callIntAdd(String id, int x, int y) {
+        return (Integer) call(id, NUMERIC_MATH, "add", MATH_ADD_PARAM_TYPES, new Object[]{x, y});
+    }
+
+    private static int callGetCounter(String id) {
+        return (Integer) call(id, NUMERIC_MATH, "getCounter", EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY);
+    }
+
+    private static Object call(String id, String className, String methodName, String[] paramTypes, Object[] paramValues) {
+        return EnclaveTestHelper.call(id, MATH_SERVICE, className, methodName, paramTypes, paramValues);
+    }
+
+    @AutomaticFeature
+    static class UTFeature implements Feature {
+
+        @Override
+        public void beforeAnalysis(BeforeAnalysisAccess access) {
+            RuntimeSerialization.register(Number.class, NoSuchAlgorithmException.class);
+        }
+    }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/SVMSimpleEnclaveCallTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/SVMSimpleEnclaveCallTest.java
new file mode 100644
index 0000000..af9732c
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/SVMSimpleEnclaveCallTest.java
@@ -0,0 +1,123 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationContext;
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+import com.alibaba.confidentialcomputing.common.SerializationHelper;
+import com.alibaba.confidentialcomputing.common.ServiceHandler;
+import com.alibaba.confidentialcomputing.enclave.c.EnclaveEnvironment.CallBacks;
+import com.alibaba.confidentialcomputing.enclave.c.EnclaveEnvironment.EncData;
+import org.graalvm.nativeimage.Isolate;
+import org.graalvm.nativeimage.IsolateThread;
+import org.graalvm.nativeimage.Isolates;
+import org.graalvm.nativeimage.StackValue;
+import org.graalvm.nativeimage.c.type.CTypeConversion;
+import org.graalvm.word.WordFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_ADD_PARAM_TYPES;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_SERVICE;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.NUMERIC_MATH;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.isInNativeImage;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class SVMSimpleEnclaveCallTest {
+    private IsolateThread isolateThread;
+
+    @BeforeEach
+    public void setup() {
+        if (isInNativeImage()) {
+            isolateThread = Isolates.createIsolate(Isolates.CreateIsolateParameters.getDefault());
+        }
+    }
+
+    @AfterEach
+    public void teardown() {
+        if (isInNativeImage()) {
+            Isolates.tearDownIsolate(isolateThread);
+        }
+    }
+
+    @Test
+    public void test() throws IOException, ClassNotFoundException {
+        if (!isInNativeImage()) {
+            return;
+        }
+        ServiceHandler[] serviceHandlers = callLoadService(MATH_SERVICE);
+        assertEquals(3, serviceHandlers.length);
+        assertEquals(NUMERIC_MATH, serviceHandlers[0].getServiceImplClassName());
+        String identity = serviceHandlers[0].getInstanceIdentity();
+        EnclaveInvocationResult result = callEnclaveMethod(MATH_SERVICE,
+                NUMERIC_MATH, identity,
+                "add",
+                MATH_ADD_PARAM_TYPES,
+                new Object[]{1, 2});
+        assertNotNull(result);
+        Object wrappedResult = result.getResult();
+        if (result.getException() != null) {
+            result.getException().printStackTrace();
+        }
+        assertNotNull(wrappedResult, "Expect to have non-null result from invoking service method call.");
+        assertNull(result.getException());
+        assertEquals(3, (Integer) wrappedResult);
+    }
+
+    private ServiceHandler[] callLoadService(String serviceName) throws IOException, ClassNotFoundException {
+        EnclaveInvocationResult ret = callEnclaveEntryPoint(serviceName,
+                (isolateThread, input, result, callbacks) -> EnclaveEntry.loadService(isolateThread, input, result, callbacks));
+        if (ret.getException() != null) {
+            ret.getException().printStackTrace();
+            fail();
+        }
+        return (ServiceHandler[]) ret.getResult();
+    }
+
+    private EnclaveInvocationResult callUnloadService(String serviceName) throws IOException, ClassNotFoundException {
+        return callEnclaveEntryPoint(serviceName,
+                (isolateThread, input, result, callbacks) -> EnclaveEntry.unloadService(isolateThread, input, result, callbacks));
+    }
+
+    private EnclaveInvocationResult callEnclaveMethod(String service, String impl, String identity, String method, String[] paramTypes, Object[] values) throws IOException, ClassNotFoundException {
+        EnclaveInvocationContext enclaveInvocationContext = new EnclaveInvocationContext(new ServiceHandler(service, impl
+                , identity), method, paramTypes, values);
+        return callEnclaveEntryPoint(enclaveInvocationContext,
+                (isolateThread, input, result, callbacks) -> EnclaveEntry.javaEnclaveInvoke(isolateThread, input, result, callbacks));
+    }
+
+    private EnclaveInvocationResult callEnclaveEntryPoint(Object input, Caller caller) throws IOException, ClassNotFoundException {
+        byte[] data = SerializationHelper.serialize(input);
+        EncData entryInput = StackValue.get(EncData.class);
+        EncData entryResult = StackValue.get(EncData.class);
+        try (CTypeConversion.CCharPointerHolder byteHolder = CTypeConversion.toCBytes(data)) {
+            entryInput.setData(byteHolder.get());
+            entryInput.setLen(data.length);
+        }
+        CallBacks callBacks = StackValue.get(CallBacks.class);
+        callBacks.setExceptionHandler(WordFactory.nullPointer());
+        callBacks.setMemCpyCCharPointerFunctionPointer(WordFactory.nullPointer());
+        int ret = caller.call(Isolates.getIsolate(isolateThread), entryInput, entryResult, callBacks);
+        if (ret == 0) {
+            return (EnclaveInvocationResult) SerializationHelper.deserialize(transformInput(entryResult));
+        } else {
+            throw new RuntimeException("Fail to execute enclave method, return value is " + ret);
+        }
+    }
+
+    @FunctionalInterface
+    private interface Caller {
+        int call(Isolate isolate, EncData input, EncData result, CallBacks callBacks);
+    }
+
+    private static byte[] transformInput(EncData input) {
+        int len = input.getLen();
+        byte[] data = new byte[len];
+        CTypeConversion.asByteBuffer(input.getData(), len).get(data);
+        return data;
+    }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/TestTarget.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/TestTarget.java
new file mode 100644
index 0000000..f604331
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/TestTarget.java
@@ -0,0 +1,12 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TestTarget {
+    Class<?> value();
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvokerTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvokerTest.java
index 7a7dc11..b0fea25 100644
--- a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvokerTest.java
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvokerTest.java
@@ -6,6 +6,7 @@ import com.alibaba.confidentialcomputing.common.ServiceHandler;
 import com.alibaba.confidentialcomputing.common.exception.ConfidentialComputingException;
 import com.alibaba.confidentialcomputing.enclave.testservice.MathService;
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -14,10 +15,12 @@ import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.EMPTY_
 import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_ADD_PARAM_TYPES;
 import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_SERVICE;
 import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.NUMERIC_MATH;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.isInNativeImage;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
 
 import java.lang.reflect.InvocationTargetException;
 
@@ -26,6 +29,11 @@ public class ServiceMethodInvokerTest {
     private static ServiceMethodInvoker serviceMethodInvoker = new ServiceMethodInvoker();
     private ServiceHandler[] services;
 
+    @BeforeAll
+    public static void svmCheck(){
+        assumeFalse(isInNativeImage());
+    }
+
     @BeforeEach
     public void setup() {
         services = EnclaveContext.getInstance().loadService(MathService.class);
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceOperationTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceOperationTest.java
index 726e8e5..1376e94 100644
--- a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceOperationTest.java
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceOperationTest.java
@@ -4,18 +4,26 @@ import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
 import com.alibaba.confidentialcomputing.common.ServiceHandler;
 import com.alibaba.confidentialcomputing.enclave.testservice.NumericMath;
 import com.alibaba.confidentialcomputing.enclave.testservice.PointMath;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_SERVICE;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.isInNativeImage;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
 
 /**
  * This class tests loading and unloading service.
  */
 public class ServiceOperationTest {
 
+    @BeforeAll
+    public static void svmCheck(){
+        assumeFalse(isInNativeImage());
+    }
+
     @Test
     public void testLoadingAndUnloading() {
         LoadServiceInvoker loadServiceInvoker = new LoadServiceInvoker();
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/MathService.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/MathService.java
index 9a01c09..8df60c8 100644
--- a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/MathService.java
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/MathService.java
@@ -1,5 +1,8 @@
 package com.alibaba.confidentialcomputing.enclave.testservice;
 
+import com.alibaba.confidentialcomputing.common.annotations.EnclaveService;
+
+@EnclaveService
 public interface MathService<T> {
     T add(T x, T y);
 
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/Point.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/Point.java
index f252d7f..31623be 100644
--- a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/Point.java
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/Point.java
@@ -1,8 +1,13 @@
 package com.alibaba.confidentialcomputing.enclave.testservice;
 
-public class Point {
-    int x;
-    int y;
+import java.io.Serializable;
+
+public class Point implements Serializable {
+
+    private static final long serialVersionUID = -3715916707782706029L;
+
+    public int x;
+    public int y;
 
     public Point(int x, int y){
         this.x = x;
diff --git a/sdk/enclave/src/test/resources/native/com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper.h b/sdk/enclave/src/test/resources/native/com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper.h
new file mode 100644
index 0000000..18b564b
--- /dev/null
+++ b/sdk/enclave/src/test/resources/native/com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper.h
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper */
+
+#ifndef _Included_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper
+#define _Included_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper
+ * Method:    invokeEnclave
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_invokeEnclave
+  (JNIEnv *, jclass, jbyteArray);
+
+/*
+ * Class:     com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper
+ * Method:    loadService
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_loadService
+  (JNIEnv *, jclass, jbyteArray);
+
+/*
+ * Class:     com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper
+ * Method:    unloadService
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_unloadService
+  (JNIEnv *, jclass, jbyteArray);
+
+/*
+ * Class:     com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper
+ * Method:    createIsolate
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_createIsolate
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper
+ * Method:    destroyIsolate
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_destroyIsolate
+  (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/sdk/enclave/src/test/resources/native/enc_invoke_entry_test.c b/sdk/enclave/src/test/resources/native/enc_invoke_entry_test.c
new file mode 100644
index 0000000..8faa6f1
--- /dev/null
+++ b/sdk/enclave/src/test/resources/native/enc_invoke_entry_test.c
@@ -0,0 +1,76 @@
+#include <stdlib.h>
+#include <string.h>
+#include "com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper.h"
+#include "enc_environment.h"
+#ifdef MUSL
+#include "libmusl_svmenclavesdk.h"
+#else
+#include "libsvm_enclave_sdk.h"
+#endif
+
+typedef int (*enclave_invoke)(graal_isolate_t* isolate, enc_data* input, enc_data* result, callbacks* callBacks);
+
+ char* memcpy_char_pointer(char* src, int len){
+    int size = sizeof(char);
+    char *dest = (char*)malloc(len*size);
+    memcpy(dest, src, len);
+    return dest;
+ }
+
+static graal_isolatethread_t *thread = NULL;
+static graal_isolate_t *isolate = NULL;
+
+jbyteArray enclave_call(JNIEnv* env, jclass clazz, jbyteArray data, enclave_invoke invoke){
+jboolean isCopy;
+	jbyte* a = (*env)->GetByteArrayElements(env, data, &isCopy);
+	enc_data invoke_data;
+	invoke_data.data=(char*)a;
+	invoke_data.data_len=(*env)->GetArrayLength(env, data);
+
+    callbacks callback_methods;
+    callback_methods.memcpy_char_pointer=&memcpy_char_pointer;
+    callback_methods.exception_handler=NULL; // Must explicitly set
+
+    enc_data ret;
+    int exit_code = invoke(isolate, &invoke_data, &ret, &callback_methods);
+    jbyteArray retVal;
+    if(exit_code == 0 ){
+        retVal = (*env)->NewByteArray(env, ret.data_len);
+        jbyte *buf = (*env)->GetByteArrayElements(env, retVal, NULL);
+        memcpy(buf, ret.data, ret.data_len);
+        (*env)->ReleaseByteArrayElements(env, retVal, buf, 0);
+        //printf("Returned type is %.*s\n", ret.verify_info_len, ret.verify_info);
+        free(ret.data);
+    }else{
+        retVal = NULL;
+    }
+	return retVal;
+}
+
+JNIEXPORT void JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_createIsolate
+   (JNIEnv *env, jclass clazz){
+       if (graal_create_isolate(NULL, &isolate, &thread) != 0) {
+         fprintf(stderr, "error on isolate creation or attach\n");
+       }
+}
+
+JNIEXPORT void JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_destroyIsolate
+     (JNIEnv *env, jclass clazz){
+     //graal_tear_down_isolate(thread);
+     graal_detach_all_threads_and_tear_down_isolate(thread);
+}
+
+JNIEXPORT jbyteArray JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_loadService
+(JNIEnv* env, jclass clazz, jbyteArray data) {
+    return enclave_call(env, clazz, data, java_loadservice_invoke);
+}
+
+JNIEXPORT jbyteArray JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_unloadService
+  (JNIEnv* env, jclass clazz, jbyteArray data){
+  return enclave_call(env, clazz, data, java_unloadservice_invoke);
+}
+
+JNIEXPORT jbyteArray JNICALL Java_com_alibaba_confidentialcomputing_enclave_EnclaveTestHelper_invokeEnclave
+(JNIEnv* env, jclass clazz, jbyteArray data) {
+    return enclave_call(env, clazz, data, java_enclave_invoke);
+}
diff --git a/tools/cicd/Dockerfile b/tools/cicd/Dockerfile
index c3a7c4d..f988bd3 100644
--- a/tools/cicd/Dockerfile
+++ b/tools/cicd/Dockerfile
@@ -7,6 +7,9 @@ ENV DEBIAN_FRONTEND noninteractive
 
 # install openjdk11 and maven.
 RUN apt-get update && \
-    echo -e 'yes\n' | apt-get install -y openjdk-11-jdk && \
     echo -e 'yes\n' | apt-get install -y maven && \
+    echo -e 'yes\n' | apt-get install -y build-essential libz-dev zlib1g-dev && \
     echo -e 'yes\n' | apt-get install -y wget
+ADD ["graalvm-enclave-22.0.0.tar", "/root/tools/"]
+ENV GRAALVM_HOME "/root/tools/graalvm-enclave-22.0.0"
+ENV JAVA_HOME "/root/tools/graalvm-enclave-22.0.0"
diff --git a/tools/cicd/make.sh b/tools/cicd/make.sh
index 0120911..04657d5 100755
--- a/tools/cicd/make.sh
+++ b/tools/cicd/make.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 BUILD_IMAGE=javaenclave_build
-BUILD_TAG=v0.1.1
+BUILD_TAG=v0.1.2
 
 SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
 
@@ -11,7 +11,11 @@ WORKDIR=$(dirname $(dirname "$PWD"))
 
 # check target images exist or not, build it if not.
 if [[ "$(docker images -q ${BUILD_IMAGE}:${BUILD_TAG} 2> /dev/null)" == "" ]]; then
+  # Get the customized Graal VM from git@gitlab.alibaba-inc.com:graal/SGXGraalVM.git
+  # This should be replaced to the offical version when all patches are accepted by the Graal community
+  wget https://graal.oss-cn-beijing.aliyuncs.com/graal-enclave/JDK11-22.0.0/graalvm-enclave-22.0.0.tar
   docker build -t ${BUILD_IMAGE}:${BUILD_TAG} .
+  rm -f graalvm-enclave-22.0.0.tar
 fi
 
 # test JavaEnclave's unit test cases and samples
@@ -19,3 +23,4 @@ docker run -i --rm --privileged --network host                    \
 -w "${WORKDIR}"                                                   \
 -v "${HOME}"/.m2:/root/.m2 -v "${WORKDIR}":"${WORKDIR}"           \
 ${BUILD_IMAGE}:${BUILD_TAG} /bin/bash build.sh
+


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org