惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
B
Blog RSS Feed
W
WeLiveSecurity
I
InfoQ
L
Lohrmann on Cybersecurity
Simon Willison's Weblog
Simon Willison's Weblog
腾讯CDC
S
Schneier on Security
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Threat Research - Cisco Blogs
P
Palo Alto Networks Blog
Attack and Defense Labs
Attack and Defense Labs
I
Intezer
Recent Commits to openclaw:main
Recent Commits to openclaw:main
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
Cisco Talos Blog
Cisco Talos Blog
T
The Exploit Database - CXSecurity.com
S
Securelist
T
Tailwind CSS Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
美团技术团队
Stack Overflow Blog
Stack Overflow Blog
T
Tor Project blog
博客园 - 叶小钗
Engineering at Meta
Engineering at Meta
Microsoft Security Blog
Microsoft Security Blog
Project Zero
Project Zero
C
Cybersecurity and Infrastructure Security Agency CISA
Apple Machine Learning Research
Apple Machine Learning Research
V
Visual Studio Blog
Know Your Adversary
Know Your Adversary
T
The Blog of Author Tim Ferriss
N
News and Events Feed by Topic
小众软件
小众软件
G
Google Developers Blog
F
Full Disclosure
O
OpenAI News
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
TaoSecurity Blog
TaoSecurity Blog
U
Unit 42
Jina AI
Jina AI
S
SegmentFault 最新的问题
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Proofpoint News Feed
Y
Y Combinator Blog
N
News and Events Feed by Topic
K
Kaspersky official blog

文章列表 - 她和她的猫

使用小鹤双拼一个月 折腾 Hugo PaperMod 主题 去长鹿旅游休博园 1Password 自动化登录堡垒机 深入 Hyperf:Inject 注解是如何工作的? 译|理解容器镜像层 使用 Rust 实现高性能预写日志(Write Ahead Log) 译|使用 Linux 命名空间隔离系统 深入 Hyperf:HTTP 服务是如何处理请求的? Linux 文件权限学习笔记 译|了解 Bitcask:基于日志结构的 KV 存储引擎 还原 SM2 压缩公钥的几种方法 深入 Hyperf:HTTP 服务启动时发生了什么? 一次 Hyperf 注解失效问题分析 将博客迁移到又拍云 译|PHP 7 新的 Hashtable 实现 排查 ES 查询问题:深入了解 json_encode() 函数 浅析 Redlock 分布式锁实现原理 Yar 源码阅读笔记:RPC 服务端 Yar 源码阅读笔记:客户端的并行调用 Yar 源码阅读笔记:客户端的同步调用 Yar 源码阅读笔记:数据传输模块 Yar 源码阅读笔记:消息编码模块 Yar 源码阅读笔记:RPC 通信协议 Yar 源码阅读笔记:开篇 PHP 8 新特性介绍 基于 OpenSSL 实现国密 SM4 加解密 什么是惊群问题 大端模式和小端模式 基于 GitHub Actions 定时推送网址到百度站长平台 聊聊五种 I/O 模型 Workerman 源码分析:文件上传 浅入浅出 HTTP 协议 PHP 网络编程:构建 MySQL 蜜罐获取攻击者微信 ID 使用 Workerman 接入 Bilibili 直播弹幕协议 使用 GitHub Actions 自动部署 Hexo 什么是二进制安全 【转载】PHP 程序员进阶之路 优化 Workerman 检查主进程是否存活的逻辑 基于 Redis 实现分布式锁 《PHP 实现 Base64 编码/解码》笔记 PHP 多进程下载必应壁纸 基于 Redis 实现延迟队列 【转载】PHP 实现 Base64 编码/解码 使用 MySQL FIELD() 函数自定义排序规则 Laravel 的 Facades 实现原理 C语言单链表实现约瑟夫环 数据结构学习笔记:顺序栈和链栈(C语言) 使用 QueryList + Redis 下载壁纸 在 Laravel 登录/注册中使用 mews/captcha 扩展包 使用 mews/captcha 扩展包为 Laravel 应用添加图片验证码 给 YOURLS 短网址系统编写插件《批量生成短网址》 给 YOURLS 短网址系统编写插件《Hello World!》 PHP 7.1 使用 json_encode 函数造成浮点类型数据出现精度问题 Laravel 授权策略(Policy)的基本使用 Ubuntu 系统下 WiFi 频繁掉线解决方法 Laravel 应用部署到 Nginx 服务器上的第一个坑 Ubuntu 16.04 LST 安装 Redis 和 Composer PHP 采集逐浪小说章节列表 Nginx 环境 SSL 强制 HTTP 301 跳转到 HTTPS 【工作&生活】第一周 ServiceStack.Redis 与多线程 Count the number of Duplicates C# Base64 加密及解密 ASP.NET MVC 使用动态类型传递数据 Django 使用 order_by() 对数据进行排序操作 ASP.NET + jQuery + AJAX 实现用户登录 ASP.NET 从客户端中检测到有潜在危险的 Request.Form 值 ASP.NET 使用 FormData 实现 AJAX 上传图片 Python 使用 BeautifulSoup 抓取网页 贪心算法之字符串的完美度 一言不和就造轮子之 CookieHelper ASP.NET 上传图片 DBHelper 类 C# 捕获屏幕源码 用 JavaScript 做一些有趣的事情 第一篇文章 好久不见。 如何防止非法调用 PHP 文件 清明小记。 PHP 生成随机字符串 Z-Blog PHP 程序做 301 跳转 PHP 处理数组常用的几个函数 Sublime Text 3 快捷键总结 PHP 使用 file_get_contents() 函数实现采集网页 使用 C# 编写学生信息管理系统 终于到了。 zblog报错: magic_quotes_gpc 和 safe_mode 世事无常。 如何使用 PHP 上传文件 安卓手机编写和运行 PHP 程序的软件 使用 AJAX 刷新、验证 PHP 图片验证码 PHP 生成图片验证码 PHP 之 session 的使用方法 PHP 之 cookie 的使用方法 PHP 实现分页原理详解 Thinkphp 框架中系统常量在 Js 文件中不解析的解决方法
PHP 源码阅读笔记:编译与调试 PHP
她和她的猫 · 2021-08-06 · via 文章列表 - 她和她的猫

