【SGX系列教程】(三)Intel-SGX 官方示例分析(SampleCode)——SampleEnclave

文章目錄

  • 一. 引言
  • 二. README
    • 2.1 項目目的
    • 2.2 構建和執行示例代碼的步驟
    • 2.3 配置參數解釋
    • 2.4 配置文件分析
    • 2.5 啟動令牌初始化
  • 三. 重點代碼分析
    • 3.1 App文件夾
      • 3.1.1 App/App.cpp
      • 3.1.2 App/Edger8rSyntax文件夾
        • 3.1.2.1 App/Edger8rSyntax/Arrays.cpp
        • 3.1.2.2 App/Edger8rSyntax/Functions.cpp
        • 3.1.2.3 App/Edger8rSyntax/Pointers.cpp
        • 3.1.2.4 App/Edger8rSyntax/Types.cpp
      • 3.1.3 App/TrustedLibrary文件夾
        • 3.1.3.1 App/TrustedLibrary/Libc.cpp
        • 3.1.3.2 App/TrustedLibrary/Libcxx.cpp
        • 3.1.3.3 App/TrustedLibrary/Thread.cpp
    • 3.2 Enclave文件夾
      • 3.2.1 Enclave/Enclave.cpp
      • 3.2.2 Enclave/Edger8rSyntax文件夾
        • 3.2.2.1 Enclave/Edger8rSyntax/Arrays.cpp
        • 3.2.2.2 Enclave/Edger8rSyntax/Functions.cpp
        • 3.2.2.3 Enclave/Edger8rSyntax/Pointers.cpp
        • 3.2.2.4 Enclave/Edger8rSyntax/Types.cpp
      • 3.2.3 Enclave/TrustedLibrary文件夾
        • 3.2.3.1 Enclave/TrustedLibrary/Libc.cpp
        • 3.2.3.2 Enclave/TrustedLibrary/Libcxx.cpp
        • 3.2.3.3 Enclave/TrustedLibrary/Thread.cpp
  • 四. 感謝支持

一. 引言

? ? SampleEnclave作為enclave開發的基礎示例,主要包括enclave的一些基礎用法介紹,本文將結合這個示例從中學習SGX的基本使用方法。關于 SGX 開發運行環境的搭建可參考之前的一篇博客:【SGX系列教程】(一)。

二. README

------------------------
Purpose of SampleEnclave
------------------------
The project demonstrates several fundamental usages of Intel(R) Software Guard 
Extensions (Intel(R) SGX) SDK:
- Initializing and destroying an enclave
- Creating ECALLs or OCALLs
- Calling trusted libraries inside the enclave------------------------------------
How to Build/Execute the Sample Code
------------------------------------
1. Install Intel(R) SGX SDK for Linux* OS
2. Enclave test key(two options):a. Install openssl first, then the project will generate a test key<Enclave_private_test.pem> automatically when you build the project.b. Rename your test key(3072-bit RSA private key) to <Enclave_private_test.pem> and put it under the <Enclave> folder.
3. Make sure your environment is set:$ source ${sgx-sdk-install-path}/environment
4. Build the project with the prepared Makefile:a. Hardware Mode, Debug build:1) Enclave with no mitigation:$ make2) Enclave with mitigations for indirects and returns only:$ make MITIGATION-CVE-2020-0551=CF3) Enclave with full mitigation:$ make MITIGATION-CVE-2020-0551=LOADb. Hardware Mode, Pre-release build:1) Enclave with no mitigation:$ make SGX_PRERELEASE=1 SGX_DEBUG=02) Enclave with mitigations for indirects and returns only:$ make SGX_PRERELEASE=1 SGX_DEBUG=0 MITIGATION-CVE-2020-0551=CF3) Enclave with full mitigation:$ make SGX_PRERELEASE=1 SGX_DEBUG=0 MITIGATION-CVE-2020-0551=LOADc. Hardware Mode, Release build:1) Enclave with no mitigation:$ make SGX_DEBUG=02) Enclave with mitigations for indirects and returns only:$ make SGX_DEBUG=0 MITIGATION-CVE-2020-0551=CF3) Enclave with full mitigation:$ make SGX_DEBUG=0 MITIGATION-CVE-2020-0551=LOADd. Simulation Mode, Debug build:$ make SGX_MODE=SIMe. Simulation Mode, Pre-release build:$ make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0f. Simulation Mode, Release build:$ make SGX_MODE=SIM SGX_DEBUG=0
5. Execute the binary directly:$ ./app
6. Remember to "make clean" before switching build mode------------------------------------------
Explanation about Configuration Parameters
------------------------------------------
TCSMaxNum, TCSNum, TCSMinPoolThese three parameters will determine whether a thread will be createddynamically  when there is no available thread to do the work.StackMaxSize, StackMinSizeFor a dynamically created thread, StackMinSize is the amount of stack availableonce the thread is created and StackMaxSize is the total amount of stack thatthread can use. The gap between StackMinSize and StackMaxSize is the stackdynamically expanded as necessary at runtime.For a static thread, only StackMaxSize is relevant which specifies the totalamount of stack available to the thread.HeapMaxSize, HeapInitSize, HeapMinSizeHeapMinSize is the amount of heap available once the enclave is initialized.HeapMaxSize is the total amount of heap an enclave can use. The gap betweenHeapMinSize and HeapMaxSize is the heap dynamically expanded as necessaryat runtime.HeapInitSize is here for compatibility.-------------------------------------------------    
Sample configuration files for the Sample Enclave
-------------------------------------------------
With below configurations, if the signed enclave is launched on a SGX2 platform
with SGX2 supported kernel, it will be loaded with EDMM enabled. Otherwise, it
will behave in way of SGX1.config.01.xml: There is no dynamic thread, no dynamic heap expansion.config.02.xml: There is no dynamic thread. But dynamic heap expansion can happen.config.03.xml: There are dynamic threads. For a dynamic thread, there's no stack expansion.config.04.xml: There are dynamic threads. For a dynamic thread, stack will expanded as necessary.Below configuration is only workable on a SGX2 platform with SGX2 supported kernel:config.05.xml: There is a user region where users could operate on.-------------------------------------------------
Launch token initialization
-------------------------------------------------
If using libsgx-enclave-common or sgxpsw under version 2.4, an initialized variable launch_token needs to be passed as the 3rd parameter of API sgx_create_enclave. For example,sgx_launch_token_t launch_token = {0};
sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, launch_token, NULL, &global_eid, NULL);

? ? 根據上面的README,我們可以分析出SampleEnclave項目的目的和構建/執行步驟,有助于理解項目的實現流程。

2.1 項目目的

? ? SampleEnclave項目演示了Intel? SGX SDK的一些基本使用方法,其主要目的包括:

  1. 初始化銷毀enclave;
  2. 創建ECALLs(Enclave Calls)OCALLs(Outside Calls);
  3. 調用enclave內的受信任庫

2.2 構建和執行示例代碼的步驟

  1. 安裝Intel? SGX SDK for Linux OS
    這是項目構建和運行的基本前提,需要確保已正確安裝SGX SDK,具體安裝教程參考第一篇博客。
  2. 準備Enclave測試密鑰(兩種方式)
    a. 安裝openssl,然后在構建項目時會自動生成一個測試密鑰Enclave_private_test.pem
    b. 使用你自己的測試密鑰(3072位RSA私鑰),將其重命名為Enclave_private_test.pem并放入文件夾中。
  3. 設置環境變量: source ${sgx-sdk-install-path}/environment
  4. 構建項目,根據不同的模式配置進行構建,具體參考上述README。
  5. 直接執行生成的二進制文件
    ./app
  6. 切換構建模式前請記得清理構建
    make clean

2.3 配置參數解釋

  1. 線程配置參數
    TCSMaxNum, TCSNum, TCSMinPool:這三個參數決定是否在沒有可用線程工作時動態創建一個線程。
  2. 配置參數
    StackMaxSize, StackMinSize:對于動態創建的線程,StackMinSize 是線程創建時的棧大小,StackMaxSize 是線程可以使用的最大棧空間。對于靜態線程,只需要設置StackMaxSize
  3. 配置參數
    HeapMaxSize, HeapInitSize,HeapMinSize:HeapMinSize 是enclave初始化時的堆大小,HeapMaxSize 是enclave可以使用的最大堆空間,HeapInitSize 是為了兼容而設置。

2.4 配置文件分析

? ? 對于不同的SGX平臺(SGX1和SGX2)和配置,enclave的行為會有所不同:

  1. SGX1平臺的行為:
    config.01.xml:無動態線程,無動態堆擴展。
    config.02.xml:無動態線程,但動態堆擴展可以發生。
    config.03.xml:有動態線程,但沒有棧擴展。
    config.04.xml:有動態線程,棧可以根據需要擴展。
  2. 僅在SGX2平臺上有效的配置:
    config.05.xml:有一個用戶區域,用戶可以對其進行操作。

2.5 啟動令牌初始化

