/src/php-src/ext/standard/dl.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright © The PHP Group and Contributors. | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to the Modified BSD License that is | |
6 | | | bundled with this package in the file LICENSE, and is available | |
7 | | | through the World Wide Web at <https://www.php.net/license/>. | |
8 | | | | |
9 | | | SPDX-License-Identifier: BSD-3-Clause | |
10 | | +----------------------------------------------------------------------+ |
11 | | | Authors: Brian Schaffner <brian@tool.net> | |
12 | | | Shane Caraveo <shane@caraveo.com> | |
13 | | | Zeev Suraski <zeev@php.net> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | #include "php.h" |
18 | | #include "dl.h" |
19 | | #include "php_globals.h" |
20 | | #include "php_ini.h" |
21 | | #include "ext/standard/info.h" |
22 | | |
23 | | #include "SAPI.h" |
24 | | |
25 | | #ifdef HAVE_LIBDL |
26 | | #include <stdlib.h> |
27 | | #include <stdio.h> |
28 | | #include <string.h> |
29 | | #ifdef PHP_WIN32 |
30 | | #include "win32/param.h" |
31 | | #include "win32/winutil.h" |
32 | | #define GET_DL_ERROR() php_win_err() |
33 | | #else |
34 | | #include <sys/param.h> |
35 | 0 | #define GET_DL_ERROR() DL_ERROR() |
36 | | #endif |
37 | | #endif /* defined(HAVE_LIBDL) */ |
38 | | |
39 | | /* {{{ Load a PHP extension at runtime */ |
40 | | PHPAPI PHP_FUNCTION(dl) |
41 | 0 | { |
42 | 0 | char *filename; |
43 | 0 | size_t filename_len; |
44 | |
|
45 | 0 | ZEND_PARSE_PARAMETERS_START(1, 1) |
46 | 0 | Z_PARAM_STRING(filename, filename_len) |
47 | 0 | ZEND_PARSE_PARAMETERS_END(); |
48 | | |
49 | 0 | if (!PG(enable_dl)) { |
50 | 0 | php_error_docref(NULL, E_WARNING, "Dynamically loaded extensions aren't enabled"); |
51 | 0 | RETURN_FALSE; |
52 | 0 | } |
53 | | |
54 | 0 | if (filename_len >= MAXPATHLEN) { |
55 | 0 | php_error_docref(NULL, E_WARNING, "Filename exceeds the maximum allowed length of %d characters", MAXPATHLEN); |
56 | 0 | RETURN_FALSE; |
57 | 0 | } |
58 | | |
59 | | #if ZEND_RC_DEBUG |
60 | | bool orig_rc_debug = zend_rc_debug; |
61 | | /* FIXME: Loading extensions during the request breaks some invariants. In |
62 | | * particular, it will create persistent interned strings, which is not |
63 | | * allowed at this stage. */ |
64 | | zend_rc_debug = false; |
65 | | #endif |
66 | | |
67 | 0 | php_dl(filename, MODULE_TEMPORARY, return_value, 0); |
68 | 0 | if (Z_TYPE_P(return_value) == IS_TRUE) { |
69 | 0 | EG(full_tables_cleanup) = 1; |
70 | 0 | } |
71 | |
|
72 | | #if ZEND_RC_DEBUG |
73 | | zend_rc_debug = orig_rc_debug; |
74 | | #endif |
75 | 0 | } |
76 | | /* }}} */ |
77 | | |
78 | | #ifdef HAVE_LIBDL |
79 | | |
80 | | /* {{{ php_load_shlib */ |
81 | | PHPAPI void *php_load_shlib(const char *path, char **errp) |
82 | 0 | { |
83 | 0 | void *handle; |
84 | 0 | char *err; |
85 | |
|
86 | 0 | handle = DL_LOAD(path); |
87 | 0 | if (!handle) { |
88 | 0 | err = GET_DL_ERROR(); |
89 | | #ifdef PHP_WIN32 |
90 | | if (err && (*err)) { |
91 | | size_t i = strlen(err); |
92 | | (*errp)=estrdup(err); |
93 | | php_win32_error_msg_free(err); |
94 | | while (i > 0 && isspace((unsigned char)(*errp)[i-1])) { (*errp)[i-1] = '\0'; i--; } |
95 | | } else { |
96 | | (*errp) = estrdup("<No message>"); |
97 | | } |
98 | | #else |
99 | 0 | (*errp) = estrdup(err); |
100 | 0 | GET_DL_ERROR(); /* free the buffer storing the error */ |
101 | 0 | #endif |
102 | 0 | } |
103 | 0 | return handle; |
104 | 0 | } |
105 | | /* }}} */ |
106 | | |
107 | | /* {{{ php_load_extension */ |
108 | | PHPAPI int php_load_extension(const char *filename, int type, int start_now) |
109 | 0 | { |
110 | 0 | void *handle; |
111 | 0 | char *libpath; |
112 | 0 | zend_module_entry *module_entry; |
113 | 0 | zend_module_entry *(*get_module)(void); |
114 | 0 | int error_type, slash_suffix = 0; |
115 | 0 | const char *extension_dir; |
116 | 0 | char *err1, *err2; |
117 | |
|
118 | 0 | if (type == MODULE_PERSISTENT) { |
119 | 0 | extension_dir = zend_ini_string_literal("extension_dir"); |
120 | 0 | } else { |
121 | 0 | extension_dir = PG(extension_dir); |
122 | 0 | } |
123 | |
|
124 | 0 | if (type == MODULE_TEMPORARY) { |
125 | 0 | error_type = E_WARNING; |
126 | 0 | } else { |
127 | 0 | error_type = E_CORE_WARNING; |
128 | 0 | } |
129 | | |
130 | | /* Check if passed filename contains directory separators */ |
131 | 0 | if (strchr(filename, '/') != NULL || strchr(filename, DEFAULT_SLASH) != NULL) { |
132 | | /* Passing modules with full path is not supported for dynamically loaded extensions */ |
133 | 0 | if (type == MODULE_TEMPORARY) { |
134 | 0 | php_error_docref(NULL, E_WARNING, "Temporary module name should contain only filename"); |
135 | 0 | return FAILURE; |
136 | 0 | } |
137 | 0 | libpath = estrdup(filename); |
138 | 0 | } else if (extension_dir && extension_dir[0]) { |
139 | 0 | slash_suffix = IS_SLASH(extension_dir[strlen(extension_dir)-1]); |
140 | | /* Try as filename first */ |
141 | 0 | if (slash_suffix) { |
142 | 0 | spprintf(&libpath, 0, "%s%s", extension_dir, filename); /* SAFE */ |
143 | 0 | } else { |
144 | 0 | spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, filename); /* SAFE */ |
145 | 0 | } |
146 | 0 | } else { |
147 | 0 | return FAILURE; /* Not full path given or extension_dir is not set */ |
148 | 0 | } |
149 | | |
150 | 0 | handle = php_load_shlib(libpath, &err1); |
151 | 0 | if (!handle) { |
152 | | /* Now, consider 'filename' as extension name and build file name */ |
153 | 0 | char *orig_libpath = libpath; |
154 | |
|
155 | 0 | if (slash_suffix) { |
156 | 0 | spprintf(&libpath, 0, "%s" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, filename); /* SAFE */ |
157 | 0 | } else { |
158 | 0 | spprintf(&libpath, 0, "%s%c" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, DEFAULT_SLASH, filename); /* SAFE */ |
159 | 0 | } |
160 | |
|
161 | 0 | handle = php_load_shlib(libpath, &err2); |
162 | 0 | if (!handle) { |
163 | 0 | php_error_docref(NULL, error_type, "Unable to load dynamic library '%s' (tried: %s (%s), %s (%s))", |
164 | 0 | filename, orig_libpath, err1, libpath, err2); |
165 | 0 | efree(orig_libpath); |
166 | 0 | efree(err1); |
167 | 0 | efree(libpath); |
168 | 0 | efree(err2); |
169 | 0 | return FAILURE; |
170 | 0 | } |
171 | 0 | efree(orig_libpath); |
172 | 0 | efree(err1); |
173 | 0 | } |
174 | 0 | efree(libpath); |
175 | |
|
176 | | #ifdef PHP_WIN32 |
177 | | if (!php_win32_image_compatible(handle, &err1)) { |
178 | | php_error_docref(NULL, error_type, "%s", err1); |
179 | | efree(err1); |
180 | | DL_UNLOAD(handle); |
181 | | return FAILURE; |
182 | | } |
183 | | #endif |
184 | |
|
185 | 0 | get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module"); |
186 | | |
187 | | /* Some OS prepend _ to symbol names while their dynamic linker |
188 | | * does not do that automatically. Thus we check manually for |
189 | | * _get_module. */ |
190 | |
|
191 | 0 | if (!get_module) { |
192 | 0 | get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module"); |
193 | 0 | } |
194 | |
|
195 | 0 | if (!get_module) { |
196 | 0 | if (DL_FETCH_SYMBOL(handle, "zend_extension_entry") || DL_FETCH_SYMBOL(handle, "_zend_extension_entry")) { |
197 | 0 | DL_UNLOAD(handle); |
198 | 0 | php_error_docref(NULL, error_type, "Invalid library (appears to be a Zend Extension, try loading using zend_extension=%s from php.ini)", filename); |
199 | 0 | return FAILURE; |
200 | 0 | } |
201 | 0 | DL_UNLOAD(handle); |
202 | 0 | php_error_docref(NULL, error_type, "Invalid library (maybe not a PHP library) '%s'", filename); |
203 | 0 | return FAILURE; |
204 | 0 | } |
205 | 0 | module_entry = get_module(); |
206 | 0 | if (zend_hash_str_exists(&module_registry, module_entry->name, strlen(module_entry->name))) { |
207 | 0 | zend_error(E_CORE_WARNING, "Module \"%s\" is already loaded", module_entry->name); |
208 | 0 | DL_UNLOAD(handle); |
209 | 0 | return FAILURE; |
210 | 0 | } |
211 | 0 | if (module_entry->zend_api != ZEND_MODULE_API_NO) { |
212 | 0 | php_error_docref(NULL, error_type, |
213 | 0 | "%s: Unable to initialize module\n" |
214 | 0 | "Module compiled with module API=%d\n" |
215 | 0 | "PHP compiled with module API=%d\n" |
216 | 0 | "These options need to match\n", |
217 | 0 | module_entry->name, module_entry->zend_api, ZEND_MODULE_API_NO); |
218 | 0 | DL_UNLOAD(handle); |
219 | 0 | return FAILURE; |
220 | 0 | } |
221 | 0 | if(strcmp(module_entry->build_id, ZEND_MODULE_BUILD_ID)) { |
222 | 0 | php_error_docref(NULL, error_type, |
223 | 0 | "%s: Unable to initialize module\n" |
224 | 0 | "Module compiled with build ID=%s\n" |
225 | 0 | "PHP compiled with build ID=%s\n" |
226 | 0 | "These options need to match\n", |
227 | 0 | module_entry->name, module_entry->build_id, ZEND_MODULE_BUILD_ID); |
228 | 0 | DL_UNLOAD(handle); |
229 | 0 | return FAILURE; |
230 | 0 | } |
231 | | |
232 | 0 | if ((module_entry = zend_register_module_ex(module_entry, type)) == NULL) { |
233 | 0 | DL_UNLOAD(handle); |
234 | 0 | return FAILURE; |
235 | 0 | } |
236 | | |
237 | 0 | module_entry->handle = handle; |
238 | |
|
239 | 0 | if ((type == MODULE_TEMPORARY || start_now) && zend_startup_module_ex(module_entry) == FAILURE) { |
240 | 0 | DL_UNLOAD(handle); |
241 | 0 | return FAILURE; |
242 | 0 | } |
243 | | |
244 | 0 | if ((type == MODULE_TEMPORARY || start_now) && module_entry->request_startup_func) { |
245 | 0 | if (module_entry->request_startup_func(type, module_entry->module_number) == FAILURE) { |
246 | 0 | php_error_docref(NULL, error_type, "Unable to initialize module '%s'", module_entry->name); |
247 | 0 | DL_UNLOAD(handle); |
248 | 0 | return FAILURE; |
249 | 0 | } |
250 | 0 | } |
251 | 0 | return SUCCESS; |
252 | 0 | } |
253 | | /* }}} */ |
254 | | |
255 | | #else |
256 | | |
257 | | static void php_dl_error(const char *filename) |
258 | | { |
259 | | php_error_docref(NULL, E_WARNING, "Cannot dynamically load %s - dynamic modules are not supported", filename); |
260 | | } |
261 | | |
262 | | PHPAPI void *php_load_shlib(const char *path, char **errp) |
263 | | { |
264 | | php_dl_error(path); |
265 | | (*errp) = estrdup("No DL support"); |
266 | | return NULL; |
267 | | } |
268 | | |
269 | | PHPAPI int php_load_extension(const char *filename, int type, int start_now) |
270 | | { |
271 | | php_dl_error(filename); |
272 | | |
273 | | return FAILURE; |
274 | | } |
275 | | |
276 | | #endif |
277 | | |
278 | | /* {{{ php_dl */ |
279 | | PHPAPI void php_dl(const char *file, int type, zval *return_value, int start_now) |
280 | 0 | { |
281 | | /* Load extension */ |
282 | 0 | if (php_load_extension(file, type, start_now) == FAILURE) { |
283 | 0 | RETVAL_FALSE; |
284 | 0 | } else { |
285 | 0 | RETVAL_TRUE; |
286 | 0 | } |
287 | 0 | } |
288 | | /* }}} */ |
289 | | |
290 | | PHP_MINFO_FUNCTION(dl) |
291 | 1 | { |
292 | 1 | #if defined(HAVE_LIBDL) |
293 | 1 | #define PHP_DL_SUPPORT_STATUS "enabled" |
294 | | #else |
295 | | #define PHP_DL_SUPPORT_STATUS "unavailable" |
296 | | #endif |
297 | 1 | php_info_print_table_row(2, "Dynamic Library Support", PHP_DL_SUPPORT_STATUS); |
298 | 1 | } |