您的位置 首页 php

【docker+gdb】调试 PHP 源码,看 strval 函数 C 实现

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 = '\0';                                                                                                           │   │794             do {                                                                                                                   │   │795                     *--buf = (char) (num % 10) + '0';                                                                              │   │796                     num /= 10;                                                                                                     │   │797             } while (num > 0);                                                                                                     │   │798             return buf;                                                                                                            │   │799     }                                                                                                                              │   │800                                                                                                                                    │   │801     /* buf points to the END of the buffer */                                                                                      │   │802     static zend_always_inline char *zend_print_long_to_buf(char *buf, zend_long num) {                                             │   │803             if (num < 0) {                                                                                                         │   │804                 char *result = zend_print_ulong_to_buf(buf, ~((zend_ulong) num) + 1);                                              │   │805                 *--result = '-';                                                                                                   │   │806                     return result;                                                                                                 │   │807             } else {                                                                                                               │   └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘multi-thre Thread 0x7ffff7fe37 In: zend_print_ulong_to_buf                                                               L793  PC: 0x8041fd

接下来,我们来进行一些单步调试,看看内存里的值

Breakpoint 1, zend_long_to_str (num=-1234) at /home/php-7.0.0/Zend/zend_operators.c:2743(gdb) c  #-------------> 表示继续执行Continuing.Breakpoint 2, zend_print_ulong_to_buf (buf=0x7fffffffafe4 "", num=1234) at /home/php-7.0.0/Zend/zend_operators.h:793(gdb) b 798  #-------------> 798行打个断点Breakpoint 6 at 0x5f38e2: /home/php-7.0.0/Zend/zend_operators.h:798. (13 locations)(gdb) c Continuing.Breakpoint 6, zend_print_ulong_to_buf (buf=0x7fffffffafe0 "1234", num=0) at /home/php-7.0.0/Zend/zend_operators.h:798(gdb) p buf  #-------------> 查看 buf 的值$2 = 0x7fffffffafe0 "1234"(gdb) x/10c buf  #-------------> 查看 buf 位置开始,连续 10 个以 char 为单位的内存值0x7fffffffafe0: 49 '1'  50 '2'  51 '3'  52 '4'  0 '\000'        0 '\000'        0 '\000'        0 '\000'0x7fffffffafe8: 0 '\000'        65 'A'(gdb)

我们看到,函数返回的 buf 是字符串类型的 '1234'

我们看看函数 zend_print_ulong_to_buf,其实就是从高位到低位,按个取模(除以 10,取整数部分),然后塞到 buf 缓冲区。

比较有意思的是,buf 初始化的时候指向的是缓冲区的末尾,所以填充的时候高位在最后,然后逐步往前填充低位。

最后结束的时候,buf 就是我们需要的字符串类容了

消化

其实,本文就是使用 gdb 调试了 PHP 代码,仅此而已。

更多的是给大家提供了一个直接上手玩玩的机会,你所需要的只是个 docker,然后动动手调试,很有意思。

动手试试吧,甚至,去看 C 源码吧!

附录

手摸手带你看 strval 函数 C 实现

b9fdc5047d5aec46adefdc9a388fb94.png

229a089a607c8d96094012dd1e1b0ad.png

21ba011c493c647b0457b84d41aa538.png

以上就是【docker+gdb】调试 PHP 源码,看 strval 函数 C 实现的详细内容,更多请关注求知技术网其它相关文章!

文章来源:智云一二三科技

文章标题:【docker+gdb】调试 PHP 源码,看 strval 函数 C 实现

文章地址:https://www.zhihuclub.com/3168.shtml

关于作者: 智云科技

热门文章

网站地图