? ? 如果使用版本低于2.4(最新安裝的都不會)的libsgx-enclave-commonsgxpsw,需要傳遞一個初始化的啟動令牌變量作為第三個參數給API sgx_create_enclave,例如:

sgx_launch_token_t launch_token = {0};
sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, launch_token, NULL, &global_eid, NULL);

總結
? ? SampleEnclave項目展示了如何使用Intel? SGX SDK進行基本的enclave操作,包括初始化和銷毀enclave創建ECALL和OCALL、以及調用enclave內的受信任庫。通過詳細的構建和執行步驟、配置參數解釋及各種配置文件的分析,可以幫助用戶更好地理解SGX的基本使用方法和配置技巧。

三. 重點代碼分析

? ?文件目錄如下圖,App側與Enclave側的程序一一對應,其中App表示REE側應用程序,Enclave表示enclave側Ecall程序。
在這里插入圖片描述

3.1 App文件夾

3.1.1 App/App.cpp

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pwd.h>
#define MAX_PATH FILENAME_MAX#include "sgx_urts.h"       // 包含SGX User Runtime庫
#include "App.h"            // 包含應用程序自定義頭文件
#include "Enclave_u.h"      // 包含Enclave生成的頭文件,用于與Enclave進行交互/* 全局EID由多個線程共享 */
sgx_enclave_id_t global_eid = 0;/* 定義一個錯誤列表,用于記錄sgx_create_enclave返回的錯誤碼 */
typedef struct _sgx_errlist_t {sgx_status_t err;const char *msg; // 錯誤信息const char *sug; // 建議
} sgx_errlist_t;/* sgx_create_enclave可能返回的錯誤碼及其對應信息 */
static sgx_errlist_t sgx_errlist[] = {{ SGX_ERROR_UNEXPECTED, "Unexpected error occurred.", NULL },{ SGX_ERROR_INVALID_PARAMETER, "Invalid parameter.", NULL },{ SGX_ERROR_OUT_OF_MEMORY, "Out of memory.", NULL },{ SGX_ERROR_ENCLAVE_LOST, "Power transition occurred.", "Please refer to the sample \"PowerTransition\" for details." },{ SGX_ERROR_INVALID_ENCLAVE, "Invalid enclave image.", NULL },{ SGX_ERROR_INVALID_ENCLAVE_ID, "Invalid enclave identification.", NULL },{ SGX_ERROR_INVALID_SIGNATURE, "Invalid enclave signature.", NULL },{ SGX_ERROR_OUT_OF_EPC, "Out of EPC memory.", NULL },{ SGX_ERROR_NO_DEVICE, "Invalid SGX device.", "Please make sure SGX module is enabled in the BIOS, and install SGX driver afterwards." },{ SGX_ERROR_MEMORY_MAP_CONFLICT, "Memory map conflicted.", NULL },{ SGX_ERROR_INVALID_METADATA, "Invalid enclave metadata.", NULL },{ SGX_ERROR_DEVICE_BUSY, "SGX device was busy.", NULL },{ SGX_ERROR_INVALID_VERSION, "Enclave version was invalid.", NULL },{ SGX_ERROR_INVALID_ATTRIBUTE, "Enclave was not authorized.", NULL },{ SGX_ERROR_ENCLAVE_FILE_ACCESS, "Can't open enclave file.", NULL },{ SGX_ERROR_MEMORY_MAP_FAILURE, "Failed to reserve memory for the enclave.", NULL },
};/* 檢查加載enclave的錯誤條件 */
void print_error_message(sgx_status_t ret)
{size_t idx = 0;size_t ttl = sizeof sgx_errlist/sizeof sgx_errlist[0];for (idx = 0; idx < ttl; idx++) {if(ret == sgx_errlist[idx].err) {if(NULL != sgx_errlist[idx].sug)printf("信息: %s\n", sgx_errlist[idx].sug);printf("錯誤: %s\n", sgx_errlist[idx].msg);break;}}if (idx == ttl)printf("錯誤代碼是 0x%X. 請參考 \"Intel SGX SDK Developer Reference\" 獲取更多詳情。\n", ret);
}/* 初始化enclave:* 調用sgx_create_enclave來初始化一個enclave實例*/
int initialize_enclave(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;/* 調用sgx_create_enclave來初始化一個enclave實例 *//* 調試支持: 將第二個參數設置為1 */ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);if (ret != SGX_SUCCESS) {print_error_message(ret);return -1;}return 0;
}/* OCall函數 */
void ocall_print_string(const char *str)
{/* 代理將會檢查字符串的長度并在結尾添加空字符,防止緩沖區溢出 */printf("%s", str);
}/* 應用程序入口 */
int SGX_CDECL main(int argc, char *argv[])
{(void)(argc);(void)(argv);/* 初始化enclave */if(initialize_enclave() < 0){printf("在退出前按下任意鍵 ...\n");getchar();return -1; }/* 使用edger8r生成的屬性調用 */edger8r_array_attributes();edger8r_pointer_attributes();edger8r_type_attributes();edger8r_function_attributes();/* 使用受信任的庫函數 */ecall_libc_functions();ecall_libcxx_functions();ecall_thread_functions();/* 銷毀enclave */sgx_destroy_enclave(global_eid);printf("信息: SampleEnclave成功返回。\n");printf("在退出前按下任意鍵 ...\n");getchar();return 0;
}

代碼功能分析

  1. 初始化與配置
    頭文件與庫的引用:
    #include “sgx_urts.h”:包含SGX User Runtime庫的頭文件。
    #include “App.h”:包含應用程序自定義頭文件。
    #include “Enclave_u.h”:包含enclave生成的頭文件,用于與enclave進行交互。
    全局變量:
    sgx_enclave_id_t global_eid:用于存儲enclave的全局ID,由多個線程共享。
    錯誤處理:
    sgx_errlist_t:定義錯誤列表結構,用于存儲錯誤碼、錯誤信息和建議。
    sgx_errlist:包含sgx_create_enclave可能返回的錯誤碼及其對應的錯誤信息和建議。
    print_error_message:根據錯誤碼打印相應的錯誤信息和建議。
  2. 初始化enclave
    initialize_enclave:
    調用 sgx_create_enclave 函數創建enclave實例,并將得到的enclave ID存儲在 global_eid 中。
    如果創建enclave失敗,則調用 print_error_message 打印錯誤信息,并返回-1。
  3. OCall函數
    ocall_print_string:
    將傳入的字符串打印到標準輸出。(在OCall過程中,代理會檢查字符串長度并在字符串結尾添加空字符,以防止緩沖區溢出)。
  4. 應用程序入口
    main函數:
    初始化enclave:調用 initialize_enclave 函數初始化enclave。如果失敗則打印錯誤信息并等待用戶輸入。
    調用edger8r生成的屬性和函數:
    edger8r_array_attributes
    edger8r_pointer_attributes
    edger8r_type_attributes
    edger8r_function_attributes
    調用受信任的庫函數:
    ecall_libc_functions
    ecall_libcxx_functions
    ecall_thread_functions
    銷毀enclave:調用 sgx_destroy_enclave 函數銷毀enclave實例。
    程序結束提示:顯示提示信息,用戶按任意鍵后退出程序。

總結
? ? 通過這段代碼,實現了SampleEnclave項目的核心功能,包括初始化和銷毀enclave調用ECALL和OCALL、以及使用enclave內的受信任庫。每一步操作都有詳細的錯誤處理和用戶提示,確保了代碼的魯棒性和易用性。

3.1.2 App/Edger8rSyntax文件夾

? ? Edger8r是SGX的一部分,是可信和不可信部分的邊界層,用來提供一些在不可信的應用和enclave之間的一些邊界路徑。Edger8r在編譯的時候自動執行但是一些高級的enclave開發人員會手動調用Edger8r。

3.1.2.1 App/Edger8rSyntax/Arrays.cpp
#include "../App.h"       // 包含應用程序的頭文件
#include "Enclave_u.h"    // 包含Enclave生成的頭文件,用于與Enclave進行交互/* edger8r_array_attributes:* 調用聲明了數組屬性的ECALL*/
void edger8r_array_attributes(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;/* user_check */int arr1[4] = {0, 1, 2, 3};  // 初始化數組arr1ret = ecall_array_user_check(global_eid, arr1); // 調用ecall_array_user_check函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序/* 確認arr1被修改 */for (int i = 0; i < 4; i++)assert(arr1[i] == (3 - i)); // 檢查arr1的每個元素是否變成3-i/* in */int arr2[4] = {0, 1, 2, 3};  // 初始化數組arr2ret = ecall_array_in(global_eid, arr2); // 調用ecall_array_in函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序/* 確認arr2未被修改 */for (int i = 0; i < 4; i++)assert(arr2[i] == i); // 檢查arr2的每個元素是否未改變/* out */int arr3[4] = {0, 1, 2, 3};  // 初始化數組arr3ret = ecall_array_out(global_eid, arr3); // 調用ecall_array_out函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序/* 確認arr3被修改 */for (int i = 0; i < 4; i++)assert(arr3[i] == (3 - i)); // 檢查arr3的每個元素是否變成3-i/* in, out */int arr4[4] = {0, 1, 2, 3};  // 初始化數組arr4ret = ecall_array_in_out(global_eid, arr4); // 調用ecall_array_in_out函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序/* 確認arr4被修改 */for (int i = 0; i < 4; i++)assert(arr4[i] == (3 - i)); // 檢查arr4的每個元素是否變成3-i/* isary */array_t arr5 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};  // 初始化數組arr5ret = ecall_array_isary(global_eid, arr5); // 調用ecall_array_isary函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序/* 確認arr5被修改 */for (int i = 0; i < 10; i++)assert(arr5[i] == (9 - i)); // 檢查arr5的每個元素是否變成9-i
}

