指令集與硬件能力)
1. 項(xiàng)目緣起為什么要在PongoOS里讀CPU寄存器最近在折騰一臺(tái)基于蘋果M1芯片的設(shè)備想搞清楚它到底支持哪些高級(jí)的ARMv8指令集擴(kuò)展。這事兒聽起來(lái)有點(diǎn)硬核但動(dòng)機(jī)其實(shí)很樸素我想知道這臺(tái)設(shè)備能不能跑某些依賴特定CPU特性的軟件比如一些需要AES、SHA3硬件加速的加密庫(kù)或者需要高級(jí)SIMD指令優(yōu)化的科學(xué)計(jì)算程序。直接問(wèn)操作系統(tǒng)macOS或者iOS的API通常不會(huì)把這么底層的CPU功能寄存器信息直接暴露給你。這時(shí)候一個(gè)更底層的環(huán)境就顯得尤為重要了。PongoOS對(duì)于不熟悉蘋果生態(tài)底層開發(fā)的朋友來(lái)說(shuō)可能是個(gè)陌生的名字。簡(jiǎn)單來(lái)說(shuō)它是一個(gè)運(yùn)行在蘋果A系列和M系列芯片設(shè)備上的“引導(dǎo)加載程序”或“微型操作系統(tǒng)”。它比iOS/macOS更早啟動(dòng)擁有對(duì)硬件更直接、更底級(jí)的訪問(wèn)權(quán)限。你可以把它想象成一個(gè)超級(jí)精簡(jiǎn)的、專為硬件調(diào)試和逆向工程設(shè)計(jì)的Linux內(nèi)核但它只服務(wù)于蘋果的ARM64 SoC。在PongoOS的環(huán)境下我們可以繞過(guò)上層操作系統(tǒng)的抽象層直接使用ARM架構(gòu)定義的匯編指令去讀寫那些關(guān)鍵的系統(tǒng)寄存器。而這次我們的目標(biāo)寄存器ID_AA64ISAR0_EL1正是ARMv8-A架構(gòu)中用于標(biāo)識(shí)AArch64執(zhí)行狀態(tài)也就是我們常說(shuō)的64位ARM模式下指令集支持的“身份證”之一。通過(guò)讀取它我們可以精確地知道這塊CPU支持ARMv8.0的哪些可選擴(kuò)展比如CRC32校驗(yàn)、原子操作、加密指令等等。這對(duì)于系統(tǒng)開發(fā)者、固件工程師、安全研究員甚至是追求極致性能優(yōu)化的應(yīng)用開發(fā)者都有著實(shí)實(shí)在在的價(jià)值。它不是一次簡(jiǎn)單的“讀值”而是一次對(duì)硬件能力的直接“摸底”。2. 理解目標(biāo)ARM64的ID寄存器體系與AA64ISAR0在動(dòng)手寫代碼之前我們必須先搞清楚我們要讀的是什么以及它在整個(gè)ARMv8架構(gòu)中的位置。否則讀出來(lái)一堆十六進(jìn)制數(shù)也毫無(wú)意義。2.1 ARMv8的系統(tǒng)寄存器命名規(guī)則ARMv8架構(gòu)有一個(gè)非常龐大的系統(tǒng)寄存器集合用于控制處理器的行為、配置內(nèi)存系統(tǒng)、記錄系統(tǒng)狀態(tài)等。這些寄存器的命名是有規(guī)律的通常格式為寄存器名_異常級(jí)別執(zhí)行狀態(tài)寄存器編號(hào)。ID_ 前綴表示這是一個(gè)“標(biāo)識(shí)”寄存器用于描述處理器的實(shí)現(xiàn)和功能是只讀的。AA64 表示這個(gè)寄存器描述的是AArch6464位執(zhí)行狀態(tài)下的特性。對(duì)應(yīng)的還有AA32用于AArch3232位狀態(tài)。ISAR 代表“Instruction Set Attribute Register”指令集屬性寄存器。顧名思義它描述了指令集相關(guān)的特性。0 編號(hào)。因?yàn)橹噶罴匦院芏嗨杂蠭SAR0,ISAR1等多個(gè)寄存器。EL1 表示這個(gè)寄存器在異常級(jí)別1EL1可訪問(wèn)。EL1通常對(duì)應(yīng)操作系統(tǒng)內(nèi)核級(jí)別。在PongoOS中我們通常就運(yùn)行在EL1或EL2虛擬化管理程序級(jí)別因此可以訪問(wèn)這些寄存器。所以ID_AA64ISAR0_EL1合起來(lái)就是在EL1異常級(jí)別下描述AArch64指令集屬性的0號(hào)標(biāo)識(shí)寄存器。2.2 ID_AA64ISAR0_EL1 的位域解析這個(gè)寄存器是一個(gè)64位的寄存器ARM架構(gòu)手冊(cè)將其劃分成了多個(gè)字段每個(gè)字段編碼了不同的指令集擴(kuò)展支持情況。下面是一個(gè)簡(jiǎn)化的位域表格列出了最常用和關(guān)鍵的幾個(gè)字段位域范圍字段名描述常見值含義 (舉例)[63:56]AES高級(jí)加密標(biāo)準(zhǔn)指令支持0b0001: 支持 AES指令[55:52]SHA1SHA1哈希指令支持0b0001: 支持 SHA1指令[51:48]SHA2SHA2-256/512哈希指令支持0b0001: 支持 SHA256指令0b0010: 支持 SHA512指令[47:44]CRC32CRC32校驗(yàn)指令支持0b0001: 支持 CRC32指令[43:40]Atomic原子操作指令支持0b0010: 支持 LSE (Large System Extensions) 原子指令[39:36]TME事務(wù)性內(nèi)存擴(kuò)展 (較少見)[35:32]RDM舍入雙倍乘加指令 (高級(jí)SIMD)0b0001: 支持[31:28]SHA3SHA3哈希指令支持0b0001: 支持[27:24]SM3/SM4中國(guó)商用密碼算法指令[23:20]DP點(diǎn)積指令 (高級(jí)SIMD)0b0001: 支持[19:16]FHM半精度浮點(diǎn)乘加指令[15:12]TS標(biāo)志操作指令[11:8]RNDR隨機(jī)數(shù)寄存器支持[7:4]Reserved保留位[3:0]Reserved保留位注意 上表是一個(gè)精簡(jiǎn)版。完整的字段定義需要查閱對(duì)應(yīng)芯片核心如Cortex-A76, Firestorm等的ARM架構(gòu)參考手冊(cè)。不同型號(hào)的蘋果CPUA12 vs M2支持的字段和值可能不同。值0b0000通常表示“不支持”非零值則表示支持具體含義需查手冊(cè)。為什么理解這個(gè)表格至關(guān)重要因?yàn)橹苯幼x取ID_AA64ISAR0_EL1得到的是一個(gè)64位的整數(shù)比如0x0000111120211120。你必須根據(jù)上面的位域劃分把這個(gè)十六進(jìn)制數(shù)“拆解”開來(lái)才能知道每一位代表的意義。例如通過(guò)移位和掩碼操作提取出[51:48]位的值如果它是0b0010你就知道這塊CPU支持SHA512硬件加速。這是從原始數(shù)據(jù)到有用信息的關(guān)鍵一步。3. 環(huán)境準(zhǔn)備PongoOS的獲取、編譯與運(yùn)行基礎(chǔ)在開始編碼之前我們需要一個(gè)可運(yùn)行的PongoOS環(huán)境。這里假設(shè)你已經(jīng)在macOS或Linux開發(fā)機(jī)上搭建好了基本的ARM64交叉編譯工具鏈例如aarch64-none-elf-。3.1 獲取PongoOS源代碼PongoOS是一個(gè)開源項(xiàng)目通常可以從GitHub等代碼托管平臺(tái)找到。由于項(xiàng)目可能活躍在不同的倉(cāng)庫(kù)這里不指定具體URL但你可以搜索“PongoOS”或“checkra1n”相關(guān)的開源項(xiàng)目來(lái)找到它。獲取代碼后目錄結(jié)構(gòu)通常包含pongoos/ ├── build/ # 編譯輸出目錄 ├── kernel/ # 內(nèi)核核心代碼 ├── modules/ # 可加載模塊 ├── include/ # 頭文件 └── ... (其他平臺(tái)相關(guān)目錄)我們主要關(guān)心的是kernel/和include/目錄以及頂層的構(gòu)建腳本如Makefile。3.2 理解PongoOS的模塊機(jī)制PongoOS支持以“模塊”的形式動(dòng)態(tài)加載代碼。這對(duì)于我們做實(shí)驗(yàn)非常方便。我們不需要修改PongoOS的核心內(nèi)核只需要編寫一個(gè)獨(dú)立的模塊在這個(gè)模塊里實(shí)現(xiàn)讀取寄存器的功能然后編譯、加載它即可。一個(gè)最簡(jiǎn)單的PongoOS模塊通常包含一個(gè)模塊入口函數(shù) 當(dāng)模塊被加載時(shí)自動(dòng)調(diào)用。必要的頭文件 包含PongoOS提供的API聲明和基礎(chǔ)類型定義。一個(gè)模塊描述結(jié)構(gòu)體 告訴PongoOS這個(gè)模塊的名字、入口點(diǎn)等信息。PongoOS的構(gòu)建系統(tǒng)會(huì)幫你把.c文件編譯成.ko內(nèi)核對(duì)象文件然后你可以在PongoOS的Shell里使用modload命令來(lái)加載它。3.3 編譯與部署到設(shè)備配置與編譯 進(jìn)入PongoOS源碼目錄根據(jù)項(xiàng)目的README指定正確的目標(biāo)設(shè)備如T8010對(duì)應(yīng)A9/A10XT8101對(duì)應(yīng)M1等運(yùn)行make命令進(jìn)行編譯。這會(huì)生成一個(gè)build目錄里面包含PongoOS的鏡像文件可能是.img4或.bin和模塊文件。引導(dǎo)設(shè)備進(jìn)入PongoOS 這通常需要利用設(shè)備的DFU設(shè)備固件升級(jí)模式。對(duì)于基于checkra1n漏洞的設(shè)備A5-A11可以使用checkra1n工具鏈來(lái)引導(dǎo)。對(duì)于更新的設(shè)備則需要依賴其他引導(dǎo)方法如palera1n等。這一步具有風(fēng)險(xiǎn)可能導(dǎo)致設(shè)備暫時(shí)無(wú)法進(jìn)入正常系統(tǒng)請(qǐng)務(wù)必在備用設(shè)備或完全了解后果的情況下操作。傳輸模塊 當(dāng)設(shè)備成功運(yùn)行PongoOS后通常會(huì)開啟一個(gè)網(wǎng)絡(luò)服務(wù)TFTP或通過(guò)串口提供Shell。你可以將編譯好的.ko模塊文件傳輸?shù)皆O(shè)備的臨時(shí)文件系統(tǒng)中。4. 核心實(shí)現(xiàn)編寫PongoOS模塊讀取CPU寄存器現(xiàn)在進(jìn)入最核心的編碼環(huán)節(jié)。我們將在PongoOS的環(huán)境下編寫一個(gè)C語(yǔ)言模塊來(lái)讀取ID_AA64ISAR0_EL1。4.1 創(chuàng)建模塊源代碼文件我們?cè)赑ongoOS源碼樹中找一個(gè)合適的位置比如modules/目錄下創(chuàng)建一個(gè)新目錄cpuid_read并在里面創(chuàng)建main.c。// modules/cpuid_read/main.c #include pongo.h // PongoOS核心頭文件提供了基本類型、打印函數(shù)等 // 模塊的入口函數(shù)當(dāng)使用 modload 加載此模塊時(shí)被調(diào)用 void module_entry() { // 使用內(nèi)聯(lián)匯編讀取 ID_AA64ISAR0_EL1 寄存器 uint64_t isar0_value; __asm__ volatile(mrs %0, ID_AA64ISAR0_EL1 : r (isar0_value)); // 將讀取到的64位值以十六進(jìn)制打印出來(lái) printf([CPUID] ID_AA64ISAR0_EL1 0x%016llx\n, isar0_value); // 解析并打印關(guān)鍵字段 (基于ARM架構(gòu)手冊(cè)的通用定義) printf([CPUID] Parsing key fields:\n); // 提取并判斷AES支持 (bits [63:56]) uint8_t aes_field (isar0_value 56) 0xFF; if(aes_field 0x1) { printf( - AES: Supported (0x%02x)\n, aes_field); } else { printf( - AES: Not supported or unknown (0x%02x)\n, aes_field); } // 提取并判斷SHA2支持 (bits [51:48]) uint8_t sha2_field (isar0_value 48) 0xF; switch(sha2_field) { case 0x1: printf( - SHA256: Supported\n); break; case 0x2: printf( - SHA512: Supported\n); break; case 0x3: printf( - SHA256 SHA512: Supported\n); break; default: printf( - SHA2: Not supported or unknown (0x%01x)\n, sha2_field); } // 提取并判斷CRC32支持 (bits [47:44]) uint8_t crc32_field (isar0_value 44) 0xF; if(crc32_field 0x1) { printf( - CRC32: Supported\n); } else { printf( - CRC32: Not supported or unknown (0x%01x)\n, crc32_field); } // 提取并判斷Atomic原子操作支持 (bits [43:40]) uint8_t atomic_field (isar0_value 40) 0xF; if(atomic_field 0x2) { printf( - Atomic (LSE): Supported\n); } else { printf( - Atomic (LSE): Not supported or unknown (0x%01x)\n, atomic_field); } // 可以繼續(xù)添加其他你感興趣的字段解析... printf([CPUID] Module finished.\n); } // PongoOS模塊聲明 // 這個(gè)結(jié)構(gòu)體告訴PongoOS加載器關(guān)于本模塊的信息 struct pongo_module my_module { .name cpuid_reader, .init module_entry, // 初始化函數(shù)即我們的入口 .exit 0, // 沒(méi)有退出清理函數(shù) };4.2 代碼詳解與內(nèi)聯(lián)匯編這段代碼的核心是這一行內(nèi)聯(lián)匯編__asm__ volatile(mrs %0, ID_AA64ISAR0_EL1 : r (isar0_value));__asm__ volatile: 告訴編譯器插入一段內(nèi)聯(lián)匯編代碼volatile關(guān)鍵字防止編譯器優(yōu)化掉這條看似“無(wú)副作用”的讀取指令。mrs %0, ID_AA64ISAR0_EL1: 這是ARM64匯編指令。MRS指令用于將系統(tǒng)寄存器的值移動(dòng)到通用寄存器中。%0是一個(gè)占位符代表后面的第一個(gè)輸出操作數(shù)。: r (isar0_value): 這是輸出操作數(shù)部分。r表示這是一個(gè)只寫()的操作數(shù)并且要求編譯器分配一個(gè)通用寄存器(r)來(lái)存放它。(isar0_value)指定這個(gè)操作數(shù)對(duì)應(yīng)的C變量。執(zhí)行完這條指令后ID_AA64ISAR0_EL1寄存器的值就被存儲(chǔ)到了C變量isar0_value中。接下來(lái)的代碼就是標(biāo)準(zhǔn)的C語(yǔ)言位操作通過(guò)右移()將感興趣的位域移動(dòng)到最低位然后通過(guò)掩碼()操作提取出指定位數(shù)的值最后根據(jù)ARM架構(gòu)手冊(cè)的定義進(jìn)行判斷和打印。4.3 修改構(gòu)建系統(tǒng)Makefile為了讓我們的模塊能被編譯需要在modules/目錄的Makefile或相應(yīng)的構(gòu)建配置中添加我們的模塊。通常PongoOS的模塊構(gòu)建系統(tǒng)是自動(dòng)掃描的但也可能需要手動(dòng)添加一行。例如在modules/Makefile中MODULES cpuid_read然后在頂層目錄執(zhí)行make時(shí)構(gòu)建系統(tǒng)會(huì)自動(dòng)進(jìn)入modules/cpuid_read/目錄并按照該目錄下的module.mk如果有或默認(rèn)規(guī)則進(jìn)行編譯。5. 實(shí)戰(zhàn)操作加載模塊與結(jié)果分析假設(shè)你已經(jīng)成功編譯并將生成的cpuid_read.ko文件傳輸?shù)搅诉\(yùn)行著PongoOS的設(shè)備上。5.1 在PongoOS Shell中加載模塊通過(guò)串口或網(wǎng)絡(luò)連接到PongoOS的Shell。使用ls或dir命令確認(rèn)你的.ko文件在當(dāng)前位置。使用modload命令加載模塊PongoOS modload cpuid_read.ko如果模塊代碼編寫正確加載命令執(zhí)行后module_entry()函數(shù)會(huì)立即被調(diào)用你將在終端看到打印的輸出信息。5.2 解讀輸出結(jié)果假設(shè)在一臺(tái)蘋果M1設(shè)備上你看到了如下輸出[CPUID] ID_AA64ISAR0_EL1 0x0000111120211120 [CPUID] Parsing key fields: - AES: Supported (0x01) - SHA256 SHA512: Supported - CRC32: Supported - Atomic (LSE): Supported [CPUID] Module finished.結(jié)果分析原始值0x0000111120211120 這就是ID_AA64ISAR0_EL1寄存器的完整內(nèi)容。AES字段 (0x01) 符合預(yù)期現(xiàn)代CPU基本都支持AES-NI指令加速。SHA2字段 解析顯示同時(shí)支持SHA256和SHA512這對(duì)于加密和完整性校驗(yàn)性能提升巨大。CRC32字段 支持這對(duì)網(wǎng)絡(luò)數(shù)據(jù)包校驗(yàn)、文件系統(tǒng)等場(chǎng)景很重要。Atomic字段 支持LSELarge System Extensions這是ARMv8.1引入的增強(qiáng)原子操作指令在多核、多線程編程中能顯著提升鎖和原子變量的操作性能是服務(wù)器級(jí)和高性能核心的重要標(biāo)志。這個(gè)結(jié)果完美印證了蘋果M1芯片作為一款高性能SoC具備了完整的現(xiàn)代ARMv8指令集擴(kuò)展支持。如果你在一臺(tái)更老的A9芯片設(shè)備上運(yùn)行可能會(huì)發(fā)現(xiàn)Atomic字段不支持LSE值為0b0001或0b0000這反映了不同代際CPU的功能差異。5.3 常見問(wèn)題與排查編譯錯(cuò)誤unknown register name ‘ID_AA64ISAR0_EL1’ in asm 這通常是因?yàn)榫幾g器或匯編器不認(rèn)識(shí)這個(gè)系統(tǒng)寄存器名。確保你使用了正確的ARM64工具鏈并且該工具鏈支持的架構(gòu)版本足夠新包含了該寄存器的定義。有時(shí)可能需要使用寄存器的編碼而非名字但GCC/Clang通常支持這些標(biāo)準(zhǔn)寄存器名。加載模塊時(shí)系統(tǒng)掛起或報(bào)錯(cuò)檢查權(quán)限 讀取ID_AA64ISAR0_EL1需要在足夠的異常級(jí)別EL1或更高。確保PongoOS運(yùn)行在正確的EL上。通常引導(dǎo)后的PongoOS運(yùn)行在EL1權(quán)限是足夠的。檢查模塊兼容性 確保模塊是針對(duì)當(dāng)前運(yùn)行的PongoOS內(nèi)核版本編譯的。不同版本的PongoOS內(nèi)核API可能有細(xì)微變化。簡(jiǎn)化代碼 先將模塊代碼精簡(jiǎn)到只剩一個(gè)printf(“Hello\n”)測(cè)試是否能正常加載運(yùn)行排除基礎(chǔ)環(huán)境問(wèn)題。讀取到的值全是0或看起來(lái)不合理確認(rèn)寄存器名 再次核對(duì)ID_AA64ISAR0_EL1的拼寫。ARM寄存器名對(duì)大小寫敏感。檢查內(nèi)聯(lián)匯編語(yǔ)法 確保mrs指令的語(yǔ)法正確輸出操作數(shù)約束匹配。芯片特異性 極少數(shù)情況下某些定制SoC可能會(huì)以非標(biāo)準(zhǔn)方式實(shí)現(xiàn)或報(bào)告特性。應(yīng)以芯片數(shù)據(jù)手冊(cè)為準(zhǔn)但蘋果的CPU通常嚴(yán)格遵循ARM規(guī)范。6. 擴(kuò)展探索超越ID_AA64ISAR0_EL1成功讀取ID_AA64ISAR0_EL1只是一個(gè)開始。ARMv8的ID寄存器家族非常龐大它們共同描繪了CPU的完整能力畫像。你可以用完全相同的方法去探索其他關(guān)鍵寄存器ID_AA64ISAR1_EL1 包含更多指令集屬性例如對(duì)RAS可靠性、可用性、可服務(wù)性擴(kuò)展、DPB數(shù)據(jù)持久化屏障等的支持。ID_AA64MMFR0_EL1/ID_AA64MMFR1_EL1 內(nèi)存模型特性寄存器。可以查詢支持的物理地址范圍PA size、虛擬地址范圍VA size、內(nèi)存屬性等對(duì)于系統(tǒng)軟件和虛擬化開發(fā)至關(guān)重要。ID_AA64DFR0_EL1 調(diào)試特性寄存器。告訴你CPU支持哪些調(diào)試功能如硬件斷點(diǎn)數(shù)量、觀察點(diǎn)數(shù)量、PMU性能監(jiān)控單元版本等。MIDR_EL1 主ID寄存器。直接告訴你這顆CPU的核心是誰(shuí)設(shè)計(jì)的ARM還是蘋果、架構(gòu)版本如ARMv8.5、核心型號(hào)如Cortex-A76、修訂版本號(hào)等最根本的身份信息。你只需要將代碼中的寄存器名替換掉并查閱ARM手冊(cè)了解新寄存器的位域定義就可以輕松獲取這些信息。例如讀取MIDR_EL1uint64_t midr_value; __asm__ volatile(mrs %0, MIDR_EL1 : r (midr_value)); printf(CPU Implementer: 0x%02lx, Architecture: 0x%01lx, Variant: 0x%01lx, PartNum: 0x%03lx, Revision: 0x%01lx\n, (midr_value 24) 0xFF, // 設(shè)計(jì)者0x41ARM0x61Apple (midr_value 16) 0xF, // 架構(gòu)版本 (midr_value 20) 0xF, // 變體 (midr_value 4) 0xFFF, // 核心部件號(hào) midr_value 0xF); // 修訂版本7. 從寄存器到應(yīng)用這些信息有什么用你可能覺(jué)得讀幾個(gè)寄存器值不過(guò)是極客的玩具。但在實(shí)際開發(fā)和研究中這些信息是做出正確技術(shù)決策的基礎(chǔ)。運(yùn)行時(shí)特性檢測(cè)與優(yōu)化 這是最直接的應(yīng)用。一個(gè)高性能的加密庫(kù)如OpenSSL在初始化時(shí)可以通過(guò)讀取ID_AA64ISAR0_EL1來(lái)檢測(cè)CPU是否支持AES、SHA2、SHA3指令從而動(dòng)態(tài)選擇使用硬件加速的匯編代碼路徑還是回退到純軟件實(shí)現(xiàn)。這比在編譯時(shí)靜態(tài)判斷CPU型號(hào)要精確和靈活得多。系統(tǒng)軟件與虛擬化 操作系統(tǒng)內(nèi)核或Hypervisor虛擬機(jī)監(jiān)控器需要知道CPU的詳細(xì)特性來(lái)正確配置自己。例如通過(guò)ID_AA64MMFR0_EL1知道物理地址位數(shù)來(lái)決定頁(yè)表的結(jié)構(gòu)通過(guò)ID_AA64ISAR0_EL1知道是否支持LSE原子指令來(lái)決定使用哪種同步原語(yǔ)實(shí)現(xiàn)這對(duì)多核性能影響巨大。安全研究與漏洞分析 某些安全漏洞或緩解措施與特定的CPU微架構(gòu)或功能相關(guān)。通過(guò)讀取ID寄存器可以精確識(shí)別CPU的型號(hào)和步進(jìn)判斷其是否受某個(gè)特定漏洞影響或者是否已經(jīng)具備了某些緩解措施如指針認(rèn)證PAC。固件與引導(dǎo)程序開發(fā) 在像PongoOS這樣的底層環(huán)境中了解硬件能力是進(jìn)行任何實(shí)質(zhì)性操作的前提。比如在初始化緩存、配置內(nèi)存控制器、設(shè)置異常向量表之前都需要先查詢CPU的相關(guān)特性。調(diào)試與性能剖析ID_AA64DFR0_EL1中關(guān)于PMU性能監(jiān)控單元的信息決定了你可以使用哪些硬件性能計(jì)數(shù)器。這對(duì)于進(jìn)行底層的性能剖析和瓶頸定位是不可或缺的。所以這次在PongoOS中讀取ID_AA64ISAR0_EL1的實(shí)踐不僅僅是一次簡(jiǎn)單的寄存器操作更是一把鑰匙它打開了直接與硬件對(duì)話、深入理解你所使用的計(jì)算平臺(tái)能力的大門。掌握了這個(gè)方法你就擁有了在蘋果ARM64生態(tài)中進(jìn)行更深層次開發(fā)、調(diào)試和優(yōu)化的基礎(chǔ)能力。下次當(dāng)你遇到一個(gè)需要特定CPU功能才能運(yùn)行的軟件時(shí)你不再需要盲目猜測(cè)而是可以親手驗(yàn)證。