第 20 章的流水線裡,、、 已經輪番上陣,但當時它們只是配角。本章把工具升格為主角: 這些程式從哪裡來、彼此什麼關係、每個常用旗標解決什麼問題。 你會發現它們全部出自同一個專案————而且與 Linux kernel 裡的 ACPI 直譯器系出同門。

把編譯器輸出讀懂、把反組譯極限摸清,是進入 Module 10 除錯實戰前的最後地基。本章操作全在使用者空間完成, 不需要重開機,放心大膽地玩。

  • 說明 ACPICA 專案的角色與工具家族,列出官方文件及各自的 適用時機。

  • 安裝 acpica-tools,讀懂 的輸出統計 與 Error/Warning/Remark 訊息等級。

  • 活用 、、、、 、、 等常用旗標。

  • 用 與 取得並抽出 系統的 ACPI 表。

  • 用 在使用者空間載入 AML、執行 Method。

  • 說明 roundtrip 不保證 byte-identical 的原因與正確比對法。

ACPICA:一套核心、整組工具

(ACPI Component Architecture)是 Intel 維護的 ACPI 參考實作,開放原始碼(GitHub 上的 )。 它的核心是一個與作業系統無關的 ACPI 直譯器——Linux kernel 的 內嵌的就是這一套。 同一份核心配上不同外殼,就變出整組工具:

ACPICA(github.com/acpica/acpica) ├── 核心直譯器 ── 內嵌進 kernel,也是 acpiexec 的心臟 └── 使用者工具 ├── iasl ← 編譯器 + 反組譯器(本章主角) ├── acpidump ← 從執行中系統倒出所有表 ├── acpixtract ← 從 dump 輸出抽出二進位表 ├── acpihelp ← 離線查 ASL 關鍵字與預定義名稱 └── acpibin ← AML 二進位層級比對/檢查

Linux 開機訊息裡的 與 的版本是同一種日期式版號,因為兩者共用同一份 核心原始碼。實用推論:dmesg 裡的 ACPI 錯誤訊息(如 )與 acpiexec 印的完全同格式 ——在使用者空間重現 bug,往往比重開機快十倍。

工具會用了,語意疑義還是要回到文件。官方文件就這五份, 建議全部下載存檔(acpica.org 與 uefi.org 皆可取得):

L4.9cmL5.2cmL3.3cm 文件 & 內容 & 什麼時候查
ACPI Specification & 語言與表格式的最終權威 & 語意有疑義時
ASL Tutorial & 官方入門教學 & 新手起步
Introduction to ASL 2.0 & ASL+ symbolic 語法總覽 & 讀寫新式語法
iASL Compiler User Guide & 旗標與訊息的完整參考 & 查旗標與訊息
ACPICA User Guide and Programmer Reference & 核心架構與 API & OS 端整合

安裝與版本

主流平台都有現成套件,套件名稱統一叫 (macOS 上是 ):