? ? 該函數 edger8r_array_attributes 展示了如何通過SGX ECALL調用將數組傳遞給enclave進行處理,并在處理后檢查數組是否按照預期進行了修改。函數中分別展示了四種不同的數組傳遞方式(user_check, in, out, in_out),并通過assert語句對修改后的數組進行驗證。此示例演示了如何在SGX環境中進行數組相關的ECALL調用和數據驗證。

3.1.2.2 App/Edger8rSyntax/Functions.cpp
#include "../App.h"       // 包含應用程序的頭文件
#include "Enclave_u.h"    // 包含Enclave生成的頭文件,用于與Enclave進行交互/* edger8r_function_attributes:* 調用使用調用約定屬性聲明的ECALL。* 調用使用[public]聲明的ECALL。*/
void edger8r_function_attributes(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 調用公開的函數ecall_function_publicret = ecall_function_public(global_eid);if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 用戶不應該在這里調用私有函數int runned = 0;ret = ecall_function_private(global_eid, &runned);// 確保返回值是SGX_ERROR_ECALL_NOT_ALLOWED,并且函數未被運行if (ret != SGX_ERROR_ECALL_NOT_ALLOWED || runned != 0)abort();  // 如果檢查失敗,終止程序
}/* ocall_function_allow:* 該OCALL調用被[allow]屬性批準的edger8r_private。*/
void ocall_function_allow(void)
{int runned = 0;sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 調用私有函數ecall_function_privateret = ecall_function_private(global_eid, &runned);// 確保返回值是SGX_SUCCESS,并且函數已被運行if (ret != SGX_SUCCESS || runned != 1)abort();  // 如果檢查失敗,終止程序
}

這段代碼展示了如何調用SGX ECALL時處理不同的函數屬性:

  • edger8r_function_attributes 函數展示了如何調用具有公開屬性的ECALL函數,以及檢查并防止直接調用私有的ECALL函數。
  • ocall_function_allow 函數展示了如何通過OCALL調用被允許的私有ECALL函數。
    通過這些示例,可以清楚地了解如何在SGX環境中管理和調用具有不同屬性的ECALL函數,以確保代碼的安全性和正確性。
3.1.2.3 App/Edger8rSyntax/Pointers.cpp
#include "../App.h"       // 包含應用程序的頭文件
#include "Enclave_u.h"    // 包含Enclave生成的頭文件,用于與Enclave進行交互/* edger8r_pointer_attributes:* 調用聲明了指針屬性的ECALL。*/
void edger8r_pointer_attributes(void)
{int val = 0;sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 定義字符數組和長度變量char c[128] = {0};size_t len = 0;// 初始化字符數組cmemset(c, 0xe, 128);ret = ecall_pointer_user_check(global_eid, &len, &c, 128); // 調用ecall_pointer_user_check函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(strcmp(c, "SGX_SUCCESS") == 0); // 檢查c是否被更改為"SGX_SUCCESS"// 測試[in]指針屬性val = 1;ret = ecall_pointer_in(global_eid, &val); // 調用ecall_pointer_in函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(val == 1); // 確保val的值未被修改// 測試[out]指針屬性val = 1;ret = ecall_pointer_out(global_eid, &val); // 調用ecall_pointer_out函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(val == 1234); // 確保val的值被修改為1234// 測試[in, out]指針屬性val = 1;ret = ecall_pointer_in_out(global_eid, &val); // 調用ecall_pointer_in_out函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(val == 1234); // 確保val的值被修改為1234ret = ocall_pointer_attr(global_eid); // 調用OCALL函數ocall_pointer_attrif (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 測試字符串指針char str1[] = "1234567890";ret = ecall_pointer_string(global_eid, str1); // 調用ecall_pointer_string函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(strlen(str1) == 10 && memcmp(str1, "0987654321", strlen(str1)) == 0); // 確保str1的值被正確修改// 測試字符串指針常量const char str2[] = "1234567890";ret = ecall_pointer_string_const(global_eid, str2); // 調用ecall_pointer_string_const函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(strlen(str2) == 10 && memcmp(str2, "1234567890", strlen(str2)) == 0); // 確保str2未被修改// 測試指針大小char str3[] = "1234567890";ret = ecall_pointer_size(global_eid, (void*)str3, strlen(str3)); // 調用ecall_pointer_size函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(strlen(str3) == 10 && memcmp(str3, "0987654321", strlen(str3)) == 0); // 確保str3的值被正確修改// 測試只讀指針char str4[] = "1234567890";ret = ecall_pointer_isptr_readonly(global_eid, (buffer_t)str4, strlen(str4)); // 調用ecall_pointer_isptr_readonly函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序assert(strlen(str4) == 10 && memcmp(str4, "1234567890", strlen(str4)) == 0); // 確保str4未被修改// 測試帶計數的指針int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};ret = ecall_pointer_count(global_eid, arr, 10); // 調用ecall_pointer_count函數if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序for (int i = 0; i < 10; i++)assert(arr[i] == (9 - i)); // 確保arr的值被正確修改為9-ireturn;
}/* ocall_pointer_user_check:* 聲明為[user_check]的OCALL。*/
void ocall_pointer_user_check(int* val)
{(void)val;  // 安靜未使用的參數警告assert(val != NULL);  // 確保指針val不為NULL
}/* ocall_pointer_in:* 聲明為[in]的OCALL。*/
void ocall_pointer_in(int* val)
{*val = 1234;  // 將指針val指向的值設置為1234
}/* ocall_pointer_out:* 聲明為[out]的OCALL。*/
void ocall_pointer_out(int* val)
{*val = 1234;  // 將指針val指向的值設置為1234
}/* ocall_pointer_in_out:* 聲明為[in, out]的OCALL。*/
void ocall_pointer_in_out(int* val)
{*val = 1234;  // 將指針val指向的值設置為1234
}

ECALL函數總結

? ?在函數 edger8r_pointer_attributes中,多個不同的ECALL被調用來測試SGX環境下的指針操作。這些ECALL通過不同的指針屬性(如 user_check, in, out, in, out,以及字符串指針)來演示如何在enclave內部和外部之間傳遞和操作數據。每次調用都使用 assert 檢查數據是否已按預期進行了修改,以驗證指針操作的正確性。

OCALL函數總結

  • ocall_pointer_user_check
    驗證傳入的指針是否為非空指針,以確保數據完整性。
  • ocall_pointer_in
    將傳入的指針值設置為 1234,用于模擬出站(outbound)數據的例子。
  • ocall_pointer_out
    將傳入的指針值設置為 1234,與 ocall_pointer_in 類似,但用于表示入站(inbound)數據。
  • ocall_pointer_in_out
    同時設置傳入指針值為 1234,展示了入站和出站指針的雙向操作。

? ? 通過這個文件,SampleEnclave項目展示了用于實現不同指針操作的ECALL和OCALL方法。這些示例確保了在enclave內進行指針操作時的安全性和正確性,同時也增強了對指針的使用和數據傳遞機制的理解。

3.1.2.4 App/Edger8rSyntax/Types.cpp
#include "../App.h"       // 包含應用程序的頭文件
#include "Enclave_u.h"    // 包含Enclave生成的頭文件,用于與Enclave進行交互/* edger8r_type_attributes:* 調用聲明了基本類型的ECALL。*/
void edger8r_type_attributes(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 調用使用char類型的ECALLret = ecall_type_char(global_eid, (char)0x12); // 傳遞一個char類型的值0x12if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 調用使用int類型的ECALLret = ecall_type_int(global_eid, (int)1234); // 傳遞一個int類型的值1234if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 調用使用float類型的ECALLret = ecall_type_float(global_eid, (float)1234.0); // 傳遞一個float類型的值1234.0if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 調用使用double類型的ECALLret = ecall_type_double(global_eid, (double)1234.5678); // 傳遞一個double類型的值1234.5678if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 調用使用size_t類型的ECALLret = ecall_type_size_t(global_eid, (size_t)12345678); // 傳遞一個size_t類型的值12345678if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 調用使用wchar_t類型的ECALLret = ecall_type_wchar_t(global_eid, (wchar_t)0x1234); // 傳遞一個wchar_t類型的值0x1234if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 創建并初始化一個struct_foo_t類型的結構體struct struct_foo_t g = {1234, 5678};ret = ecall_type_struct(global_eid, g); // 傳遞一個結構體gif (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 創建并初始化一個union_union_foo_t類型的聯合體union union_foo_t val = {0};ret = ecall_type_enum_union(global_eid, ENUM_FOO_0, &val); // 傳遞一個枚舉值ENUM_FOO_0和一個聯合體valif (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 確認聯合體val的值是否被正確修改assert(val.union_foo_0 == 2);
}