基本信息

  • PHP 版本:7.1.0
  • 调试环境:Ubuntu(WSL)
  • 调试工具:GDB、Clion

下载并安装 PHP:

$ wget http://cn2.php.net/distributions/php-7.1.0.tar.gz
$ tar -xzvf php-7.1.0.tar.gz
$ cd php-7.1.0
$ ./configure --prefix=$HOME/php-7.1.0/build --enable-fpm

注意:$HOME/php-7.1.0/build 是 PHP 执行文件和库文件安装的目录,可以自定义。--enable-fpm 表示同时安装 php-fpm。

接下来修改编译优化等级,方便我们在调试的过程中查看变量的内容。

首先在 php-7.1.0/Makefile 文件中搜索 CFLAGS_CLEAN,然后将 -02 改为 -o0,表示不需要优化。

CFLAGS_CLEAN = -I/usr/include -g -O0 -fvisibility=hidden -DZEND_SIGNALS $(PROF_FLAGS)

执行 make 命令:

执行完成后,就可以在 php-7.1.0/build/bin 目录下看到 PHP 相关的可执行文件了。

pear  peardev  pecl  phar  phar.phar  php  php-cgi  php-config  phpdbg  phpize

调试 PHP

使用 Clion 进行调试

首先用 Clion 打开 php-7.1.0 目录,Clion 会在根目录下自动创建 CMakeLists.txt 文件,复制下面的内容覆盖 CMakeLists.txt 中的内容:

cmake_minimum_required(VERSION 3.10)

project(php_7_1_0)

set(CMAKE_CXX_STANDARD 14)

set(PHP_SOURCE .)

file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE})
file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/ext)
file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/Zend)
file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/sapi)
file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/pear)
file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/TSRM)

