使用vscode調試PHP底層C源碼 一直想著有機會調試一下php底層代碼來著,這周正好心血來潮,就跟著教程配置了一下。本篇文章是基於macOS,可能在編譯php源碼之前的步驟對使用windows的師傅沒啥可參考的。 windows下比較麻煩,主要是在編譯php源碼這一步,最方便的辦法是用docke ...
使用vscode調試PHP底層C源碼
一直想著有機會調試一下php底層代碼來著,這周正好心血來潮,就跟著教程配置了一下。本篇文章是基於macOS,可能在編譯php源碼之前的步驟對使用windows的師傅沒啥可參考的。
windows下比較麻煩,主要是在編譯php源碼這一步,最方便的辦法是用docker來遠程調試。具體可以參考這篇文章vscode遠程調試php底層代碼。使用p牛的dockerfile來自己建一個調試用docker。
說回mac下調試PHP源碼需要的準備
下載並編譯PHP
使用git來下載源碼,這樣切換PHP版本會較為方便。(不過我現在應該不會這麼做,因為下載下來的源碼並不能直接編譯成功,需要自己修改。改完的源碼不捨得切換了)
git clone https://github.com/php/php-src
cd php-src/
git checkout PHP-7.3.67
當然,不排除個人環境的原因,通過checkout切換分支,重新編譯一下還是很方便的,如果編譯不出錯的話。
如果是mac,編譯PHP前需要安裝一下最新版的bison,mac自帶的版本太老。
brew install bison
# bison的具體路徑可以通過brew list bison來查看
export PATH=/opt/homebrew/Cellar/bison/3.8.2/bin:$PATH
編譯需要調試的PHP。像我這裡就開啟了debug模式,開啟了phar擴展,如果需要開啟別的擴展,需要再./configure
命令後面自行指定。
./buildconf
./configure --disable-all --enable-debug --enable-phar --prefix=/source/php7.3.6/
make
make install
編譯完後,編譯結果都在/source/php7.3.6/
文件夾下,/source/php7.3.6/bin/php
為可執行文件。
編譯好的PHP,執行./php -v
會顯示NTS DEBUG
。
編譯過程中的錯誤
在進行make編譯的時候,碰到了兩次報錯。
第一個報錯:
/Users/niushaogang/jkbPhpPackage/php-5.4.45/main/reentrancy.c:139:23: error: too few arguments to function call, expected 3, have 2
readdir_r(dirp, entry);
~~~~~~~~~ ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/dirent.h:110:5: note: 'readdir_r' declared here
int readdir_r(DIR *, struct dirent *, struct dirent **) __DARWIN_INODE64(readdir_r);
^
1 error generated.
make: *** [main/reentrancy.lo] Error 1
根據網上的教程,瞭解到這是php源碼調用readdir_r函數的時候少傳了一個參數。
查看php-src/main/reentrancy.c
函數定義:
int readdir_r(DIR *, struct dirent *, struct dirent **)
php調用:
readdir_r(dirp, entry)
readdir_r(dirp, entry)
修改為 readdir_r(dirp, entry,&entry)
即可編譯通過
第二個報錯:
/Users/dre0m1/CTF/學習筆記/PHP源碼/php-src/Zend/zend_language_parser.y:1317:5: error: implicit declaration of function 'yystpcpy' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
yystpcpy(yyres, "end of file");
^
/Users/dre0m1/CTF/學習筆記/PHP源碼/php-src/Zend/zend_language_parser.y:1317:5: note: did you mean 'stpcpy'?
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/string.h:130:7: note: 'stpcpy' declared here
char *stpcpy(char *__dst, const char *__src);
^
/Users/dre0m1/CTF/學習筆記/PHP源碼/php-src/Zend/zend_language_parser.y:1324:29: error: implicit declaration of function 'yystrlen' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
yystr_len = (unsigned int)yystrlen(yystr);
^
/Users/dre0m1/CTF/學習筆記/PHP源碼/php-src/Zend/zend_language_parser.y:1324:29: note: did you mean 'strlen'?
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/string.h:82:9: note: 'strlen' declared here
size_t strlen(const char *__s);
^
/Users/dre0m1/CTF/學習筆記/PHP源碼/php-src/Zend/zend_language_parser.y:1345:4: error: implicit declaration of function 'yystpcpy' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
yystpcpy(yyres, buffer);
^
/Users/dre0m1/CTF/學習筆記/PHP源碼/php-src/Zend/zend_language_parser.y:1352:10: error: implicit declaration of function 'yystrlen' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
return yystrlen(yystr) - (*yystr == '"' ? 2 : 0);
^
/Users/dre0m1/CTF/學習筆記/PHP源碼/php-src/Zend/zend_language_parser.y:1365:2: error: implicit declaration of function 'yystpcpy' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
yystpcpy(yyres, yystr);
^
5 errors generated.
make: *** [Zend/zend_language_parser.lo] Error 1
報錯內容較多,出現在Zend/zend_language_parser.lo位置。當時查找了好久的資料,都沒有找到相關的內容。只知道implicit declaration of function 'yystrlen' is invalid in C99
這種報錯類型是因為缺少相應的定義,由報錯中的提示可以看出應該是缺少了yystpcpy這個函數。
訪問文件php-src/Zend/zend_language_parser.c,可以看到這樣一段代碼
格式和yystpcpy
還有yystrlen
異常的統一,我當時就懷疑應該是在這個c文件中進行了函數重命名。
代碼中出現yystpcpy
函數的位置一共有三處:
if (yyres) {
yystpcpy(yyres, "end of file");
}
return sizeof("end of file")-1;
}
yystpcpy(yyres, buffer);
yystpcpy(yyres, yystr);
看樣子應該就是stpcpy
函數沒錯了,試著在上面的#define處加入yystpcpy
與yystrlen
的定義:
#define yystpcpy zendstpcpy
#define yystrlen zenddtrlen
依舊報錯,這邊是我沒動腦子了,光想著和上面的define內容結構統一,但是strlen和stpcpy這兩個函數前面其實是不需要zend首碼的。
修改之後重新編譯,make成功。
vscode調試
用vscode打開PHP源碼,增加一個調試的配置:
選擇環境c++(GDB/LLDB)-> Default Configuration,然後會生成一個配置文件:
{
// 使用 IntelliSense 瞭解相關屬性。
// 懸停以查看現有屬性的描述。
// 欲瞭解更多信息,請訪問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "debuug php source",
"type": "cppdbg",
"request": "launch",
"program": "/Users/dre0m1/CTF/學習筆記/PHP源碼/source/bin/php",
"args": ["-f","/Users/dre0m1/CTF/學習筆記/PHP源碼/1.php"],
"stopAtEntry": true,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
}
]
}
參數詳解:
- program 可執行的PHP文件的路徑(編譯生成的php文件)
- args 傳給php的參數列表,像我上面所填寫的執行的就是
php -f /Users/dre0m1/CTF/學習筆記/PHP源碼/1.php
- cwd 當前目錄,如果調試web應用,可以改成web根目錄的路徑
- stopAtEntry 是否在main函數的時候斷下
之後就可以正常調試了。