? ? 這段代碼展示了如何通過SGX調用包含基本類型的ECALL。函數 edger8r_type_attributes 分別演示了如何傳遞 charintfloatdoublesize_twchar_t 類型的數據,以及如何傳遞 struct union 類型的數據。通過檢查每次ECALL的返回值,代碼確保了ECALL調用的正確性,同時也演示了如何在SGX環境中進行各種數據類型的操作。

3.1.3 App/TrustedLibrary文件夾

3.1.3.1 App/TrustedLibrary/Libc.cpp
#include "../App.h"       // 包含應用程序的頭文件
#include "Enclave_u.h"    // 包含Enclave生成的頭文件,用于與Enclave進行交互/* ecall_libc_functions:* 調用標準C函數。*/
void ecall_libc_functions(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 調用ECALL來演示malloc和free的使用ret = ecall_malloc_free(global_eid); // 調用malloc和free的ECALLif (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 初始化cpuid數組int cpuid[4] = {0x0, 0x0, 0x0, 0x0};// 調用ECALL來獲取CPUID信息ret = ecall_sgx_cpuid(global_eid, cpuid, 0x0); // 調用獲取CPUID信息的ECALLif (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序
}

? ? 通過這段代碼,SampleEnclave項目展示了如何在SGX enclave中調用標準C庫函數。函數 ecall_libc_functions 通過兩個ECALL演示了 mallocfree 的使用,以及如何獲取CPUCPUID信息。每次ECALL調用后都檢查其返回值以確保操作的成功。此示例有助于理解如何在SGX環境中使用標準C庫函數以及執行基本的內存管理和系統操作。

3.1.3.2 App/TrustedLibrary/Libcxx.cpp
#include <stdio.h>        // 包含標準輸入輸出頭文件#include "../App.h"       // 包含應用程序的頭文件
#include "Enclave_u.h"    // 包含Enclave生成的頭文件,用于與Enclave進行交互/* ecall_libcxx_functions:* 調用標準C++函數。*/
void ecall_libcxx_functions(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 調用ECALL函數來演示C++異常處理ret = ecall_exception(global_eid); // 調用處理異常的ECALLif (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序// 調用ECALL函數來演示C++標準庫中的map容器ret = ecall_map(global_eid); // 調用處理map的ECALLif (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序
}

? ?通過這段代碼,SampleEnclave項目展示了如何在SGX enclave中調用標準C++庫函數。函數 ecall_libcxx_functions 通過兩個ECALL函數演示了如何在enclave中處理C++異常以及使用C++標準庫中的 map 容器。每次ECALL調用后都檢查其返回值以確保操作的成功。此示例有助于理解如何在SGX環境中使用標準C++庫函數以及執行基本的異常處理和容器操作。

3.1.3.3 App/TrustedLibrary/Thread.cpp
#include <thread>        // 包含標準C++線程庫頭文件
#include <stdio.h>       // 包含標準輸入輸出頭文件
using namespace std;     // 使用標準命名空間#include "../App.h"      // 包含應用程序的頭文件
#include "Enclave_u.h"   // 包含Enclave生成的頭文件,用于與Enclave進行交互static size_t counter = 0;  // 全局計數器/* 增加計數器的函數 */
void increase_counter(void)
{size_t cnr = 0;sgx_status_t ret = SGX_ERROR_UNEXPECTED;ret = ecall_increase_counter(global_eid, &cnr);  // 調用ECALL增加計數器if (cnr != 0) counter = cnr;  // 如果cnr不為0,更新全局計數器if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序
}/* 數據生產者函數 */
void data_producer(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;ret = ecall_producer(global_eid);  // 調用ECALL數據生產者if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序
}/* 數據消費者函數 */
void data_consumer(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;ret = ecall_consumer(global_eid);  // 調用ECALL數據消費者if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序
}/* ecall_thread_functions:* 調用線程函數,包括互斥鎖、條件變量等。*/
void ecall_thread_functions(void)
{// 創建和啟動四個增加計數器的線程thread adder1(increase_counter);thread adder2(increase_counter);thread adder3(increase_counter);thread adder4(increase_counter);// 等待所有線程完成adder1.join();adder2.join();adder3.join();adder4.join();// 確認計數器的值是否符合預期assert(counter == 4 * LOOPS_PER_THREAD);printf("信息: 正在執行線程同步,請稍候...  \n");// 創建和啟動消費和生產線程thread consumer1(data_consumer);thread producer0(data_producer);thread consumer2(data_consumer);thread consumer3(data_consumer);thread consumer4(data_consumer);// 等待所有線程完成consumer1.join();consumer2.join();consumer3.join();consumer4.join();producer0.join();
}

? ? 這段代碼展示了如何在SGX enclave中通過ECALL實現基本的線程操作,包括線程同步、互斥鎖和條件變量等。通過創建多個線程并調用相應的ECALL函數,該示例演示了如何在enclave中進行并發操作,并驗證了全局計數器的正確性。同時,該代碼還演示了數據生產者和消費者的模式,以展示更復雜的線程同步機制。每個ECALL后都檢查其返回值,以確保操作的成功。

3.2 Enclave文件夾

3.2.1 Enclave/Enclave.cpp

/* * printf: *   Invokes OCALL to display the enclave buffer to the terminal.*/
int printf(const char* fmt, ...)
{char buf[BUFSIZ] = { '\0' };va_list ap;va_start(ap, fmt);vsnprintf(buf, BUFSIZ, fmt, ap);va_end(ap);ocall_print_string(buf);return (int)strnlen(buf, BUFSIZ - 1) + 1;
}

? ? 在Enclave中的主函數Enclave.cpp主要提供printf的OCALL函數,也就是將enclave通過該函數在REE中打印出來。

3.2.2 Enclave/Edger8rSyntax文件夾

3.2.2.1 Enclave/Edger8rSyntax/Arrays.cpp
/* 測試數組屬性 */
#include "sgx_trts.h"       // 包含SGX運行時庫的頭文件
#include "../Enclave.h"     // 包含Enclave自定義頭文件
#include "Enclave_t.h"      // 包含由Edger8r工具生成的頭文件,用于定義ECALL和OCALL接口
#include <string.h>         // 包含標準C庫的字符串操作頭文件/* ecall_array_user_check:*   [user_check] 參數不會執行拷貝操作。*/
void ecall_array_user_check(int arr[4])
{// 檢查數組是否位于Enclave外部if (sgx_is_outside_enclave(arr, 4 * sizeof(int)) != 1)abort();// 遍歷數組并修改其值for (int i = 0; i < 4; i++) {assert(arr[i] == i); // 確認數組元素的值是否為預期// 以下代碼執行arr[i] = (3 - i)// 它將4個字節寫入不受信任的內存,非8字節對齊。// 因此出于安全考慮需要使用memcpy_verw()int tmp = 3 - i;memcpy_verw(&arr[i], &tmp, sizeof(int));}
}/* ecall_array_in:*   arr[] 被拷貝到受信任域中,但修改后的結果不會反映到不受信任的一側。*/
void ecall_array_in(int arr[4])
{// 遍歷數組并修改其值for (int i = 0; i < 4; i++) {assert(arr[i] == i); // 確認數組元素的值是否為預期arr[i] = (3 - i);    // 修改數組元素的值}
}/* ecall_array_out:*   arr[] 在Enclave內部分配,并且會被拷貝到不受信任的一側。*/
void ecall_array_out(int arr[4])
{// 遍歷數組并修改其值for (int i = 0; i < 4; i++) {/* arr 不是從應用程序中拷貝過來的 */assert(arr[i] == 0); // 確認數組元素的初始值為0arr[i] = (3 - i);    // 修改數組元素的值}
}/* ecall_array_in_out:*   arr[] 會在Enclave內部分配,其內容也會被拷貝。*   ECALL返回后,結果會被拷貝到外部。*/
void ecall_array_in_out(int arr[4])
{// 遍歷數組并修改其值for (int i = 0; i < 4; i++) {assert(arr[i] == i); // 確認數組元素的值是否為預期arr[i] = (3 - i);    // 修改數組元素的值}
}/* ecall_array_isary:*   [isary] 告訴Edger8r用戶定義的 'array_t' 是一個數組類型。*/
void ecall_array_isary(array_t arr)
{// 檢查數組是否位于Enclave外部if (sgx_is_outside_enclave(arr, sizeof(array_t)) != 1)abort();int n = sizeof(array_t) / sizeof(arr[0]); // 計算數組元素的個數// 遍歷數組并修改其值for (int i = 0; i < n; i++) {assert(arr[i] == i);  // 確認數組元素的值是否為預期// 以下代碼執行arr[i] = (n - 1 - i);// 它將4個字節寫入不受信任的內存,非8字節對齊。// 因此出于安全考慮需要使用memcpy_verw()int tmp = n - 1 - i;memcpy_verw(&arr[i], &tmp, sizeof(int));}
}

