/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 | } |