1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "zend_extensions.h"
21 #include "zend_system_id.h"
22
23 ZEND_API zend_llist zend_extensions;
24 ZEND_API uint32_t zend_extension_flags = 0;
25 ZEND_API int zend_op_array_extension_handles = 0;
26 ZEND_API int zend_internal_function_extension_handles = 0;
27 static int last_resource_number;
28
zend_load_extension(const char * path)29 zend_result zend_load_extension(const char *path)
30 {
31 #if ZEND_EXTENSIONS_SUPPORT
32 DL_HANDLE handle;
33
34 handle = DL_LOAD(path);
35 if (!handle) {
36 #ifndef ZEND_WIN32
37 fprintf(stderr, "Failed loading %s: %s\n", path, DL_ERROR());
38 #else
39 fprintf(stderr, "Failed loading %s\n", path);
40 /* See http://support.microsoft.com/kb/190351 */
41 fflush(stderr);
42 #endif
43 return FAILURE;
44 }
45 #ifdef ZEND_WIN32
46 char *err;
47 if (!php_win32_image_compatible(handle, &err)) {
48 zend_error(E_CORE_WARNING, err);
49 return FAILURE;
50 }
51 #endif
52 return zend_load_extension_handle(handle, path);
53 #else
54 fprintf(stderr, "Extensions are not supported on this platform.\n");
55 /* See http://support.microsoft.com/kb/190351 */
56 #ifdef ZEND_WIN32
57 fflush(stderr);
58 #endif
59 return FAILURE;
60 #endif
61 }
62
zend_load_extension_handle(DL_HANDLE handle,const char * path)63 zend_result zend_load_extension_handle(DL_HANDLE handle, const char *path)
64 {
65 #if ZEND_EXTENSIONS_SUPPORT
66 zend_extension *new_extension;
67
68 const zend_extension_version_info *extension_version_info = (const zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "extension_version_info");
69 if (!extension_version_info) {
70 extension_version_info = (const zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "_extension_version_info");
71 }
72 new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "zend_extension_entry");
73 if (!new_extension) {
74 new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "_zend_extension_entry");
75 }
76 if (!extension_version_info || !new_extension) {
77 fprintf(stderr, "%s doesn't appear to be a valid Zend extension\n", path);
78 /* See http://support.microsoft.com/kb/190351 */
79 #ifdef ZEND_WIN32
80 fflush(stderr);
81 #endif
82 DL_UNLOAD(handle);
83 return FAILURE;
84 }
85
86 /* allow extension to proclaim compatibility with any Zend version */
87 if (extension_version_info->zend_extension_api_no != ZEND_EXTENSION_API_NO &&(!new_extension->api_no_check || new_extension->api_no_check(ZEND_EXTENSION_API_NO) != SUCCESS)) {
88 if (extension_version_info->zend_extension_api_no > ZEND_EXTENSION_API_NO) {
89 fprintf(stderr, "%s requires Zend Engine API version %d.\n"
90 "The Zend Engine API version %d which is installed, is outdated.\n\n",
91 new_extension->name,
92 extension_version_info->zend_extension_api_no,
93 ZEND_EXTENSION_API_NO);
94 /* See http://support.microsoft.com/kb/190351 */
95 #ifdef ZEND_WIN32
96 fflush(stderr);
97 #endif
98 DL_UNLOAD(handle);
99 return FAILURE;
100 } else if (extension_version_info->zend_extension_api_no < ZEND_EXTENSION_API_NO) {
101 fprintf(stderr, "%s requires Zend Engine API version %d.\n"
102 "The Zend Engine API version %d which is installed, is newer.\n"
103 "Contact %s at %s for a later version of %s.\n\n",
104 new_extension->name,
105 extension_version_info->zend_extension_api_no,
106 ZEND_EXTENSION_API_NO,
107 new_extension->author,
108 new_extension->URL,
109 new_extension->name);
110 /* See http://support.microsoft.com/kb/190351 */
111 #ifdef ZEND_WIN32
112 fflush(stderr);
113 #endif
114 DL_UNLOAD(handle);
115 return FAILURE;
116 }
117 } else if (strcmp(ZEND_EXTENSION_BUILD_ID, extension_version_info->build_id) &&
118 (!new_extension->build_id_check || new_extension->build_id_check(ZEND_EXTENSION_BUILD_ID) != SUCCESS)) {
119 fprintf(stderr, "Cannot load %s - it was built with configuration %s, whereas running engine is %s\n",
120 new_extension->name, extension_version_info->build_id, ZEND_EXTENSION_BUILD_ID);
121 /* See http://support.microsoft.com/kb/190351 */
122 #ifdef ZEND_WIN32
123 fflush(stderr);
124 #endif
125 DL_UNLOAD(handle);
126 return FAILURE;
127 } else if (zend_get_extension(new_extension->name)) {
128 fprintf(stderr, "Cannot load %s - it was already loaded\n", new_extension->name);
129 /* See http://support.microsoft.com/kb/190351 */
130 #ifdef ZEND_WIN32
131 fflush(stderr);
132 #endif
133 DL_UNLOAD(handle);
134 return FAILURE;
135 }
136
137 zend_register_extension(new_extension, handle);
138 return SUCCESS;
139 #else
140 fprintf(stderr, "Extensions are not supported on this platform.\n");
141 /* See http://support.microsoft.com/kb/190351 */
142 #ifdef ZEND_WIN32
143 fflush(stderr);
144 #endif
145 return FAILURE;
146 #endif
147 }
148
149
zend_register_extension(zend_extension * new_extension,DL_HANDLE handle)150 void zend_register_extension(zend_extension *new_extension, DL_HANDLE handle)
151 {
152 #if ZEND_EXTENSIONS_SUPPORT
153 zend_extension extension;
154
155 extension = *new_extension;
156 extension.handle = handle;
157
158 zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
159
160 zend_llist_add_element(&zend_extensions, &extension);
161
162 if (extension.op_array_ctor) {
163 zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_CTOR;
164 }
165 if (extension.op_array_dtor) {
166 zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_DTOR;
167 }
168 if (extension.op_array_handler) {
169 zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_HANDLER;
170 }
171 if (extension.op_array_persist_calc) {
172 zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST_CALC;
173 }
174 if (extension.op_array_persist) {
175 zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST;
176 }
177 /*fprintf(stderr, "Loaded %s, version %s\n", extension.name, extension.version);*/
178 #endif
179 }
180
181
zend_extension_shutdown(zend_extension * extension)182 static void zend_extension_shutdown(zend_extension *extension)
183 {
184 #if ZEND_EXTENSIONS_SUPPORT
185 if (extension->shutdown) {
186 extension->shutdown(extension);
187 }
188 #endif
189 }
190
191 /* int return due to zend linked list API */
zend_extension_startup(zend_extension * extension)192 static int zend_extension_startup(zend_extension *extension)
193 {
194 #if ZEND_EXTENSIONS_SUPPORT
195 if (extension->startup) {
196 if (extension->startup(extension)!=SUCCESS) {
197 return 1;
198 }
199 zend_append_version_info(extension);
200 }
201 #endif
202 return 0;
203 }
204
205
zend_startup_extensions_mechanism(void)206 void zend_startup_extensions_mechanism(void)
207 {
208 /* Startup extensions mechanism */
209 zend_llist_init(&zend_extensions, sizeof(zend_extension), (void (*)(void *)) zend_extension_dtor, 1);
210 zend_op_array_extension_handles = 0;
211 zend_internal_function_extension_handles = 0;
212 last_resource_number = 0;
213 }
214
215
zend_startup_extensions(void)216 void zend_startup_extensions(void)
217 {
218 zend_llist_apply_with_del(&zend_extensions, (int (*)(void *)) zend_extension_startup);
219 }
220
221
zend_shutdown_extensions(void)222 void zend_shutdown_extensions(void)
223 {
224 zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_shutdown);
225 zend_llist_destroy(&zend_extensions);
226 }
227
228
zend_extension_dtor(zend_extension * extension)229 void zend_extension_dtor(zend_extension *extension)
230 {
231 #if ZEND_EXTENSIONS_SUPPORT && !ZEND_DEBUG
232 if (extension->handle && !getenv("ZEND_DONT_UNLOAD_MODULES")) {
233 DL_UNLOAD(extension->handle);
234 }
235 #endif
236 }
237
238
zend_extension_message_dispatcher(const zend_extension * extension,int num_args,va_list args)239 static void zend_extension_message_dispatcher(const zend_extension *extension, int num_args, va_list args)
240 {
241 int message;
242 void *arg;
243
244 if (!extension->message_handler || num_args!=2) {
245 return;
246 }
247 message = va_arg(args, int);
248 arg = va_arg(args, void *);
249 extension->message_handler(message, arg);
250 }
251
252
zend_extension_dispatch_message(int message,void * arg)253 ZEND_API void zend_extension_dispatch_message(int message, void *arg)
254 {
255 zend_llist_apply_with_arguments(&zend_extensions, (llist_apply_with_args_func_t) zend_extension_message_dispatcher, 2, message, arg);
256 }
257
258
zend_get_resource_handle(const char * module_name)259 ZEND_API int zend_get_resource_handle(const char *module_name)
260 {
261 if (last_resource_number<ZEND_MAX_RESERVED_RESOURCES) {
262 zend_add_system_entropy(module_name, "zend_get_resource_handle", &last_resource_number, sizeof(int));
263 return last_resource_number++;
264 } else {
265 return -1;
266 }
267 }
268
269 /**
270 * The handle returned by this function can be used with
271 * `ZEND_OP_ARRAY_EXTENSION(op_array, handle)`.
272 *
273 * The extension slot has been available since PHP 7.4 on user functions and
274 * has been available since PHP 8.2 on internal functions.
275 *
276 * # Safety
277 * The extension slot made available by calling this function is initialized on
278 * the first call made to the function in that request. If you need to
279 * initialize it before this point, call `zend_init_func_run_time_cache`.
280 *
281 * The function cache slots are not available if the function is a trampoline,
282 * which can be checked with something like:
283 *
284 * if (fbc->type == ZEND_USER_FUNCTION
285 * && !(fbc->op_array.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)
286 * ) {
287 * // Use ZEND_OP_ARRAY_EXTENSION somehow
288 * }
289 */
zend_get_op_array_extension_handle(const char * module_name)290 ZEND_API int zend_get_op_array_extension_handle(const char *module_name)
291 {
292 int handle = zend_op_array_extension_handles++;
293 zend_add_system_entropy(module_name, "zend_get_op_array_extension_handle", &zend_op_array_extension_handles, sizeof(int));
294 return handle;
295 }
296
297 /** See zend_get_op_array_extension_handle for important usage information. */
zend_get_op_array_extension_handles(const char * module_name,int handles)298 ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int handles)
299 {
300 int handle = zend_op_array_extension_handles;
301 zend_op_array_extension_handles += handles;
302 zend_add_system_entropy(module_name, "zend_get_op_array_extension_handle", &zend_op_array_extension_handles, sizeof(int));
303 return handle;
304 }
305
zend_get_internal_function_extension_handle(const char * module_name)306 ZEND_API int zend_get_internal_function_extension_handle(const char *module_name)
307 {
308 int handle = zend_internal_function_extension_handles++;
309 zend_add_system_entropy(module_name, "zend_get_internal_function_extension_handle", &zend_internal_function_extension_handles, sizeof(int));
310 return handle;
311 }
312
zend_get_internal_function_extension_handles(const char * module_name,int handles)313 ZEND_API int zend_get_internal_function_extension_handles(const char *module_name, int handles)
314 {
315 int handle = zend_internal_function_extension_handles;
316 zend_internal_function_extension_handles += handles;
317 zend_add_system_entropy(module_name, "zend_get_internal_function_extension_handle", &zend_internal_function_extension_handles, sizeof(int));
318 return handle;
319 }
320
zend_internal_run_time_cache_reserved_size(void)321 ZEND_API size_t zend_internal_run_time_cache_reserved_size(void) {
322 return zend_internal_function_extension_handles * sizeof(void *);
323 }
324
zend_init_internal_run_time_cache(void)325 ZEND_API void zend_init_internal_run_time_cache(void) {
326 size_t rt_size = zend_internal_run_time_cache_reserved_size();
327 if (rt_size) {
328 size_t functions = zend_hash_num_elements(CG(function_table));
329 zend_class_entry *ce;
330 ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
331 functions += zend_hash_num_elements(&ce->function_table);
332 } ZEND_HASH_FOREACH_END();
333
334 size_t alloc_size = functions * rt_size;
335 char *ptr = pemalloc(alloc_size, 1);
336
337 CG(internal_run_time_cache) = ptr;
338 CG(internal_run_time_cache_size) = alloc_size;
339
340 zend_internal_function *zif;
341 ZEND_HASH_MAP_FOREACH_PTR(CG(function_table), zif) {
342 if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL) {
343 ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr);
344 ptr += rt_size;
345 }
346 } ZEND_HASH_FOREACH_END();
347 ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
348 ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zif) {
349 if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL) {
350 ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr);
351 ptr += rt_size;
352 }
353 } ZEND_HASH_FOREACH_END();
354 } ZEND_HASH_FOREACH_END();
355 }
356 }
357
zend_reset_internal_run_time_cache(void)358 ZEND_API void zend_reset_internal_run_time_cache(void) {
359 if (CG(internal_run_time_cache)) {
360 memset(CG(internal_run_time_cache), 0, CG(internal_run_time_cache_size));
361 }
362 }
363
zend_get_extension(const char * extension_name)364 ZEND_API zend_extension *zend_get_extension(const char *extension_name)
365 {
366 zend_llist_element *element;
367
368 for (element = zend_extensions.head; element; element = element->next) {
369 zend_extension *extension = (zend_extension *) element->data;
370
371 if (!strcmp(extension->name, extension_name)) {
372 return extension;
373 }
374 }
375 return NULL;
376 }
377
378 typedef struct _zend_extension_persist_data {
379 zend_op_array *op_array;
380 size_t size;
381 char *mem;
382 } zend_extension_persist_data;
383
zend_extension_op_array_persist_calc_handler(zend_extension * extension,zend_extension_persist_data * data)384 static void zend_extension_op_array_persist_calc_handler(zend_extension *extension, zend_extension_persist_data *data)
385 {
386 if (extension->op_array_persist_calc) {
387 data->size += extension->op_array_persist_calc(data->op_array);
388 }
389 }
390
zend_extension_op_array_persist_handler(zend_extension * extension,zend_extension_persist_data * data)391 static void zend_extension_op_array_persist_handler(zend_extension *extension, zend_extension_persist_data *data)
392 {
393 if (extension->op_array_persist) {
394 size_t size = extension->op_array_persist(data->op_array, data->mem);
395 if (size) {
396 data->mem = (void*)((char*)data->mem + size);
397 data->size += size;
398 }
399 }
400 }
401
zend_extensions_op_array_persist_calc(zend_op_array * op_array)402 ZEND_API size_t zend_extensions_op_array_persist_calc(zend_op_array *op_array)
403 {
404 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST_CALC) {
405 zend_extension_persist_data data;
406
407 data.op_array = op_array;
408 data.size = 0;
409 data.mem = NULL;
410 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_persist_calc_handler, &data);
411 return data.size;
412 }
413 return 0;
414 }
415
zend_extensions_op_array_persist(zend_op_array * op_array,void * mem)416 ZEND_API size_t zend_extensions_op_array_persist(zend_op_array *op_array, void *mem)
417 {
418 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST) {
419 zend_extension_persist_data data;
420
421 data.op_array = op_array;
422 data.size = 0;
423 data.mem = mem;
424 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_persist_handler, &data);
425 return data.size;
426 }
427 return 0;
428 }
429