? ? 這段代碼展示了多個ECALL函數,演示了如何在Enclave中處理不同的數組操作,包括傳入傳出雙向傳輸數組。每個函數都通過遍歷數組元素,執行各種檢查和修改操作,并考慮到內存訪問安全性的因素。尤其是在需要寫入不受信任內存時,使用 memcpy_verw 來確保安全性。這些示例有助于理解如何在SGX環境中安全地處理數組數據。

  • Arrays.edl
/* Arrays.edl - Samples for array attributes. */
enclave {/* *  Only for fixed-size array (size is explicitly specified).*/trusted {/** []:  can be used to declare an array.* [user_check]:*      pointer of the array won't be verified, and the buffer pointed by 'arr' *      is not copied into the enclave either. But enclave can modify the memory outside.*/public void ecall_array_user_check([user_check] int arr[4]);/** [in]:*      buffer for the array will be allocated inside the enclave, *      content of the array will be copied into the new allocated memory inside. *      Any changes performed inside the enclave will not affect the array outside.*/public void ecall_array_in([in] int arr[4]);/** [out]:*      buffer for the array will be allocated inside the enclave,*      but the content of the array won't be copied. After ECALL returns, *      the buffer inside the enclave will copied into outside array.*/public void ecall_array_out([out] int arr[4]);/** [in, out]:*      buffer for the array will be allocated inside the enclave,*      the content of the array will be copied either. After ECALL returns, *      the buffer inside the enclave will by copied into outside array again.*/public void ecall_array_in_out([in, out] int arr[4]);/** [isary]:*      tells Edger8r the user defined 'array_t' is an array type, 'arr' will be *      treated as a pointer, no memory copied either due to [user_check].*      For OCALLs, 'arr' shall point to the memory outside the enclave. */public void ecall_array_isary([user_check, isary] array_t arr);};untrusted {/** [user_check|in|out|in,out|isary] can also be used in OCALLs, refer to the "User Guide" for details.*/};};

這個 Arrays.edl 文件通過定義多個ECALL接口,詳細展示了如何在SGX Enclave中處理數組數據以及不同的數組屬性(如 [user_check], [in], [out], [in, out], [isary])。每種屬性都影響了數組數據在Enclave中的操作和在Enclave外部的反映方式。這個文件提供了豐富的示例,有助于理解在Enclave環境中如何安全且靈活地操作數組數據。

3.2.2.2 Enclave/Edger8rSyntax/Functions.cpp
/* 測試調用約定 */
#include <string.h>       // 包含標準C庫的字符串操作頭文件
#include <stdio.h>        // 包含標準輸入輸出頭文件#include "../Enclave.h"   // 包含Enclave自定義頭文件
#include "Enclave_t.h"    // 包含由Edger8r工具生成的頭文件,用于定義ECALL和OCALL接口/* ecall_function_public:*   一個公開的ECALL,調用OCALL 'ocall_function_allow'。*/
void ecall_function_public(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 調用OCALL函數'ocall_function_allow'ret = ocall_function_allow();if (ret != SGX_SUCCESS)abort();  // 如果調用失敗,終止程序return;
}/* ecall_function_private:*   一個私有的ECALL,僅能在OCALL 'ocall_function_allow'中被調用。*/
int ecall_function_private(void)
{return 1;  // 返回值1表示函數成功調用
}

? ? 這段代碼展示了如何在SGX Enclave中處理不同調用類型(公開 or 私有)的函數。函數 ecall_function_public 是一個公開的ECALL,它調用了一個OCALL函數 ocall_function_allow,并且檢查調用是否成功;如果失敗則終止程序。而 ecall_function_private是一個私有的ECALL,僅能在OCALL ocall_function_allow 中被調用,返回一個整數 1 表示成功。

? ? 這個示例有助于理解在SGX環境中如何區分和處理公開與私有的ECALL函數,以及如何通過OCALL來調用這些ECALL函數。

  • Functions.edl
/* Functions.edl - Samples for function attributes. */
enclave {/* * Following keywords/attributes are supported for untrusted functions: *      cdecl, stdcall, fastcall, dllimport (only for Windows).*      [public] is only supported for the trusted functions.* Trusted function will be treated as [private] w/o the [public].*/trusted {/** [public]:*      public ECALL can be called directly in App.*/public void ecall_function_public(void);/** [private]:*      private ECALL cannot be called directly in App.*/int ecall_function_private(void);};untrusted {/** [allow]:*      OCALL 'ocall_function_allow' can invoke ECALL 'ecall_function_private' in App side. ** Note: No ECALL can be called in OCALL w/o [allow].*/void ocall_function_allow(void) allow(ecall_function_private);};
};

? ? 這個Functions.edl文件定義了兩個ECALL和一個OCALL,同時展示了如何使用函數屬性來控制它們的調用權限。

  1. 公開ECALL:ecall_function_public 使用 [public] 屬性,可以直接在應用程序中調用,沒有限制。
  2. 私有ECALL:ecall_function_private 沒有使用 [public] 屬性,因此被視為私有的,不能直接在應用程序中調用。只能在被授權的OCALL中調用。
  3. OCALL與授權:ocall_function_allow 是一個OCALL,使用 [allow] 屬性,授權該OCALL調用特定的私有ECALL ecall_function_private。這種機制確保了只有特定的OCALL可以調用私有的ECALL,從而增強了安全性和控制。

? ? 這個示例有助于理解在SGX環境中如何使用EDL來定義和控制不同類型的函數調用,以及如何通過函數屬性來管理這些調用的權限。

3.2.2.3 Enclave/Edger8rSyntax/Pointers.cpp
#include <string.h>
#include <sys/types.h>#include "../Enclave.h"
#include "Enclave_t.h"
#include "sgx_lfence.h"
#include "sgx_trts.h"/* checksum_internal:*   計算輸入緩沖區和長度的簡單校驗和*/
int32_t checksum_internal(char* buf, size_t count)
{register int32_t sum = 0;int16_t* ptr = (int16_t*)buf;/* 主循環求和 */while (count > 1) {sum = sum + *ptr++;count = count - 2;}/* 如果有剩余字節,加上它 */if (count > 0) {sum = sum + *((char*)ptr);}return ~sum;
}/* ecall_pointer_user_check, ecall_pointer_in, ecall_pointer_out, ecall_pointer_in_out:*   測試[in]、[out]、[user_check]屬性的根ECALL。*/
size_t ecall_pointer_user_check(void* val, size_t sz)
{/* 檢查緩沖區是否在enclave外分配 */if (sgx_is_outside_enclave(val, sz) != 1)abort();/* 在sgx_is_outside_enclave檢查后插入fence */sgx_lfence();char tmp[100] = { 0 };size_t len = sz > 100 ? 100 : sz;/* 將內存復制到enclave中,以確保在checksum_internal()中不更改'val' */memcpy(tmp, val, len);int32_t sum = checksum_internal((char*)tmp, len);printf("Checksum(0x%p, %zu) = 0x%x\n",val, len, (unsigned int)sum);/* 直接修改外部內存 */memcpy_verw(val, "SGX_SUCCESS", len > 12 ? 12 : len);return len;
}/* ecall_pointer_in:*   val的緩沖區被復制到enclave中。*/
void ecall_pointer_in(int* val)
{if (sgx_is_within_enclave(val, sizeof(int)) != 1)abort();assert(*val == 1);*val = 1234;
}/* ecall_pointer_out:*   val的緩沖區被復制到不可信的一側。*/
void ecall_pointer_out(int* val)
{if (sgx_is_within_enclave(val, sizeof(int)) != 1)abort();assert(*val == 0);*val = 1234;
}/* ecall_pointer_in_out:*   val的緩沖區被雙重復制。*/
void ecall_pointer_in_out(int* val)
{if (sgx_is_within_enclave(val, sizeof(int)) != 1)abort();assert(*val == 1);*val = 1234;
}/* ocall_pointer_attr:*   測試OCALL [in]、[out]、[user_check]的根ECALL。*/
void ocall_pointer_attr(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;int val = 0;ret = ocall_pointer_user_check(&val);if (ret != SGX_SUCCESS)abort();val = 0;ret = ocall_pointer_in(&val);if (ret != SGX_SUCCESS)abort();assert(val == 0);val = 0;ret = ocall_pointer_out(&val);if (ret != SGX_SUCCESS)abort();assert(val == 1234);val = 0;ret = ocall_pointer_in_out(&val);if (ret != SGX_SUCCESS)abort();assert(val == 1234);return;
}/* ecall_pointer_string:*   [string]定義一個字符串。*/
void ecall_pointer_string(char* str)
{strncpy(str, "0987654321", strlen(str));
}/* ecall_pointer_string_const:*   const [string]定義一個不能修改的字符串。*/
void ecall_pointer_string_const(const char* str)
{char* temp = new char[strlen(str)];strncpy(temp, str, strlen(str));delete[] temp;
}/* ecall_pointer_size:*   'len'需要指定以告訴Edger8r 'str'的長度。*/
void ecall_pointer_size(void* ptr, size_t len)
{strncpy((char*)ptr, "0987654321", len);
}/* ecall_pointer_count:*   'cnt'需要指定以告訴Edger8r 'arr'中的元素數量。*/
void ecall_pointer_count(int* arr, size_t count)
{int cnt = (int)count;for (int i = (cnt - 1); i >= 0; i--)arr[i] = (cnt - 1 - i);
}/* ecall_pointer_isptr_readonly:*   'buf'是用戶定義類型,應標記為[isptr]。*   如果它不可寫,應指定[readonly]。*/
void ecall_pointer_isptr_readonly(buffer_t buf, size_t len)
{strncpy((char*)buf, "0987654321", len);
}