\(sudo apt install acpica-tools # Debian / Ubuntu\) sudo dnf install acpica-tools # Fedora / RHEL \(brew install acpica # macOS\) git clone https://github.com/acpica/acpica.git # or: source \(cd acpica && make -j8 && sudo make install\) iasl -v Intel ACPI Component Architecture ASL+ Optimizing Compiler/Disassembler version 20240927

版本以日期命名。處理 vendor DSDT 盡量用新版;你用哪一版編譯, 會被寫進表頭的 Creator Revision(第 18 章),將來查得到。

第一次編譯:讀懂 iasl 的輸出

寫一份最小但五臟俱全的測試檔:

DefinitionBlock ("test.aml", "SSDT", 2, "DEMO", "TOOLTEST", 0x00000001)

Name (MSG, "Hello from AML")

Method (ADD2, 2, NotSerialized) Return (Arg0 + Arg1)

Method (MAIN, 0, NotSerialized) Debug = MSG Return (ADD2 (2, 3))

基本指令就是 ,成功時產生 :

\(iasl test.dsl ASL+ Optimizing Compiler/Disassembler version 20240927 ASL Input: test.dsl - 16 lines, 420 bytes, 6 keywords AML Output: test.aml - 100 bytes, 3 named objects, 3 executable opcodes Compilation successful. 0 Errors, 0 Warnings, 0 Remarks, 1 Optimizations \end{shell} ASL Input 統計原始碼規模;AML Output 告訴你位元組碼多大、掛了 幾個具名物件;最後一行是四種訊息的計數。等級的意義: \begin{center} \small \begin{tabular}{llL{5.2cm}l} \toprule \rowcolor{acpiblue!10} \textbf{等級} & \textbf{編號} & \textbf{意義} & \textbf{產出 .aml?} \\ \midrule Error & 6xxx & 語法或語意錯誤 & 否 \\ Warning & 3xxx & 很可能是問題,需逐條判讀 & 是 \\ Remark & 2xxx & 風格與效率提醒 & 是 \\ Optimization & —— & 編譯器做了等價改寫的通知 & 是 \\ \bottomrule \end{tabular} \end{center} 訊息格式固定:先印出檔名、行號與該行原文,再印等級、編號與說明, \code{\^{}} 指向出錯位置。故意寫壞一份來看: \begin{asl}[title={bad.dsl(片段):兩個經典錯誤}] Method (BAD0, 1, NotSerialized) { Store (Arg0, Local0) // Local0 never read back Return (Local3) // Local3 never initialized } \end{asl} \begin{shell}[title={錯誤訊息的長相}]\) iasl bad.dsl

bad.dsl 6: Store (Arg0, Local0) Warning 3144 - Method Local is set but never used ^ (Local0)

bad.dsl 7: Return (Local3) Error 6066 - Method local variable is not initialized ^ (Local3)

一個 Warning、一個 Error:前者放行,後者直接擋下 產出。訊息編號穩定,可拿去搜尋,也可餵給 選擇性消音(21.5 節)。

反組譯:-d 與 -e

iasl 的另一個身分是反組譯器。 吃二進位表、 吐回 :

\(iasl -d -p round test.aml AML/ASL+ Disassembler version 20240927 Loading Acpi table from file test.aml Disassembly completed ASL Output: round.dsl - 1207 bytes \end{shell} 這裡順手用了 \code{-p round} 指定輸出檔名前綴:不加的話輸出叫 \code{test.dsl},會直接\textbf{覆蓋}你的原始碼——新手最常見的 資料損失事故。反組譯真實系統的 DSDT 時,還必須帶 \code{-e} 把其他表餵進來當符號參考(\code{iasl -e ssdt*.dat -d dsdt.dat}), 原因第 20 章拆解過。\code{round.dsl} 與 \code{test.dsl} 語意相同但不會一模一樣——深層原因與正確的比對方式留到 21.8 節。 \section{常用旗標速查} 先上總表,再逐一示範最重要的幾個: \begin{center} \small \begin{tabular}{lL{6.0cm}L{4.6cm}} \toprule \rowcolor{acpiblue!10} \textbf{旗標} & \textbf{作用} & \textbf{備註} \\ \midrule \code{-d} & 反組譯 \code{.dat}/\code{.aml} 成 \code{.dsl} & 輸出同名檔,小心覆蓋 \\ \code{-e} & 反組譯時提供外部符號參考表 & 多表系統必帶(第 20 章) \\ \code{-da} & 一次反組譯多張表,共用 namespace & \code{iasl -da dsdt.dat ssdt*.dat} \\ \code{-p NAME} & 指定輸出檔名前綴 & 避免覆蓋原檔 \\ \code{-tc} & 額外輸出 C hex 陣列(\code{.hex}) & 給 EDK II 嵌入韌體用 \\ \code{-l} & 產生 mixed listing(\code{.lst}) & ASL 對照 AML opcode \\ \code{-g} & 從執行中 OS 取得系統表 & 僅 Windows;Linux 用 acpidump \\ \code{-vw ID} & 抑制特定編號的訊息 & 如 \code{-vw 3144} \\ \code{-f} & 忽略錯誤強制產出 AML & 危險,除錯限定 \\ \code{-h} & 完整旗標說明 & \code{-v} 顯示版本 \\ \bottomrule \end{tabular} \end{center} \subsection{-tc:把 AML 變成 C 陣列} 韌體專案(EDK II)常把 AML 以 C 陣列形式連結進 BIOS 映像。 \code{iasl -tc test.dsl} 除了 \code{test.aml} 還會生出 \code{test.hex}: \begin{clang}[title={test.hex(節錄)}] /* * Intel ACPI Component Architecture * Compilation of "test.dsl" - C source code output * AML code block contains 0x64 bytes */ unsigned char AmlCode[] = { 0x53,0x53,0x44,0x54,0x64,0x00,0x00,0x00, /* "SSDTd..." */ 0x02,0x1B,0x44,0x45,0x4D,0x4F,0x00,0x00, /* "..DEMO.." */ ... }; \end{clang} 開頭四個位元組就是簽章 \code{SSDT}——第 18 章的表頭知識再次 通用;EDK II 的平台程式碼引用這個陣列,把表安裝進記憶體。 \subsection{-l:mixed listing,理解 AML 的利器} \code{iasl -l test.dsl} 產生 \code{test.lst},把每行 ASL 與它編譯出的 AML 位元組\textbf{並排列出}: \begin{shell}[title={test.lst(節錄)}] 6: Method (ADD2, 2, NotSerialized) 000000E1 14 0B 41 44 44 32 02 8: Return (Arg0 + Arg1) 000000E8 A4 72 68 69 00 \end{shell} 逐位元組讀:\code{14} 是 MethodOp;\code{0B} 是 PkgLength (區塊共 11 位元組);\code{41 44 44 32} 是 ASCII 的 \code{ADD2};\code{02} 是旗標(2 個參數、NotSerialized)。 下一段 \code{A4} 是 ReturnOp、\code{72} 是 AddOp、\code{68 69} 是 Arg0 與 Arg1、\code{00} 是空目標。想確認編譯器生了什麼碼、 或手上只有 AML 片段要人肉解碼時,mixed listing 是最快的對照表。 \subsection{-g 與 acpidump 的分工} \code{iasl -g} 會呼叫 OS 介面抓下執行中系統的表,但只在 \textbf{Windows} 上有實作(需管理員權限);Linux 上由 \code{acpidump} 負責,直接讀 \code{/sys/firmware/acpi/tables/}。 功能等價、工具不同,跨平台時記得換手。 \section{acpidump 與 acpixtract} 這對搭檔在第 20 章登場過,這裡補齊完整用法: \begin{center} \small \begin{tabular}{lL{4.6cm}lL{4.2cm}} \toprule \rowcolor{acpiblue!10} \multicolumn{2}{l}{\textbf{acpidump}} & \multicolumn{2}{l}{\textbf{acpixtract}} \\ \midrule (無參數) & 全部表倒成十六進位文字 & (無參數) & 只抽 DSDT 與 SSDT \\ \code{-o F} & 輸出導向檔案 F & \code{-a} & 抽出全部的表 \\ \code{-b} & 每張表直接存成二進位 & \code{-l} & 只列出有哪些表 \\ \code{-n SIG} & 只倒指定簽章 & \code{-s SIG} & 只抽指定簽章 \\ \bottomrule \end{tabular} \end{center} \begin{shell}[title={組合技示範}]\) sudo acpidump -o acpi.out # dump everything \(acpixtract -l acpi.out # what is inside? DSDT - 183615 bytes SSDT - 933 bytes ...\) acpixtract -s DSDT acpi.out # extract one signature Acpi table [DSDT] - 183615 bytes written to dsdt.dat

\(sudo acpidump -b && ls *.dat # or: skip the text step dsdt.dat facp.dat ssdt1.dat ssdt2.dat \end{shell} \code{acpidump} 需要 root;\code{-b} 直接產出二進位、省掉 acpixtract 一步,適合自動化腳本。文字模式的 \code{acpi.out} 則適合連同錯誤報告寄給別人——一個檔案裝下整台機器的 ACPI 狀態。 \section{acpiexec:不開機就執行 AML} \code{acpiexec} 把 ACPICA 核心直譯器包成使用者空間程式: 載入你的表、建出 Namespace、互動式執行 Method ——不必燒韌體、不必重開機: \begin{shell}[title={第一次 acpiexec(輸出節錄)}]\) acpiexec test.aml

Intel ACPI Component Architecture AML Execution/Debug environment version 20240927

- methods 2 arguments arguments

- execute MAIN Executing String [0x0E] "Hello from AML" Execution of returned object, external buffer length 18 [Integer] = 0000000000000005

提示符 之後是除錯器指令: 列出所有 Method、 呼叫指定 Method(可帶參數)、 傾印整棵樹。Debug 物件的輸出直接印在畫面上 ——真機上要開除錯選項才看得到的東西,這裡免費; 第 22、23 章將以 acpiexec 為主軸做整套 AML 除錯。

Roundtrip:反組譯再重編不是影印機

很多人以為 反組譯再編回去,會得到一模一樣的 位元組——實際上不保證,原因:

  • 反組譯是重建,不是還原:註解、排版、include 結構在編譯時就消失了,永遠回不來。

  • ResourceTemplate 被巨集化:反組譯器把 resource descriptor 重建成巨集寫法,重編後編碼細節可能與原表不同。

  • External 與最佳化:反組譯器自動補 External; 編譯器又會折疊常數、精簡 opcode(統計裡的 Optimizations 就是次數)。

  • 版本差異:不同版 iasl 產碼策略不同,Creator Revision 欄位本身就會變。

正確的驗證是比語意不比位元組:原始與重編的 各自反組譯,diff 兩份 ——編譯器的重寫會被 同樣的反組譯規則抹平,剩下的差異才是你真正改到的東西。

  • 的輸出與輸入同名(只換副檔名),會無預警 覆蓋既有 ;用 防呆,並永遠保留原始 備份。

  • 別拿「重編後 byte 數不同」判斷自己改壞了東西; 用雙反組譯 diff 法。

  • 重編 vendor 表用新版 iasl 並記錄版本;跨版本混用會讓 diff 全是雜訊。

用本章的 完成三個回合,全程不需要 root:

  1. 編譯:,確認 0 Errors, 記下 AML 大小與 named objects 數量。

  2. 反組譯:;與原始檔對照, 找出至少三處差異(註解消失、寫法互換、排版重排)。

  3. 對照 listing:,在 找出 Method 的 、Return 的 與 Add 的 ,親手把一行 ASL 對應到它的位元組。

收尾加碼:把同一套流程套在你機器的某張小 SSDT 上。

  • ACPICA 是 Intel 維護的參考實作;kernel 內嵌同一套核心, 全家桶工具全部同源。

  • iasl 身兼編譯器與反組譯器:Error(6xxx)擋下產出, Warning(3xxx)與 Remark(2xxx)放行但要判讀。

  • 反組譯用 、多表加 、 防覆蓋; 出 C 陣列給 EDK II; 的 mixed listing 是 ASL 對照 AML 的最佳教材。

  • 抓系統表僅限 Windows;Linux 由 acpidump 負責, 直接出二進位。

  • acpiexec 不開機就能載表、跑 Method、看 Debug 輸出——第 22、23 章的主戰場。

  • roundtrip 不保證 byte-identical;比對改動用 「雙反組譯再 diff」法。

  1. Errors/Warnings/Remarks/Optimizations 各代表什麼? 哪一種會擋下 產出?

  2. 為什麼反組譯 DSDT 時常要用 帶上所有 SSDT? 沒帶齊時會出現哪兩類症狀?

  3. mixed listing 裡 Method 那行的第一個位元組是 ,後面的 是什麼? 對照第 3 章的 AML 知識回答。

  4. 解釋 roundtrip 為何不保證 byte-identical,並寫出驗證 「只改到我想改的地方」的三條指令。

  5. 動手做:完成 labbox 三連發後,用 執行 帶兩個自選參數。