Coverage Report

Created: 2025-09-27 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/opcache/jit/ir/ir_patch.c
Line
Count
Source
1
/*
2
 * IR - Lightweight JIT Compilation Framework
3
 * (Native code patcher)
4
 * Copyright (C) 2022 Zend by Perforce.
5
 * Authors: Dmitry Stogov <dmitry@php.net>
6
 *
7
 * Based on Mike Pall's implementation for LuaJIT.
8
 */
9
10
#include "ir.h"
11
#include "ir_private.h"
12
13
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
14
static uint32_t _asm_x86_inslen(const uint8_t* p)
15
0
{
16
0
  static const uint8_t map_op1[256] = {
17
0
    0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x20,
18
0
    0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,
19
0
    0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,
20
0
    0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,
21
0
#ifdef IR_TARGET_X64
22
0
    0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,
23
#else
24
    0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,
25
#endif
26
0
    0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,
27
0
    0x51,0x51,0x92,0x92,0x10,0x10,0x12,0x11,0x45,0x86,0x52,0x93,0x51,0x51,0x51,0x51,
28
0
    0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,
29
0
    0x93,0x86,0x93,0x93,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,
30
0
    0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x47,0x51,0x51,0x51,0x51,0x51,
31
0
#ifdef IR_TARGET_X64
32
0
    0x59,0x59,0x59,0x59,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51,
33
#else
34
    0x55,0x55,0x55,0x55,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51,
35
#endif
36
0
    0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,
37
0
    0x93,0x93,0x53,0x51,0x70,0x71,0x93,0x86,0x54,0x51,0x53,0x51,0x51,0x52,0x51,0x51,
38
0
    0x92,0x92,0x92,0x92,0x52,0x52,0x51,0x51,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,
39
0
    0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x45,0x45,0x47,0x52,0x51,0x51,0x51,0x51,
40
0
    0x10,0x51,0x10,0x10,0x51,0x51,0x63,0x66,0x51,0x51,0x51,0x51,0x51,0x51,0x92,0x92
41
0
  };
42
0
  static const uint8_t map_op2[256] = {
43
0
    0x93,0x93,0x93,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x51,0x52,0x51,0x93,0x52,0x94,
44
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
45
0
    0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
46
0
    0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x34,0x51,0x35,0x51,0x51,0x51,0x51,0x51,
47
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
48
0
    0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
49
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
50
0
    0x94,0x54,0x54,0x54,0x93,0x93,0x93,0x52,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
51
0
    0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,
52
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
53
0
    0x52,0x52,0x52,0x93,0x94,0x93,0x51,0x51,0x52,0x52,0x52,0x93,0x94,0x93,0x93,0x93,
54
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x94,0x93,0x93,0x93,0x93,0x93,
55
0
    0x93,0x93,0x94,0x93,0x94,0x94,0x94,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,
56
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
57
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
58
0
    0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x52
59
0
  };
60
0
  uint32_t result = 0;
61
0
  uint32_t prefixes = 0;
62
0
  uint32_t x = map_op1[*p];
63
64
0
  for (;;) {
65
0
    switch (x >> 4) {
66
0
      case 0:
67
0
        return result + x + (prefixes & 4);
68
0
      case 1:
69
0
        prefixes |= x;
70
0
        x = map_op1[*++p];
71
0
        result++;
72
0
        break;
73
0
      case 2:
74
0
        x = map_op2[*++p];
75
0
        break;
76
0
      case 3:
77
0
        p++;
78
0
        goto mrm;
79
0
      case 4:
80
0
        result -= (prefixes & 2);
81
        /* fallthrough */
82
0
      case 5:
83
0
        return result + (x & 15);
84
0
      case 6: /* Group 3. */
85
0
        if (p[1] & 0x38) {
86
0
          x = 2;
87
0
        } else if ((prefixes & 2) && (x == 0x66)) {
88
0
          x = 4;
89
0
        }
90
0
        goto mrm;
91
0
      case 7: /* VEX c4/c5. */
92
#ifdef IR_TARGET_X86
93
        if (p[1] < 0xc0) {
94
          x = 2;
95
          goto mrm;
96
        }
97
#endif
98
0
        if (x == 0x70) {
99
0
          x = *++p & 0x1f;
100
0
          result++;
101
0
          if (x >= 2) {
102
0
            p += 2;
103
0
            result += 2;
104
0
            goto mrm;
105
0
          }
106
0
        }
107
0
        p++;
108
0
        result++;
109
0
        x = map_op2[*++p];
110
0
        break;
111
0
      case 8:
112
0
        result -= (prefixes & 2);
113
        /* fallthrough */
114
0
      case 9:
115
0
mrm:
116
        /* ModR/M and possibly SIB. */
117
0
        result += (x & 15);
118
0
        x = *++p;
119
0
        switch (x >> 6) {
120
0
          case 0:
121
0
            if ((x & 7) == 5) {
122
0
              return result + 4;
123
0
            }
124
0
            break;
125
0
          case 1:
126
0
            result++;
127
0
            break;
128
0
          case 2:
129
0
            result += 4;
130
0
            break;
131
0
          case 3:
132
0
            return result;
133
0
        }
134
0
        if ((x & 7) == 4) {
135
0
          result++;
136
0
          if (x < 0x40 && (p[1] & 7) == 5) {
137
0
            result += 4;
138
0
          }
139
0
        }
140
0
        return result;
141
0
    }
142
0
  }
143
0
}
144
145
typedef IR_SET_ALIGNED(1, uint16_t unaligned_uint16_t);
146
typedef IR_SET_ALIGNED(1, int32_t unaligned_int32_t);
147
148
static int ir_patch_code(const void *code, size_t size, const void *from_addr, const void *to_addr)
149
0
{
150
0
  int ret = 0;
151
0
  uint8_t *p, *end;
152
153
0
  p = (uint8_t*)code;
154
0
  end = p + size - 4;
155
0
  while (p < end) {
156
0
    if ((*(unaligned_uint16_t*)p & 0xf0ff) == 0x800f && p + *(unaligned_int32_t*)(p+2) == (uint8_t*)from_addr - 6) {
157
0
      *(unaligned_int32_t*)(p+2) = ((uint8_t*)to_addr - (p + 6));
158
0
      ret++;
159
0
    } else if (*p == 0xe9 && p + *(unaligned_int32_t*)(p+1) == (uint8_t*)from_addr - 5) {
160
0
      *(unaligned_int32_t*)(p+1) = ((uint8_t*)to_addr - (p + 5));
161
0
      ret++;
162
0
    }
163
0
    p += _asm_x86_inslen(p);
164
0
  }
165
0
  if (ret) {
166
0
    ir_mem_flush((void*)code, size);
167
0
  }
168
0
  return ret;
169
0
}
170
171
#elif defined(IR_TARGET_AARCH64)
172
173
static int ir_patch_code(const void *code, size_t size, const void *from_addr, const void *to_addr)
174
{
175
  int ret = 0;
176
  uint8_t *p, *end;
177
  const void *veneer = NULL;
178
  ptrdiff_t delta;
179
180
  end = (uint8_t*)code;
181
  p = end + size;
182
  while (p > end) {
183
    uint32_t *ins_ptr;
184
    uint32_t ins;
185
186
    p -= 4;
187
    ins_ptr = (uint32_t*)p;
188
    ins = *ins_ptr;
189
    if ((ins & 0xfc000000u) == 0x14000000u) {
190
      // B (imm26:0..25)
191
      delta = (uint32_t*)from_addr - ins_ptr;
192
      if (((ins ^ (uint32_t)delta) & 0x01ffffffu) == 0) {
193
        delta = (uint32_t*)to_addr - ins_ptr;
194
        if (((delta + 0x02000000) >> 26) != 0) {
195
          abort(); // branch target out of range
196
        }
197
        *ins_ptr = (ins & 0xfc000000u) | ((uint32_t)delta & 0x03ffffffu);
198
        ret++;
199
        if (!veneer) {
200
          veneer = p;
201
        }
202
      }
203
    } else if ((ins & 0xff000000u) == 0x54000000u ||
204
               (ins & 0x7e000000u) == 0x34000000u) {
205
      // B.cond, CBZ, CBNZ (imm19:5..23)
206
      delta = (uint32_t*)from_addr - ins_ptr;
207
      if (((ins ^ ((uint32_t)delta << 5)) & 0x00ffffe0u) == 0) {
208
        delta = (uint32_t*)to_addr - ins_ptr;
209
        if (((delta + 0x40000) >> 19) != 0) {
210
          if (veneer) {
211
            delta = (uint32_t*)veneer - ins_ptr;
212
            if (((delta + 0x40000) >> 19) != 0) {
213
              abort(); // branch target out of range
214
            }
215
          } else {
216
            abort(); // branch target out of range
217
          }
218
        }
219
        *ins_ptr = (ins & 0xff00001fu) | (((uint32_t)delta & 0x7ffffu) << 5);
220
        ret++;
221
      }
222
    } else if ((ins & 0x7e000000u) == 0x36000000u) {
223
      // TBZ, TBNZ (imm14:5..18)
224
      delta = (uint32_t*)from_addr - ins_ptr;
225
      if (((ins ^ ((uint32_t)delta << 5)) & 0x0007ffe0u) == 0) {
226
        delta = (uint32_t*)to_addr - ins_ptr;
227
        if (((delta + 0x2000) >> 14) != 0) {
228
          if (veneer) {
229
            delta = (uint32_t*)veneer - ins_ptr;
230
            if (((delta + 0x2000) >> 14) != 0) {
231
              abort(); // branch target out of range
232
            }
233
          } else {
234
            abort(); // branch target out of range
235
          }
236
        }
237
        *ins_ptr = (ins & 0xfff8001fu) | (((uint32_t)delta & 0x3fffu) << 5);
238
        ret++;
239
      }
240
    }
241
  }
242
243
  if (ret) {
244
    ir_mem_flush((void*)code, size);
245
  }
246
247
  return ret;
248
}
249
#endif
250
251
int ir_patch(const void *code, size_t size, uint32_t jmp_table_size, const void *from_addr, const void *to_addr)
252
0
{
253
0
  int ret = 0;
254
255
0
  if (jmp_table_size) {
256
0
    const void **jmp_slot = (const void **)((char*)code + IR_ALIGNED_SIZE(size, sizeof(void*)));
257
258
0
    do {
259
0
      if (*jmp_slot == from_addr) {
260
0
        *jmp_slot = to_addr;
261
0
        ret++;
262
0
      }
263
0
      jmp_slot++;
264
0
    } while (--jmp_table_size);
265
0
  }
266
267
0
  ret += ir_patch_code(code, size, from_addr, to_addr);
268
269
0
  return ret;
270
0
}