file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS
        ${PHP_SOURCE}/*.c
        ${PHP_SOURCE}/ext/*.c
        ${PHP_SOURCE}/main/*.c
        ${PHP_SOURCE}/Zend/*.c
        ${PHP_SOURCE}/sapi/*.c
        ${PHP_SOURCE}/pear/*.c
        ${PHP_SOURCE}/TSRM/*.c
)

include_directories(${PHP_SOURCE})
include_directories(${PHP_SOURCE}/ext)
include_directories(${PHP_SOURCE}/main)
include_directories(${PHP_SOURCE}/Zend)
include_directories(${PHP_SOURCE}/sapi)
include_directories(${PHP_SOURCE}/pear)
include_directories(${PHP_SOURCE}/TSRM)

add_executable(${PROJECT_NAME} ${SRC_LIST})
add_custom_target(makefile COMMAND make && make install WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})

注意:/home/ubuntu/php-7.1.0 要替换成你自己的 PHP 所在的路径。

点击右上角绿色锤子旁边的下拉框 => Edit Configurations => makefile,按照下面的步骤进行操作:

  • Target:makefile(一般不用动)
  • Executable:点击右边的 ... 按钮,选择 php-7.1.0/bin/php 文件
  • Program arguments:输入你要调试的 PHP 脚本的文件名,比如 index.php
  • Working directory:选择上面 PHP 脚本所在的目录
  • 删除 Before launch 中的 Build,避免每次调试都重新构建 PHP

点击右上角绿色的小虫子或者使用快捷键 Shift + F9 开始调试

使用 GDB 进行调试

用来测试的 index.php :

<?php

$a = 'hello';

var_dump($a);

开始 GDB 调试:

gdb /php-7.1.0/build/bin/php

使用 b 命令在 var_dump 处打断点:

(gdb) b zif_var_dump
Breakpoint 1 at 0x48f3bc: file /home/ubuntu/php-7.1.0/ext/standard/var.c, line 200.

为啥这里是 zif_var_dump?

因为 zif_var_dump 是 var_dump 函数的底层实现, 是由 PHP_FUNCITON(var_dump) 宏展开后的名称,所以直接搜 zif_var_dump 是搜不到的。

#define ZEND_FN(name)   zif_##name
#define ZEND_NAMED_FUNCTION(name)   void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))

/* PHP-named Zend macro wrappers */
#define PHP_FUNCTION			ZEND_FUNCTION

PHP_FUNCTION(var_dump)
{
	zval *args;
	int argc;
	int	i;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
		return;
	}

	for (i = 0; i < argc; i++) {
		php_var_dump(&args[i], 1);
	}
}

开始运行 PHP 文件:

(gdb) r index.php
Starting program: /home/ubuntu/php-7.1.0/build/bin/php index.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, zif_var_dump (execute_data=0x7ffffb6130d0, return_value=0x7ffffffea400)
    at /home/ubuntu/php-7.1.0/ext/standard/var.c:200
200     {

代码执行到 zif_var_dump 就停止了,说明命中了断点。我们可以使用 l 命令查看附近的源码:

(gdb) l
195     /* }}} */
196
197     /* {{{ proto void var_dump(mixed var)
198        Dumps a string representation of variable to output */
199     PHP_FUNCTION(var_dump)
200     {
201             zval *args;
202             int argc;
203             int     i;
204

使用 bt 命令查看调用栈:

(gdb) bt
#0  zif_var_dump (execute_data=0x7ffffb6130d0, return_value=0x7ffffffea400)
    at /home/ubuntu/php-7.1.0/ext/standard/var.c:200
#1  0x0000000008642151 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER ()
    at /home/ubuntu/php-7.1.0/Zend/zend_vm_execute.h:628
#2  0x000000000864038e in execute_ex (ex=0x7ffffb613030) at /home/ubuntu/php-7.1.0/Zend/zend_vm_execute.h:429
#3  0x0000000008640d05 in zend_execute (op_array=0x7ffffb67e000, return_value=0x0)
    at /home/ubuntu/php-7.1.0/Zend/zend_vm_execute.h:474
#4  0x00000000085a2ca6 in zend_execute_scripts (type=8, retval=0x0, file_count=3)
    at /home/ubuntu/php-7.1.0/Zend/zend.c:1474
#5  0x00000000084eb1ce in php_execute_script (primary_file=0x7ffffffecd00) at /home/ubuntu/php-7.1.0/main/main.c:2533
#6  0x0000000008743462 in do_cli (argc=2, argv=0x90c1ef0) at /home/ubuntu/php-7.1.0/sapi/cli/php_cli.c:990
#7  0x000000000874483e in main (argc=2, argv=0x90c1ef0) at /home/ubuntu/php-7.1.0/sapi/cli/php_cli.c:1378

使用 u 命令让代码运行到指定的行:

(gdb) u 209
zif_var_dump (execute_data=0x7ffffb6130d0, return_value=0x7ffffffea400)
    at /home/ubuntu/php-7.1.0/ext/standard/var.c:209
209             for (i = 0; i < argc; i++) {

使用 p 命令打印变量 $a 的值:

(gdb) p args[0].value.str.val
$1 = "h"

使用 @ 指定输出长度:

(gdb) p args[0].value.str.val@5
$2 = {"h", "e", "l", "l", "o"}