代碼功能分析
? ? 這段代碼展示了如何在Intel SGX中處理指針屬性的示例,包括[in]、[out]、[user_check]、[string]、[size]、[count]和[readonly]等屬性。以下是主要功能和步驟的詳細分析:

  1. 校驗和計算:
    checksum_internal函數計算輸入緩沖區的簡單校驗和。
  2. 用戶檢查指針:
    ecall_pointer_user_check函數檢查指針是否在enclave外部分配,并計算校驗和。
  3. 指針屬性測試:
    ecall_pointer_in函數驗證指針在enclave內并修改其值。
    ecall_pointer_out函數驗證指針在enclave內并修改其值。
    ecall_pointer_in_out函數驗證指針在enclave內并修改其值。
  4. OCALL指針屬性測試:
    ocall_pointer_attr函數測試OCALL中的指針屬性,包括用戶檢查、輸入和輸出指針。
  5. 字符串指針:
    ecall_pointer_string函數處理可修改的字符串指針。
    ecall_pointer_string_const函數處理不可修改的字符串指針。
  6. 指針大小和計數:
    ecall_pointer_size函數處理指定長度的指針。
    ecall_pointer_count函數處理指定元素數量的指針數組。
  7. 只讀指針:
    ecall_pointer_isptr_readonly函數處理只讀指針。
  • Pointers.edl
