首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

GDB 實戰:遍歷 PHP 內核 EG(symbol_table) 哈希表鍵名與值

     編輯:PHP 相關知識

背景:為什麼要用 GDB 檢查 EG(symbol_table)?

EG(symbol_table) 是 Zend 引擎執行期全域符號表(Executor Globals 結構成員),儲存當前作用域所有變數($GLOBALS 即此表)。透過 GDB 直接檢視哈希桶結構,可除錯變數異常、記憶體洩漏、擴展開發時的符號表操作。

測試環境準備

# 1. 編譯含除錯符號的 PHP
./configure --enable-debug --disable-optimization
make

# 2. 撰寫測試擴展 confirm_getGlobal_compiled
// 參考 Sara Golemon 文章,功能:傳入變數名,從 EG(symbol_table) 查找並回傳值

# 3. 測試腳本 a.php
$abc = 'string';
$def = 'string2';
var_dump(confirm_getGlobal_compiled('abc'));

GDB 實戰步驟

1. 啟動 GDB 並設定中斷點

gdb --args sapi/cli/php -c php.ini a.php
(gdb) b confirm_getGlobal_compiled.c:301  // 擴展原始碼第 301 行
(gdb) r

2. 進入 zend_hash_find 觀察哈希表結構

(gdb) n  // 執行到 zend_hash_find 呼叫前
(gdb) step  // 進入 zend_hash_find 內部
zend_hash_find (ht=0x82e3250, arKey=0x837a42c "abc", nKeyLength=4, pData=0xbfffc484)

3. 檢視 HashTable 結構體

(gdb) p *ht
$9 = {
  nTableSize = 64,
  nTableMask = 63,
  nNumOfElements = 10,        // 共 10 個元素
  nNextFreeElement = 0,
  pInternalPointer = 0x83edc98,
  pListHead = 0x83edc98,      // 雙向鏈表頭
  pListTail = 0x837a3fc,      // 雙向鏈表尾
  arBuckets = 0x83705a8,      // 雜湊桶陣列指標
  pDestructor = 0x81923b0 <_zval_ptr_dtor>,
  persistent = 0,
  nApplyCount = 0,
  bApplyProtection = 1
}

4. 遍歷雙向鏈表 (pListHead → pListNext) 查看所有鍵名

(gdb) p *ht.pListHead
$2 = { h = 2572561225, nKeyLength = 8, pData = 0x83edca4, pDataPtr = 0x83edc7c,
       pListNext = 0x8378c4c, pListLast = 0x0, pNext = 0x0, pLast = 0x0,
       arKey = "GLOBALS" }  // 第一個元素永遠是 $GLOBALS

(gdb) p *ht.pListHead.pListNext
$3 = { h = 253399445, nKeyLength = 5, pData = 0x8378c58, pDataPtr = 0x8378b60,
       pListNext = 0x8378c7c, pListLast = 0x83edc98, pNext = 0x0, pLast = 0x0,
       arKey = "_POST" }

(gdb) p *ht.pListHead.pListNext.pListNext
$4 = { arKey = "_GET", ... }

... 繼續 .pListNext 直到 pListTail ...

// 最後兩個為測試腳本定義的變數
(gdb) p *ht.pListHead.pListNext... (第 9 個)
$10 = { h = 2090069483, nKeyLength = 4, pData = 0x837a3d8, pDataPtr = 0x8379ef8,
        pListNext = 0x837a3fc, pListLast = 0x8379e8c, pNext = 0x0, pLast = 0x0,
        arKey = "abc" }

(gdb) p *ht.pListHead.pListNext... (第 10 個)
$11 = { h = 2090180660, nKeyLength = 4, pData = 0x837a408, pDataPtr = 0x8379edc,
        pListNext = 0x0, pListLast = 0x837a3cc, pNext = 0x0, pLast = 0x0,
        arKey = "def" }

5. 從 Bucket 取得實際 zval 值

Bucket 結構中 pDataPtr 指向實際 zval*(PHP 5.3 為 pDataPtr,PHP 7+ 直接內嵌 zval)。

(gdb) p *(zval *)$10->pDataPtr
$30 = {
  value = { lval = 138024112, dval = 1.28e-313,
            str = { val = 0x83a14b0 "string", len = 6 },
            ht = 0x83a14b0, obj = { handle = 138024112, handlers = 0x6 } },
  refcount__gc = 1,
  type = 6,        // IS_STRING
  is_ref__gc = 0
}

成功讀到 $abc = "string" 的字串值與長度。

6. pData vs pDataPtr 關係

(gdb) p &$10->pDataPtr
$46 = (void **) 0x839fe28
(gdb) p $10->pData
$47 = (void *) 0x839fe28
// 結論:pData 存放的是 pDataPtr 的位址 (void**),即 &pDataPtr

Bucket 結構體定義參考 (PHP 5.3)

typedef struct bucket {
    ulong h;                // 雜湊值 (數字鍵) 或字串雜湊
    uint nKeyLength;        // 鍵名長度 (含結尾 \0),0 表示數字鍵
    void *pData;            // 指向 pDataPtr 的指標 (void**)
    void *pDataPtr;         // 實際資料指標 (zval*)
    struct bucket *pListNext;  // 全域雙向鏈表下一個
    struct bucket *pListLast;  // 全域雙向鏈表上一個
    struct bucket *pNext;      // 同一雜湊桶鏈表下一個
    struct bucket *pLast;      // 同一雜湊桶鏈表上一個
    char arKey[1];          // 彈性陣列成員,存放鍵名字串
} Bucket;

常用 GDB 命令速查

命令用途
p *ht印出 HashTable 結構體
p *ht.pListHead印出鏈表頭 Bucket
p *ht.pListHead.pListNext印出下一個 Bucket (鏈式)
p *(zval*)bucket->pDataPtr解引用取得 zval 內容
p bucket->arKey[0]@bucket->nKeyLength印出完整鍵名字串
set $bucket = ht.pListHead
while($bucket) { p *(zval*)$bucket->pDataPtr; set $bucket = $bucket->pListNext }
腳本化遍歷所有變數

PHP 7+ 差異提醒

  • zval 不再分離指標,直接內嵌於 Bucket (zval val 成員取代 pDataPtr)
  • 哈希表改用 zend_array + Bucket 陣列,無雙向鏈表,改用 nNextFreeElement 與 arData 陣列索引迭代
  • GDB 腳本需依版本調整

實戰記錄整理自 xiaoq3406 專欄《gdb方式遍歷EG(symbol_table) 哈希表的key》

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved