Coverage Report

Created: 2026-06-02 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/zend_gdb.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend Engine                                                          |
4
   +----------------------------------------------------------------------+
5
   | Copyright © Zend Technologies Ltd., a subsidiary company of          |
6
   |     Perforce Software, Inc., and Contributors.                       |
7
   +----------------------------------------------------------------------+
8
   | This source file is subject to the Modified BSD License that is      |
9
   | bundled with this package in the file LICENSE, and is available      |
10
   | through the World Wide Web at <https://www.php.net/license/>.        |
11
   |                                                                      |
12
   | SPDX-License-Identifier: BSD-3-Clause                                |
13
   +----------------------------------------------------------------------+
14
   | Authors: Dmitry Stogov <dmitry@zend.com>                             |
15
   |          Xinchen Hui <xinchen.h@zend.com>                            |
16
   +----------------------------------------------------------------------+
17
*/
18
19
#include "zend.h"
20
#include "zend_gdb.h"
21
22
#include <sys/types.h>
23
#include <sys/stat.h>
24
#include <fcntl.h>
25
#include <unistd.h>
26
27
#if defined(__FreeBSD__) && __FreeBSD_version >= 1100000
28
# include <sys/user.h>
29
# include <libutil.h>
30
#endif
31
32
enum {
33
  ZEND_GDBJIT_NOACTION,
34
  ZEND_GDBJIT_REGISTER,
35
  ZEND_GDBJIT_UNREGISTER
36
};
37
38
typedef struct _zend_gdbjit_code_entry {
39
  struct _zend_gdbjit_code_entry *next_entry;
40
  struct _zend_gdbjit_code_entry *prev_entry;
41
  const char                     *symfile_addr;
42
  uint64_t                        symfile_size;
43
} zend_gdbjit_code_entry;
44
45
typedef struct _zend_gdbjit_descriptor {
46
  uint32_t                         version;
47
  uint32_t                         action_flag;
48
  struct _zend_gdbjit_code_entry *relevant_entry;
49
  struct _zend_gdbjit_code_entry *first_entry;
50
} zend_gdbjit_descriptor;
51
52
ZEND_API zend_gdbjit_descriptor __jit_debug_descriptor = {
53
  1, ZEND_GDBJIT_NOACTION, NULL, NULL
54
};
55
56
ZEND_API zend_never_inline void __jit_debug_register_code(void)
57
0
{
58
0
  __asm__ __volatile__("");
59
0
}
60
61
ZEND_API bool zend_gdb_register_code(const void *object, size_t size)
62
0
{
63
0
  zend_gdbjit_code_entry *entry;
64
65
0
  entry = malloc(sizeof(zend_gdbjit_code_entry) + size);
66
0
  if (entry == NULL) {
67
0
    return 0;
68
0
  }
69
70
0
  entry->symfile_addr = ((char*)entry) + sizeof(zend_gdbjit_code_entry);
71
0
  entry->symfile_size = size;
72
73
0
  memcpy((char *)entry->symfile_addr, object, size);
74
75
0
  entry->prev_entry = NULL;
76
0
  entry->next_entry = __jit_debug_descriptor.first_entry;
77
78
0
  if (entry->next_entry) {
79
0
    entry->next_entry->prev_entry = entry;
80
0
  }
81
0
  __jit_debug_descriptor.first_entry = entry;
82
83
  /* Notify GDB */
84
0
  __jit_debug_descriptor.relevant_entry = entry;
85
0
  __jit_debug_descriptor.action_flag = ZEND_GDBJIT_REGISTER;
86
0
  __jit_debug_register_code();
87
88
0
  return 1;
89
0
}
90
91
ZEND_API void zend_gdb_unregister_all(void)
92
0
{
93
0
  zend_gdbjit_code_entry *entry;
94
95
0
  __jit_debug_descriptor.action_flag = ZEND_GDBJIT_UNREGISTER;
96
0
  while ((entry = __jit_debug_descriptor.first_entry)) {
97
0
    __jit_debug_descriptor.first_entry = entry->next_entry;
98
0
    if (entry->next_entry) {
99
0
      entry->next_entry->prev_entry = NULL;
100
0
    }
101
    /* Notify GDB */
102
0
    __jit_debug_descriptor.relevant_entry = entry;
103
0
    __jit_debug_register_code();
104
105
0
    free(entry);
106
0
  }
107
0
}
108
109
ZEND_API bool zend_gdb_present(void)
110
0
{
111
0
  bool ret = false;
112
0
#if defined(__linux__) /* netbsd while having this procfs part, does not hold the tracer pid */
113
0
  int fd = open("/proc/self/status", O_RDONLY);
114
115
0
  if (fd >= 0) {
116
0
    char buf[1024];
117
0
    ssize_t n = read(fd, buf, sizeof(buf) - 1);
118
0
    char *s;
119
0
    pid_t pid;
120
121
0
    if (n > 0) {
122
0
      buf[n] = 0;
123
0
      s = strstr(buf, "TracerPid:");
124
0
      if (s) {
125
0
        s += sizeof("TracerPid:") - 1;
126
0
        while (*s == ' ' || *s == '\t') {
127
0
          s++;
128
0
        }
129
0
        pid = atoi(s);
130
0
        if (pid) {
131
0
          char out[1024];
132
0
          snprintf(buf, sizeof(buf), "/proc/%d/exe", (int)pid);
133
0
          if (readlink(buf, out, sizeof(out) - 1) > 0) {
134
0
            if (strstr(out, "gdb")) {
135
0
              ret = true;
136
0
            }
137
0
          }
138
0
        }
139
0
      }
140
0
    }
141
142
0
    close(fd);
143
0
  }
144
#elif defined(__FreeBSD__) && __FreeBSD_version >= 1100000
145
    struct kinfo_proc *proc = kinfo_getproc(getpid());
146
147
    if (proc) {
148
        if ((proc->ki_flag & P_TRACED) != 0) {
149
            struct kinfo_proc *dbg = kinfo_getproc(proc->ki_tracer);
150
151
            ret = (dbg && strstr(dbg->ki_comm, "gdb"));
152
        }
153
    }
154
#endif
155
156
0
  return ret;
157
0
}