/* Pointers.edl - Samples for pointer attributes. */
enclave {/* * Following keywords/attributes are supported for pointers in Edger8r: *      in, out, user_check, *      string, wstring,*      const, size, count, isptr, readonly*/trusted {/** [user_check]:*      the pointer won't be validated, and the buffer pointed by*      'val' is not copied into the enclave either. But Enclave *      can modify the memory pointed by 'val'.*/public size_t ecall_pointer_user_check([user_check] void *val, size_t sz);/** [in]:*      buffer with the same size will be allocated inside the enclave,*      content pointed by 'val' will be copied into the new allocated*      memory inside. Any changes performed inside the enclave will not *      affect the buffer outside.*/public void ecall_pointer_in([in] int *val);/** [out]:*      buffer with the same size will be allocated inside the enclave,*      but the content pointed by 'val' won't be copied. But after return, *      the buffer inside the enclave will copied into outside 'val'.*/public void ecall_pointer_out([out] int *val);/** [in, out]:*      buffer with the same size will be allocated inside the enclave,*      the content pointed by 'val' will be copied either. After return, *      the buffer inside the enclave will by copied into outside 'val' again.*/public void ecall_pointer_in_out([in, out] int *val);/** [string]:*      the attribute tells Edger8r 'str' is NULL terminated string, so strlen *      will be used to count the length of buffer pointed by 'str'.*/public void ecall_pointer_string([in, out, string] char *str);/** [const]:*      the attribute tells Edger8r the buffer pointed by 'str' cannot be modified,*      so users cannot decorate 'str' with [out] attribute anymore.*/public void ecall_pointer_string_const([in, string] const char *str);/** [size]:*      the attribute tells Edger8r the length of buffer in byte pointed by 'ptr' *      (shall be copied or not). * Note: Users shall not specify [size] on [string] parameters.*/public void ecall_pointer_size([in, out, size=len] void *ptr, size_t len);/** [count]:*      the attribute tells Edger8r the number of integers to be copied from 'arr'.*/public void ecall_pointer_count([in, out, count=cnt] int *arr, size_t cnt);/** [isptr]:*      tells Edger8r the user defined type is a pointer; * [readonly]:*      forbids the buffer allocated inside the enclave to be copied back to App*      (cannot use with [out]).*/public void ecall_pointer_isptr_readonly([in, isptr, readonly, size=len] buffer_t buf, size_t len);};/** Users can define multiple trusted/untrusted blocks, * edger8r will merged them into one trusted/untrusted block.*/trusted {/** Test pointer attributes in OCALLs*/public void ocall_pointer_attr(void);};untrusted {/** [user_check]:*      the pointer won't be verified, and the buffer pointed by 'val' is not *      copied to outside buffer either. Besides 'App' cannot modify the memory *      pointer by 'val'.*/void ocall_pointer_user_check([user_check] int *val);/** [in]:*      buffer with the same size will be allocated in 'App' side, the content *      pointed by 'val' will be copied into the new allocated memory outside. *      Any changes performed by 'App' will not affect the buffer pointed by 'val'.*/void ocall_pointer_in([in] int *val);/** [out]:*      buffer with the same size will be allocated in 'App' side, the content*      pointed by 'val' won't be copied. But after return, the buffer outside*      will be copied into the enclave.*/void ocall_pointer_out([out] int *val);/** [in, out]:*      buffer with the same size will be allocated in 'App' side, the content*      pointed by 'val' will be copied either. After return, the buffer outside *      will copied into the enclave.*/void ocall_pointer_in_out([in, out] int *val);};};
3.2.2.4 Enclave/Edger8rSyntax/Types.cpp
#include "sgx_trts.h"
#include "../Enclave.h"
#include "Enclave_t.h"
#include <limits>
#include <cmath>/* 用于消除“未使用變量”警告 */
#define UNUSED(val) (void)(val)#define ULP 2/* 用于比較double變量以避免編譯警告 */
bool  almost_equal(double x, double y)
{/* 機器epsilon必須根據較大值的量級進行縮放并乘以所需的ULP(最后一位的單位)精度 */return std::abs(x-y) <= std::numeric_limits<double>::epsilon() * std::abs(x+y) * ULP;
}/* 用于比較float變量以避免編譯警告 */
bool  almost_equal(float x, float y)
{/* 機器epsilon必須根據較大值的量級進行縮放并乘以所需的ULP(最后一位的單位)精度 */return std::abs(x-y) <= std::numeric_limits<float>::epsilon() * std::abs(x+y) * ULP;
}/* ecall_type_char:*   [char]值由App傳遞。*/
void ecall_type_char(char val)
{assert(val == 0x12);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_int:*   [int]值由App傳遞。*/
void ecall_type_int(int val)
{assert(val == 1234);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_float:*   [float]值由App傳遞。*/
void ecall_type_float(float val)
{assert(almost_equal(val, (float)1234.0));
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_double:*   [double]值由App傳遞。*/
void ecall_type_double(double val)
{assert(almost_equal(val, (double)1234.5678));
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_size_t:*   [size_t]值由App傳遞。*/
void ecall_type_size_t(size_t val)
{assert(val == (size_t)12345678);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_wchar_t:*   [wchar_t]值由App傳遞。*/
void ecall_type_wchar_t(wchar_t val)
{assert(val == (wchar_t)0x1234);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_struct:*   struct_foo_t在EDL中定義,可以在ECALL中使用。*/
void ecall_type_struct(struct struct_foo_t val)
{assert(val.struct_foo_0 == 1234);assert(val.struct_foo_1 == 5678);
#ifndef DEBUGUNUSED(val);
#endif
}/** ecall_type_enum_union:*   enum_foo_t/union_foo_t在EDL中定義*   并且可以在ECALL中使用。*/
void ecall_type_enum_union(enum enum_foo_t val1, union union_foo_t *val2)
{if (sgx_is_outside_enclave(val2, sizeof(union union_foo_t)) != 1)abort();val2->union_foo_0 = 1;val2->union_foo_1 = 2; /* 覆蓋union_foo_0 */assert(val1 == ENUM_FOO_0);
#ifndef DEBUGUNUSED(val1);
#endif
}

? ? 這段代碼展示了如何在SGX中處理各種基本類型,包括浮點數、整數、結構體、枚舉和聯合體。通過詳細的注釋和步驟分析,讀者可以更好地理解SGX中基本類型的工作原理和實現細節。代碼中涵蓋了如何接收和驗證不同類型的值,以及如何處理浮點數比較等操作。

  • Types.edl
/* Types.edl - Samples for basic types. */
enclave {/* * Following types can be supported in Edger8r: *      char, short, int, float, double, void, *      int8_t, int16_t, int32_t, int64_t,*      size_t, wchar_t, *      uint8_t, uint16_t, uint32_t, uint64_t, *      unsigned, struct, enum, union.*//** We will demo few types in ECALL functions, data * types in OCALL functions can be handled either.*//* structure definition */struct struct_foo_t {/* Basic types can be used in structure. */uint32_t struct_foo_0;uint64_t struct_foo_1;};/* enum definition */enum enum_foo_t {ENUM_FOO_0 = 0,ENUM_FOO_1 = 1};/* union definition */union union_foo_t {uint32_t union_foo_0;uint32_t union_foo_1;uint64_t union_foo_3;};trusted {public void ecall_type_char(char val);public void ecall_type_int(int val);public void ecall_type_float(float val);public void ecall_type_double(double val);public void ecall_type_size_t(size_t val);public void ecall_type_wchar_t(wchar_t val);public void ecall_type_struct(struct struct_foo_t val);public void ecall_type_enum_union(enum enum_foo_t val1, [user_check] union union_foo_t *val2);/* for using user defined types, please refer to Pointers.edl, Arrays.edl. */};
};

3.2.3 Enclave/TrustedLibrary文件夾

3.2.3.1 Enclave/TrustedLibrary/Libc.cpp
#include <string.h>
#include "sgx_cpuid.h"
#include "sgx_trts.h"
#include "../Enclave.h"
#include "Enclave_t.h"/* ecall_malloc_free:*   使用malloc/free分配/釋放受信任的內存。*/
void ecall_malloc_free(void)
{void *ptr = malloc(100); // 分配100字節的受信任內存assert(ptr != NULL); // 確保內存分配成功memset(ptr, 0x0, 100); // 將分配的內存設置為0free(ptr); // 釋放內存
}/* ecall_sgx_cpuid:*   使用sgx_cpuid獲取CPU特性和類型。*/
void ecall_sgx_cpuid(int cpuinfo[4], int leaf)
{sgx_status_t ret = sgx_cpuid(cpuinfo, leaf); // 獲取指定leaf的CPU信息if (ret != SGX_SUCCESS) // 如果獲取失敗,則終止執行abort();
}

? ? 這段代碼展示了如何在Intel SGX中使用malloc/free分配和釋放受信任的內存,以及如何使用sgx_cpuid獲取CPU特性和類型。以下是主要功能和步驟的詳細分析:

  • 內存分配和釋放:
    ecall_malloc_free函數使用malloc分配100字節的受信任內存,并確保內存分配成功。
    使用memset將分配的內存設置為0。
    使用free釋放內存。
  • 獲取CPU特性和類型:
    ecall_sgx_cpuid函數使用sgx_cpuid獲取指定leaf的CPU信息。
    如果獲取CPU信息失敗,則終止執行。
3.2.3.2 Enclave/TrustedLibrary/Libcxx.cpp
#include <cstdlib>
#include <string>#include "../Enclave.h"
#include "Enclave_t.h"/** ecall_exception:*   在enclave內部拋出/捕獲C++異常。*/void ecall_exception(void)
{std::string foo = "foo";try {throw std::runtime_error(foo); // 拋出runtime_error異常}catch (std::runtime_error const& e) {assert(foo == e.what()); // 捕獲runtime_error異常,并驗證異常信息std::runtime_error clone(""); // 創建一個空的runtime_error異常clone = e; // 復制異常assert(foo == clone.what()); // 驗證復制的異常信息}catch (...) {assert(false); // 捕獲所有其他異常,并斷言失敗}
}#include <map>
#include <algorithm>using namespace std;/** ecall_map:*   在enclave中使用STL <map>。*/
void ecall_map(void)
{typedef map<char, int, less<char> > map_t; // 定義一個字符到整數的映射類型typedef map_t::value_type map_value; // 定義映射的值類型map_t m; // 創建一個映射實例m.insert(map_value('a', 1)); // 插入鍵值對('a', 1)m.insert(map_value('b', 2)); // 插入鍵值對('b', 2)m.insert(map_value('c', 3)); // 插入鍵值對('c', 3)m.insert(map_value('d', 4)); // 插入鍵值對('d', 4)assert(m['a'] == 1); // 驗證鍵'a'的值為1assert(m['b'] == 2); // 驗證鍵'b'的值為2assert(m['c'] == 3); // 驗證鍵'c'的值為3assert(m['d'] == 4); // 驗證鍵'd'的值為4assert(m.find('e') == m.end()); // 驗證鍵'e'不存在于映射中return;
}

? ? 這段代碼展示了在Intel SGX中使用C++異常處理STL容器(如std::map)的示例。以下是主要功能和步驟的詳細分析:

  • 異常處理:
    ecall_exception函數在enclave內部拋出和捕獲C++異常
    使用try-catch塊拋出std::runtime_error異常,并在catch塊中捕獲異常。
    驗證捕獲的異常信息是否與預期值一致。
    創建并復制異常對象,驗證復制的異常信息是否與原始異常一致。
  • STL容器std::map的使用:
    ecall_map函數在enclave內部使用STL容器std::map
    定義一個字符到整數的映射類型,并插入一些鍵值對。
    驗證插入的鍵值對是否正確。
    檢查某個鍵是否存在于映射中。
3.2.3.3 Enclave/TrustedLibrary/Thread.cpp
#include "../Enclave.h"
#include "Enclave_t.h"#include "sgx_thread.h"static size_t global_counter = 0; // 全局計數器
static sgx_thread_mutex_t global_mutex = SGX_THREAD_MUTEX_INITIALIZER; // 全局互斥鎖#define BUFFER_SIZE 50 // 緩沖區大小typedef struct {int buf[BUFFER_SIZE]; // 緩沖區數組int occupied; // 已占用的緩沖區數量int nextin; // 下一個寫入位置int nextout; // 下一個讀取位置sgx_thread_mutex_t mutex; // 互斥鎖sgx_thread_cond_t more; // 條件變量,表示有更多數據sgx_thread_cond_t less; // 條件變量,表示有更少數據
} cond_buffer_t;static cond_buffer_t buffer = {{0, 0, 0, 0, 0, 0}, 0, 0, 0,SGX_THREAD_MUTEX_INITIALIZER, SGX_THREAD_COND_INITIALIZER, SGX_THREAD_COND_INITIALIZER}; // 初始化緩沖區/** ecall_increase_counter:*   在enclave中使用線程API。*/
size_t ecall_increase_counter(void)
{size_t ret = 0;for (int i = 0; i < LOOPS_PER_THREAD; i++) {sgx_thread_mutex_lock(&global_mutex); // 加鎖/* 互斥地增加計數器 */size_t tmp = global_counter;global_counter = ++tmp;if (4*LOOPS_PER_THREAD == global_counter)ret = global_counter;sgx_thread_mutex_unlock(&global_mutex); // 解鎖}return ret;
}void ecall_producer(void)
{for (int i = 0; i < 4*LOOPS_PER_THREAD; i++) {cond_buffer_t *b = &buffer;sgx_thread_mutex_lock(&b->mutex); // 加鎖while (b->occupied >= BUFFER_SIZE)sgx_thread_cond_wait(&b->less, &b->mutex); // 等待緩沖區有空閑空間b->buf[b->nextin] = b->nextin; // 寫入數據b->nextin++;b->nextin %= BUFFER_SIZE; // 循環使用緩沖區b->occupied++; // 增加占用計數sgx_thread_cond_signal(&b->more); // 通知消費者有更多數據sgx_thread_mutex_unlock(&b->mutex); // 解鎖}
}void ecall_consumer(void)
{for (int i = 0; i < LOOPS_PER_THREAD; i++) {cond_buffer_t *b = &buffer;sgx_thread_mutex_lock(&b->mutex); // 加鎖while(b->occupied <= 0)sgx_thread_cond_wait(&b->more, &b->mutex); // 等待緩沖區有數據b->buf[b->nextout++] = 0; // 讀取數據b->nextout %= BUFFER_SIZE; // 循環使用緩沖區b->occupied--; // 減少占用計數sgx_thread_cond_signal(&b->less); // 通知生產者有空閑空間sgx_thread_mutex_unlock(&b->mutex); // 解鎖}
}

? ? 這段代碼展示了如何在Intel SGX中使用線程同步原語(如互斥鎖和條件變量)進行多線程編程。以下是主要功能和步驟的詳細分析:

  1. 全局計數器的增加:
    ecall_increase_counter函數使用互斥鎖global_mutex來保護全局計數器global_counter
    在循環中,函數鎖定互斥鎖,安全地增加計數器,然后解鎖互斥鎖。
  2. 生產者-消費者模型:
  • 使用cond_buffer_t結構體定義一個帶有互斥鎖條件變量的緩沖區。
  • ecall_producer函數實現生產者邏輯:
    在循環中,函數鎖定緩沖區的互斥鎖。
    如果緩沖區已滿,等待less條件變量。
    向緩沖區寫入數據,更新寫入位置和占用計數。
    發出more條件變量信號,通知消費者有更多數據。
    解鎖互斥鎖。
  • ecall_consumer函數實現消費者邏輯:
    在循環中,函數鎖定緩沖區的互斥鎖。
    如果緩沖區為空,等待more條件變量。
    從緩沖區讀取數據,更新讀取位置和占用計數。
    發出less條件變量信號,通知生產者有空閑空間。
    解鎖互斥鎖。

四. 感謝支持

? ? 完結撒花!后續將持續輸出,形成Intel SGX的系列教程,并結合密碼學算法設計更復雜功能。希望看到這里的小伙伴能點個關注,也歡迎志同道合的小伙伴一起廣泛交流。
? ? 碼字實屬不易,如果本文對你有10分幫助,就賞個10分把,感謝各位大佬支持!

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/37785.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/37785.shtml
英文地址,請注明出處:http://en.pswp.cn/web/37785.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一文全概括,建議收藏,那些你不可錯過的IC設計書籍合集(可下載)

集成電路設計工程師的角色不僅是推動技術創新的中堅力量&#xff0c;更是實現產品從概念到現實的關鍵橋梁。隨著對高性能、低功耗芯片的需求不斷增長&#xff0c;IC設計工程師的專業技能和知識深度成為了衡量其職業價值的重要標準。無論是在數字邏輯設計、功能驗證、可測試性設…

JMeter--定時執行的方法

原文網址&#xff1a;JMeter--定時執行的方法_IT利刃出鞘的博客-CSDN博客 簡介 本文介紹JMeter如何使用定時器定時執行測試任務。 Java技術星球&#xff1a;way2j.com 方法 第一步&#xff1a;新建定時器 右鍵測試任務> Add > Timer> Constant Timer 如下圖所示…

qt中數據庫和excel互導數據————附帶詳細步驟和代碼

文章目錄 0 背景1 準備QXlsx環境1.1 cmake安裝使用1.2 qmake使用 2 把excel數據導出到mysql數據庫3 把mysql數據庫的數據寫入到excel4 完整代碼5 項目代碼倉庫 0 背景 因為需要批量導入和導出數據&#xff0c;所以需要用到excel。實現把數據庫的數據導入到excel中&#xff0c;…

圓圈序號1~10復制粘貼直接用

1. 空心圓圈數字序號&#xff1a; ①、②、③、④、⑤、⑥、⑦、⑧、⑨、⑩ 2. 實心圓圈數字序號&#xff1a; ?、?、?、?、?、?、?、?、?、? 3. 空心圓圈中文序號&#xff1a; 一、二、三、四、五、六、七、八、九、十

linux高級編程(線程)(1)

虛擬地址&#xff1a; 線程&#xff1a; 概念&#xff1a;線程是輕量級進程&#xff0c;一般是一個進程中的多個任務。 進程是系統中最小的資源分配單位。&#xff08;競爭計算機資源的最小單位&#xff09;&#xff08;進程能分配硬件資源&#xff0c;線程不行&#x…

解析QAnything啟動命令過程

一.啟動命令過程日志 啟動命令bash ./run.sh -c local -i 0 -b hf -m Qwen-1_8B-Chat -t qwen-7b-chat。輸入日志如下所示&#xff1a; rootMM-202203161213:/mnt/l/20230918_RAG方向/QAnything# bash ./run.sh -c local -i 0 -b hf -m Qwen-1_8B-Chat -t qwen-7b-chat From …

Leetcode Java學習記錄——棧和隊列 IDEA

文章目錄 棧和隊列stack Classqueue InterfaceDeque Interfaceadd 和 push Priority Queue -- Class題目 codestyleIDEA 操作快捷鍵選擇代碼生成類 棧和隊列 stack Class google stack java 8/12 empty() peek() pop() push(E item) search(Object o) 最近相關性會用到棧 …

湘潭大學軟件工程數據庫總結

文章目錄 前言試卷結構給學弟學妹的一些參考自己的一些總結 前言 自己可能很早很早之前就準備復習了&#xff0c;但是感覺還是沒有學到要點&#xff0c;主要還是沒啥緊迫的壓力&#xff0c;我們是三月份開學&#xff0c;那時候實驗室有朋友挺認真開始學習數據庫了&#xff0c;…

理性決策的藝術:從購房到擇偶的數學智慧;37% 規則,做出最佳決策的秘訣;用數學模型解決人生難題

在面對人生重大決策時&#xff0c;如購房或擇偶&#xff0c;我們常常感到迷茫和困惑。然而&#xff0c;如果我們能夠將這些看似復雜的問題簡化為數學模型&#xff0c;我們就能以更加理性和系統的方式做出決策。 37%規則 1950年代&#xff0c;當時幾位數學家開始研究這樣一個問…

值得收藏!盤點那些適合普通人方便又好用的AIGC工具!(下)

【導讀】接上一篇文章&#xff0c;盤點國內外適合普通人能夠輕松上手的AIGC工具&#xff08;上&#xff09;。今天又為大家整理了一些好用又方便的AI設計工具、AI辦公工具、AI編程工具、AI指令工具和AI檢測工具&#xff0c;如果有沒更新到的工具也歡迎大家評論區交流。 一 、A…

Kafka 入門指南

Kafka 入門指南 簡介 Kafka 是一個由 Apache 軟件基金會開發的開源流處理平臺。它最初由 LinkedIn 開發&#xff0c;并在 2011 年作為開源項目發布。Kafka 是一個分布式、可擴展、高吞吐量的消息隊列系統&#xff0c;廣泛應用于實時數據流處理場景。 主要概念 1. 主題 (Top…

C#/WPF 自制截圖工具

在日常使用電腦辦公時&#xff0c;我們經常遇到需要截圖然后保存圖片&#xff0c;我們往往需要借助安裝截圖工具才能實現&#xff0c;現在我們通過C#自制截圖工具&#xff0c;也能夠輕松進行截圖。 我們可以通過C#調用WindousAPI來實現截圖&#xff0c;實例代碼如下&#xff1a…

AI基本概念(人工智能、機器學習、深度學習)

人工智能 、 機器學習、 深度學習的概念和關系 人工智能 &#xff08;Artificial Intelligence&#xff09;AI- 機器展現出人類智慧機器學習 &#xff08;Machine Learning) ML, 達到人工智能的方法深度學習 &#xff08;Deep Learning&#xff09;DL,執行機器學習的技術 從范圍…

算法 —— 滑動窗口

目錄 長度最小的子數組 無重復字符的最長子串 最大連續1的個數 將x減到0的最小操作數 找到字符串中所有字母異位詞 長度最小的子數組 sum比target小就進窗口&#xff0c;sum比target大就出窗口&#xff0c;由于數組是正數&#xff0c;所以相加會使sum變大&#xff0c;相減…

關于redis的運維面試題-1

1. 什么是Redis&#xff1f; Redis&#xff08;Remote Dictionary Server&#xff09;是一個開源的內存數據結構存儲&#xff0c;通常用作數據庫、緩存和消息代理。它支持多種數據結構&#xff0c;如字符串&#xff08;strings&#xff09;、哈希&#xff08;hashes&#xff0…

大二暑假 + 大三上

希望&#xff0c;暑假能早睡早起&#xff0c;胸圍達到 95&#xff0c;腰圍保持 72&#xff0c;大臂 36&#xff0c;小臂 32&#xff0c;小腿 38&#x1f36d;&#x1f36d; 目錄 &#x1f348;暑假計劃 &#x1f339;每周進度 &#x1f923;寒假每日進度&#x1f602; &…

DiskGeniusV5.6.0.1565發布!

DiskGenius是一款功能強大的磁盤管理和數據恢復工具&#xff0c;V5.6.0.1565上線。新版本變化比較大&#xff0c;增加新的功能&#xff0c;修正已經問題&#xff0c;值得試一下。提醒大家&#xff0c;磁盤管理軟件涉及數據安全&#xff0c;請始終使用最新版本&#xff01; 下面…

JS hook

參照&#xff1a; JS 逆向之 Hook JS Hook 與 過 debugger 一、常用Hook 1. eval (function() {let _eval eval;eval function(val) {if (val.indexof(debugger) -1) {_eval_cache(obj);}} })(); 2. JSON.parse() (function () {var parse_ JSON.parse;JSON.parse …

C++ initializer_list類型推導

目錄 initializer_list C自動類型推斷 auto typeid decltype initializer_list<T> C支持統一初始化{ }&#xff0c;出現了一個新的類型initializer_list<T>&#xff0c;一切類型都可以用列表初始化。提供了一種更加靈活、安全和明確的方式來初始化對象。 class…

IO-Link OD介紹

IO-Link OD&#xff08;On-request Data&#xff0c;按需數據&#xff09;是IO-Link通信中的一種重要數據類型&#xff0c;主要用于參數讀寫、指令交互、事件上傳等動作。以下是關于IO-Link OD的結構、構成以及功能使用的詳細說明&#xff1a; 結構與構成 定義&#xff1a;OD…