[docker + GDB] debugging PHP source code, see the C implementation of strval function

Time:2021-6-8

PHP strval function is very simple, that is, you give him a value, he gives you the return string type.

It’s a relatively simple function. Let’s explore it through GDB.

Through this article, you can get a glimpse

  • Simple use of GDB
  • On GDB GUI mode
  • Look at the usual PHP code written in the C language
  • Have a preliminary understanding of debugging PHP code with GDB
  • By the way, there are some screenshots at the end of the article. Don’t miss them

Purchasing food materials

  • A computer
  • Docker and docker compose

GDB or PHP, all packaged as docker image, open bag ready to eat, very good.

Food preparation

1. Using docker to pull environment

#Pull ready environment

About the environment inside the container, you can have a lookdockerfile

In fact, it’s very simple. It’s built based on the official image of GCC, then added VIM GDB, downloaded the source code of php7.0.0, and compiled according to the debug parameters

The display is as follows

Creating network "gdb-php-src_default" with the default driver
Creating gdb-php-src ... done

2. Enter the container

docker exec -it gdb-php-src bash

Let’s look at the environment inside the container (PHP and GDB)

###Let's look at the environment inside the container

Fire (please operate in container)

1. New test file

[email protected]:/home# vi test.php

Enter the following

<?php

strval(1234);

What this file does is relatively simple, which is to convert – 1234 [shaping] to – 1234 [string]

2. Start debugging and enter GDB

Next, the speed is faster. Follow the steps

inputgdb php, start debugging

[email protected]:/home# gdb php
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (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. Make some breakpoints (you can complete them with tab when you type the command)

(gdb) b zend_long_to_str
Breakpoint 1 at 0x810423: file /home/php-7.0.0/Zend/zend_operators.c, line 2743.
(gdb) b zend_print_ulong_to_buf
Breakpoint 2 at 0x5f387b: zend_print_ulong_to_buf. (13 locations)
(gdb)

Here’s the key functionzend_long_to_strandzend_print_ulong_to_bufIt’s broken.

bIn GDB, it is the abbreviation of breakpoint, which can be followed by the function name or the line number of the current file

4. Execute, view breakpoint value

(GDB) r test. PHP # execute the PHP file we just created

I don’t know,ctrl + xThen pressaGo into GUI mode and have a look

┌──/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)

That’s interesting. The function is broken on line 2743. Let’s take a look at the value of num

(gdb) p num
$1 = -1234
(gdb)

Right, that’s what we’re going to deal with. We’re going to run at full speedzend_print_long_to_bufLook inside

(gdb) c
Continuing.

The display is as follows

(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 = '
(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
'; │ │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

Next, let’s do some single step debugging to see the values in memory

Breakpoint 1, zend_ long_ to_ str (num=-1234) at /home/php-7.0.0/Zend/zend_ operators.c:2743

We see that the buf returned by the function is a string type ‘1234’

Let’s look at the functionzend_print_ulong_to_bufIn fact, from high to low, take the modulus by one (divide by 10, take the integer part), and then plug it into the buf buffer.

Interestingly, when buf is initialized, it points to the end of the buffer, so when filling, the high bit is at the end, and then the low bit is gradually filled forward.

At the end, buf is the string class we need

Digestion

In fact, this article uses GDB to debug PHP code, that’s all.

More is to provide you with an opportunity to play directly, you just need a docker, and then start debugging, very interesting.

Try it, even, to see the C source code!

appendix

[docker + GDB] debugging PHP source code, see the C implementation of strval function

[docker + GDB] debugging PHP source code, see the C implementation of strval function

[docker + GDB] debugging PHP source code, see the C implementation of strval function

This work adoptsCC agreementReprint must indicate the author and the link of this article