【docker+gdb】调试 PHP 源码,看 strval 函数 C 实现
内容导读
收集整理的这篇技术教程文章主要介绍了【docker+gdb】调试 PHP 源码,看 strval 函数 C 实现,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含15121字,纯文字阅读大概需要22分钟。
内容图文
php strval 函数的作用很简单,就是你给他一个值,他给你返回字符串类型。算是一个比较简单的函数了,我们来通过 gdb 来一探究竟。
通过本文,你可以窥探下
● gdb 的简单使用
● gdb gui 模式初探
● 看看平时写的 PHP 代码在 C 语言里的样子
● 对使用 gdb 调试 php 代码有个初步了解
● 对了,文末有一些截图,不要错过
采购食材
● 电脑一台
● docker 和 docker-compose
gdb 也好, PHP 也好,都打包成 docker 镜像啦,开袋即食,甚好。
备菜环节
1、使用 docker 拉取环境
# 拉取准备好的环境git clone https://github.com/rovast/docker-examples.git# 进入项目cd docker-examples/gdb-php-src/# 启动,会经历一个漫长又不太漫长的等待,看你网速docker-compose up -d
关于容器内的环境,大家可以看看 dockerfile
其实很简单,就是基于 gcc 官方镜像构建,然后增加了 vim gdb,并且下载了 php7.0.0 的源码,按照 debug 参数进行编译
显示如下
Creating network "gdb-php-src_default" with the default driverCreating gdb-php-src ... done
2、进入容器
docker exec -it gdb-php-src bash### 显示下面的东西,表示你已经进入到容器内了 ####root@71a98d1bc1a6:/home#
我们看看容器内的环境(php 以及 gdb)
### 我们在容器内看看环境root@71a98d1bc1a6:/home# lsphp-7.0.0 start.mdroot@71a98d1bc1a6:/home# gdb -vGNU gdb (Debian 7.12-6) 7.12.0.20161007-gitCopyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word".root@71a98d1bc1a6:/home# php -vPHP 7.0.0 (cli) (built: Apr 17 2019 13:33:30) ( NTS DEBUG )Copyright (c) 1997-2015 The PHP GroupZend Engine v3.0.0, Copyright (c) 1998-2015 Zend Technologiesroot@71a98d1bc1a6:/home#
开火(请在容器内操作)
1、新建测试文件
root@71a98d1bc1a6:/home# vi test.php
输入以下内容
<?phpstrval(1234);
这个文件干的事情就比较简单了,就是把 -1234 [整形] 转换为 -1234 [字符串]
2、开始调试,进入 gdb
接下来车速较快,各位按步骤跟上
输入 gdb php,开始调试
root@71a98d1bc1a6:/home# gdb phpGNU gdb (Debian 7.12-6) 7.12.0.20161007-gitCopyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from php...done.(gdb)
3、打一些断点(敲命令时可以用 tab 补全)
(gdb) b zend_long_to_strBreakpoint 1 at 0x810423: file /home/php-7.0.0/Zend/zend_operators.c, line 2743.(gdb) b zend_print_ulong_to_bufBreakpoint 2 at 0x5f387b: zend_print_ulong_to_buf. (13 locations)(gdb)
这里在关键函数 zend_long_to_str 和 zend_print_ulong_to_buf 打了断点。
b 在 gdb 中是 breakpoint 缩写,后面可以加函数名,或者当前文件的行号都是可以的
4、执行,查看断点值
(gdb) r test.php # 执行我们刚才的那个 PHP 文件Starting program: /usr/local/bin/php test.php[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".warning: File "/usr/local/lib64/libstdc++.so.6.0.25-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".To enable execution of this file add add-auto-load-safe-path /usr/local/lib64/libstdc++.so.6.0.25-gdb.pyline to your configuration file "/root/.gdbinit".To completely disable this security protection add set auto-load safe-path /line to your configuration file "/root/.gdbinit".For more information about this security protection see the"Auto-loading safe path" section in the GDB manual. E.g., run from the shell: info "(gdb)Auto-loading safe path"Breakpoint 1, zend_long_to_str (num=-1234) at /home/php-7.0.0/Zend/zend_operators.c:27432743 char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, num);(gdb)
看的好像不明了嘛, ctrl + x 后再按 a 进入 gui 模式看看
┌──/home/php-7.0.0/Zend/zend_operators.c────────────────────────────────────────────────────────────────────────────────────────────────┐ │2731 ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_DC) /* {{{ */ │ │2732 { │ │2733 zend_string *str; │ │2734 │ │2735 str = zend_strpprintf(0, "%.*G", (int) EG(precision), (double)Z_DVAL_P(op)); │ │2736 ZVAL_NEW_STR(op, str); │ │2737 } │ │2738 /* }}} */ │ │2739 │ │2740 ZEND_API zend_string* ZEND_FASTCALL zend_long_to_str(zend_long num) /* {{{ */ │ │2741 { │ │2742 char buf[MAX_LENGTH_OF_LONG + 1]; │B+>│2743 char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, num); │ │2744 return zend_string_init(res, buf + sizeof(buf) - 1 - res, 0); │ │2745 } │ │2746 /* }}} */ │ │2747 │ │2748 ZEND_API zend_uchar ZEND_FASTCALL is_numeric_str_function(const zend_string *str, zend_long *lval, double *dval) /* {{{ */ { │ │2749 return is_numeric_string_ex(ZSTR_VAL(str), ZSTR_LEN(str), lval, dval, -1, NULL); │ │2750 } │ │2751 /* }}} */ │ │2752 │ │2753 ZEND_API zend_uchar ZEND_FASTCALL _is_numeric_string_ex(const char *str, size_t length, zend_long *lval, double *dval, int allo│ │2754 { │ │2755 const char *ptr; │ │2756 int digits = 0, dp_or_e = 0; │ │2757 double local_dval = 0.0; │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘multi-thre Thread 0x7ffff7fe37 In: zend_long_to_str L2743 PC: 0x810423(gdb)
有点意思了,函数在 2743 行断住了,我们来看看 num 的值
(gdb) p num$1 = -1234(gdb)
对嘛,这个就是我们要处理的值,我们全速运行到 zend_print_long_to_buf 里看看
(gdb) cContinuing.
显示如下
(gdb) c ┌──/home/php-7.0.0/Zend/zend_operators.h────────────────────────────────────────────────────────────────────────────────────────────────┐ │781 else │ │782 ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(opcode) │ │783 │ │784 #define ZEND_TRY_UNARY_OBJECT_OPERATION(opcode) │ │785 if (UNEXPECTED(Z_TYPE_P(op1) == IS_OBJECT) │ │786 && UNEXPECTED(Z_OBJ_HANDLER_P(op1, do_operation)) │ │787 && EXPECTED(SUCCESS == Z_OBJ_HANDLER_P(op1, do_operation)(opcode, result, op1, NULL))) { │ │788 return SUCCESS; │ │789 } │ │790 │ │791 /* buf points to the END of the buffer */ │ │792 static zend_always_inline char *zend_print_ulong_to_buf(char *buf, zend_ulong num) { │B+>│793 *buf = '