本文作者:邱朋飞
这篇文章主要讲述获得Enclave函数执行后的返回值。以一个累加函数的返回值为例。
安装完SGX SDK后,我们可以在输入的安装目录下的/sgxsdk/SampleCode文件夹下找到Intel提供的SGX应用程序示例,共有6个例子,根据名字可以大概判断是做什么的,每个示例中的README.txt文件给出了示例的详细信息。6个示例如下所示:
SampleEnclave示例实现了安全printf函数,可以被用来安全打印我们需要的字符串。我们把SampleEnclave示例复制到自己的文件夹中,基于该示例,我们开发一个使用Enclave做累加运算的函数,并获得函数的返回值。我们把复制得到的SampleEnclave文件夹名称改成RetSum文件夹。下左1图展示了项目的目录结构:
2. 删除一些不需要的文件
其中.settings是隐藏文件夹,里面的language.settings.xml定义了一些与语言相关的信息,我们不用管它。.cproject和.project是隐藏文件,我们也不用管它们。README.txt没有用,我们可以删除README.txt。App文件夹下的Edger8rSyntax和TrustedLibrary文件夹及其中的文件在这个项目中是没有用的,我们把它们删除,同理Enclave文件夹下的Edger8rSyntax和TrustedLibrary文件夹在这个项目中也是没有用的,我们把它们删除。最后项目的目录结构如上左2图所示。
3. 项目各个部分内容简介
3.1 App文件夹
该文件夹存放应用程序中的不可信代码部分,共有2个文件,App.cpp文件和App.h文件
3.1.1 App.cpp文件:该文件是应用程序中的不可信部分代码,其中包括了创建Enclave及销毁Enclave的代码,也定义了一些相关的返回码供使用者查看Enclave程序的执行状态。其中的main函数是整个项目的入口函数。是我们要修改的文件。
3.1.2 App.h文件:该文件是应用程序中的不可信部分代码的头文件,定义了一些宏常量和函数声明。是我们要修改的文件。
3.2 Enclave文件夹
该文件夹存放应用程序中的可信代码部分和可信与不可信代码接口文件,共有6个文件,Enclave.config.xml文件、Enclave.cpp文件、Enclave.h文件、Enclave.edl文件、Enclave.lds文件和Enclave_private.pem文件
3.2.1 Enclave.config.xml文件:该文件是Enclave的配置文件,定义了Enclave的元数据信息,我们可以不用更改。
3.2.2 Enclave.cpp文件:该文件是应用程序中的可信部分代码,包括了可信函数的实现,是我们要修改的文件。
3.2.3 Enclave.h文件:该文件是应用程序中的可信部分代码的头文件,定义了一些宏常量和函数声明。是我们要修改的文件。
3.2.4 Enclave.edl文件:该文件是Enclave的接口定义文件,定义了不可信代码调用可信代码的ECALL函数接口和可信代码调用不可信代码的OECALL函数接口,trusted{}中定义了ECALL函数接口,untrusted{}中定义了OECALL函数接口是我们要修改的文件。
3.2.5 Enclave.lds文件:该文件定义了一些Enclave可执行文件信息,我们不用更改。
3.2.6 Enclave_private.pem文件:该文件是SGX生成的私钥,我们不用更改。
3.3 Include文件夹
该文件夹存放被Enclave接口定义文件Enclave.edl使用的头文件,包括一些宏定义,我们不用管它。共有一个文件,user_types.h文件。
3.3.1 user_types.h文件:该文件定义了用户自定义的类型。
3.4 Makefile文件
该文件是项目的编译文件,定义了项目的编译信息,详细说明还请参考http://blog.csdn.net/daichunkai123/article/details/78027895。是需要修改的部分。
4 编写我们的代码
我们要修改的文件包括:Enclave.edl文件、Enclave.cpp文件、Enclave.h文件、App.cpp文件、App.h文件和Makefile文件。
4.1 Enclave.edl文件修改:
我们要在Enclave.edl文件中定义不可信代码调用可信函数的接口,比如我们定义的可信函数是MyAccum,在该函数中我们计算两个整数之间的所有整数的累加和(包括这两个整数),该函数有2个参数:起始整数start和结束整数end。该函数有一个整数返回值。
4.1.1 由于我们不需要OCALL接口,也就是可信函数没有调用不可信函数,我们把untrusted{}中的”void ocall_print_string([in, string] const char *str);“删除,此时untrusted{}中是空的。
4.1.2 由于我们不需要Edger8rSyntax和TrustedLibrary,所以我们把与这两部分相关的函数导入删除,也就是把下面部分删除:
from "Edger8rSyntax/Types.edl" import *; from "Edger8rSyntax/Pointers.edl" import *; from "Edger8rSyntax/Arrays.edl" import *; from "Edger8rSyntax/Functions.edl" import *; from "TrustedLibrary/Libc.edl" import *; from "TrustedLibrary/Libcxx.edl" import ecall_exception, ecall_map; from "TrustedLibrary/Thread.edl" import *;
4.1.3 我们添加我们的ECALL,我们将以下代码添加到Enclave.edl文件中的untrusted{}之前。参数start、end和返回值的数据类型都是size_t。
trusted{ public size_t MyAccum(size_t start, size_t end); };
4.1.4 修改后的Enclave.edl文件如下所示
enclave { include "user_types.h" /* buffer_t */ trusted{ public size_t MyAccum(size_t start, size_t end); }; untrusted { }; };
4.2 Enclave.cpp文件修改
我们在Enclave.cpp文件中实现MyAccum函数,该函数计算两个整数之间的所有整数的累加和(包括这两个整数),并返回计算的累加值。
4.2.1 由于不需要”void printf(const char *fmt, ...)“函数,故删除Enclave.cpp文件中的”void printf(const char *fmt, ...)“函数,并将以下代码添加到Enclave.cpp文件中
size_t MyAccum(size_t start, size_t end){ size_t sum = 0; for (; start <= end; start++) sum += start; return sum; }
4.2.3 修改后的Enclave.cpp文件如下所示
#include
#include /* vsnprintf */ #include "Enclave.h" #include "Enclave_t.h" /* print_string */ size_t MyAccum(size_t start, size_t end){ size_t sum = 0; for (; start <= end; start++) sum += start; return sum; } 4.3 Enclave.h文件修改
我们在Enclave.h文件中添加MyAccum函数声明,该文件修改比较简单。
4.3.1 删除Enclave.h文件中的”void printf(const char *fmt, ...);“函数声明,并将以下代码添加到Enclave.h文件中
size_t MyAccum(size_t start, size_t end);
4.3.2 修改后的Enclave.h文件如下所示
#ifndef _ENCLAVE_H_ #define _ENCLAVE_H_ #include
#include #if defined(__cplusplus) extern "C" { #endif size_t MyAccum(size_t start, size_t end); #if defined(__cplusplus) } #endif #endif /* !_ENCLAVE_H_ */ 4.4 App.cpp文件修改
我们在该文件中调用自定义的ECALL函数MyAccum计算两个整数之间的所有整数的累加和,并将计算结果存储到一个变量中。该文件修改比较简单
4.4.1 由于我们不需要Edger8rSyntax和TrustedLibrary,所以我们把main函数中与这两部分相关的函数调用删除,也就是把下面部分删除:
/* Utilize edger8r attributes */ edger8r_array_attributes(); edger8r_pointer_attributes(); edger8r_type_attributes(); edger8r_function_attributes(); /* Utilize trusted libraries */ ecall_libc_functions(); ecall_libcxx_functions(); ecall_thread_functions();
4.4.2 我们在main定义两个整数start和end,并定义存储返回值的变量sum
size_t start = 5; size_t end = 10; size_t sum = 0;
4.4.3 我们在main函数中的Enclave创建和销毁之间插入我们定义的MyAccum函数的调用代码,也插入打印累加和的代码,也就是把以下代码放到”sgx_destroy_enclave(global_eid);“之前。global_eid参数是必须的,是enclave的id。这个地方需要注意的是:获得Enclave函数的返回值和获得普通的C/C++函数的返回值是不一样的,获得C/C++函数的返回值可以直接通过赋值得到,但是获得Enclave函数的返回值需要把存储返回值的变量的地址传给Enclave函数,Enlave函数执行完之后,SGX会把返回值复制到地址参数指向的内存地址处,从而将返回值返回给想返回的变量。事实上,Enclave函数执行时是有返回值的,返回的是该函数执行的状态,可以通过赋值获得Enclave函数执行的状态,从而判断有没有正确执行。
MyAccum(global_eid, &sum, start, end); printf("%d\n", sum);
4.4.4 我们也可以将main函数后面的无关代码给删了,也就是删除以下代码。
printf("Info: SampleEnclave successfully returned.\n"); printf("Enter a character before exit ...\n"); getchar();
4.4.5 修改后的main函数如下所示
int SGX_CDECL main(int argc, char *argv[]) { (void)(argc); (void)(argv); size_t start = 5; size_t end = 10; size_t sum = 0; /* Initialize the enclave */ if(initialize_enclave() < 0){ printf("Enter a character before exit ...\n"); getchar(); return -1; } MyAccum(global_eid, &sum, start, end); printf("%d\n", sum); /* Destroy the enclave */ sgx_destroy_enclave(global_eid); return 0; }
4.5 App.h文件修改
该文件修改比较简单。
4.3.1 删除App.h文件中的与Edger8rSyntax和TrustedLibrary相关的函数声明,也就是将一下内容删除
void edger8r_array_attributes(void); void edger8r_type_attributes(void); void edger8r_pointer_attributes(void); void edger8r_function_attributes(void); void ecall_libc_functions(void); void ecall_libcxx_functions(void); void ecall_thread_functions(void);
4.3.2 修改后的App.h文件如下所示
#ifndef _APP_H_ #define _APP_H_ #include
#include #include #include #include "sgx_error.h" /* sgx_status_t */ #include "sgx_eid.h" /* sgx_enclave_id_t */ #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif # define TOKEN_FILENAME "enclave.token" # define ENCLAVE_FILENAME "enclave.signed.so" extern sgx_enclave_id_t global_eid; /* global enclave id */ #if defined(__cplusplus) extern "C" { #endif #if defined(__cplusplus) } #endif #endif /* !_APP_H_ */ 4.6 Makefile文件修改
我们在Makefile文件中修改编译相关的信息。主要更改”SGX_SDK ?= /opt/intel/sgxsdk“、”App_Cpp_Files := App/App.cpp $(wildcard App/Edger8rSyntax/*.cpp) $(wildcard App/TrustedLibrary/*.cpp)“和”Enclave_Cpp_Files := Enclave/Enclave.cpp $(wildcard Enclave/Edger8rSyntax/*.cpp) $(wildcard Enclave/TrustedLibrary/*.cpp)“
4.6.1 修改SGX SDK路径
如果我们的SGX SDK路径不是/opt/intel/sgxsdk,我们要进行修改,我的是/home/SGXSDK/sgxsdk,所以要将其改成/home/SGXSDK/sgxsdk,结果如下
SGX_SDK ?= /home/SGXSDK/sgxsdk
4.6.2 修改不可信代码编译的源文件
由于我们不需要Edger8rSyntax和TrustedLibrary,所以我们要把App_Cpp_Files中的编译源文件中的Edger8rSyntax和TrustedLibrary删除,由于我们的不可信代码可能是多个文件,因此,我们最好在App_Cpp_Files中包括App文件夹中的所有cpp文件,更改后的结果如下
App_Cpp_Files := $(wildcard App/*.cpp)
4.6.3 修改可信代码编译的源文件
由于我们不需要Edger8rSyntax和TrustedLibrary,所以我们要把Enclave_Cpp_Files中的编译源文件中的Edger8rSyntax和TrustedLibrary删除,由于我们的可信代码可能是多个文件,因此,我们最好在Enclave_Cpp_Files中包括App文件夹中的所有cpp文件,更改后的结果如下
Enclave_Cpp_Files := $(wildcard Enclave/*.cpp)
5. 编译修改后的项目文件
打开终端,进入到项目文件夹中,输入make进行编译,如下所示
6. 执行程序
在终端中输入./app,可以看到输出了45,说明我们正确编写了程序。