Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,11 @@ typedef struct _zend_fcall_info_cache {
#define ZEND_MODULE_GLOBALS_CTOR_D(module) void ZEND_MODULE_GLOBALS_CTOR_N(module)(zend_##module##_globals *module##_globals)
#define ZEND_MODULE_GLOBALS_DTOR_D(module) void ZEND_MODULE_GLOBALS_DTOR_N(module)(zend_##module##_globals *module##_globals)

#define ZEND_MODULE_ENTRY(name) (&name##_module_entry)

#define ZEND_GET_MODULE(name) \
BEGIN_EXTERN_C()\
ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }\
ZEND_DLEXPORT zend_module_entry *get_module(void) { return ZEND_MODULE_ENTRY(name); }\
END_EXTERN_C()

#define ZEND_BEGIN_MODULE_GLOBALS(module_name) \
Expand Down
45 changes: 45 additions & 0 deletions Zend/zend_extensions.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,50 @@

#include "zend_extensions.h"
#include "zend_system_id.h"
#include "zend_API.h"
#include "zend_modules.h"

ZEND_API zend_llist zend_extensions;
ZEND_API uint32_t zend_extension_flags = 0;
ZEND_API int zend_op_array_extension_handles = 0;
ZEND_API int zend_internal_function_extension_handles = 0;
static int last_resource_number;

// TODO: revisit this name if we want to make it ZEND_API.
static zend_result zend_try_register_hybrid_module(zend_extension *extension)
{
zend_module_entry *module = extension->module_entry;

if (!module) {
return SUCCESS;
}

if (module->zend_api != ZEND_MODULE_API_NO) {
zend_error(E_CORE_WARNING,
"Hybrid module \"%s\" from Zend extension \"%s\" cannot be initialized: "
"Module API=%d, PHP API=%d",
module->name ? module->name : "<unknown>",
extension->name ? extension->name : "<unknown>",
module->zend_api, ZEND_MODULE_API_NO);
return FAILURE;
}

if (!module->build_id || strcmp(module->build_id, ZEND_MODULE_BUILD_ID)) {
zend_error(E_CORE_WARNING,
"Hybrid module \"%s\" from Zend extension \"%s\" cannot be initialized: "
"Module build ID=%s, PHP build ID=%s",
module->name ? module->name : "<unknown>",
extension->name ? extension->name : "<unknown>",
module->build_id ? module->build_id : "<unknown>",
ZEND_MODULE_BUILD_ID);
return FAILURE;
}

// The handle is owned by the zend_extension for hybrid extensions.
module->handle = NULL;
return zend_register_module_ex(module, MODULE_PERSISTENT) ? SUCCESS : FAILURE;
}

zend_result zend_load_extension(const char *path)
{
#if ZEND_EXTENSIONS_SUPPORT
Expand Down Expand Up @@ -134,6 +171,14 @@ zend_result zend_load_extension_handle(DL_HANDLE handle, const char *path)
return FAILURE;
}

// The module_entry is registered before the extension is registered
// because zend_register_extension() returns void and is ZEND_API, so
// operations which can fail need to be performed before it.
if (zend_try_register_hybrid_module(new_extension) != SUCCESS) {
DL_UNLOAD(handle);
return FAILURE;
}

zend_register_extension(new_extension, handle);
return SUCCESS;
#else
Expand Down
15 changes: 14 additions & 1 deletion Zend/zend_extensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef struct _zend_extension_version_info {
#define ZEND_EXTENSION_BUILD_ID "API" ZEND_TOSTR(ZEND_EXTENSION_API_NO) ZEND_BUILD_TS ZEND_BUILD_DEBUG ZEND_BUILD_SYSTEM ZEND_BUILD_EXTRA

typedef struct _zend_extension zend_extension;
typedef struct _zend_module_entry zend_module_entry;

/* Typedef's for zend_extension function pointers */
typedef int (*startup_func_t)(zend_extension *extension);
Expand Down Expand Up @@ -101,7 +102,19 @@ struct _zend_extension {
int (*build_id_check)(const char* build_id);
op_array_persist_calc_func_t op_array_persist_calc;
op_array_persist_func_t op_array_persist;
void *reserved5;

/* Setting a module_entry indicates a hybrid extension, meaning an
* extension which is also a module. Such extensions can be loaded with
* either "zend_extension=<name>" or "extension=<name>" by INI.
*
* The symbol "get_module" must _not_ be exported, i.e. don't call
* ZEND_GET_MODULE(), and instead use ZEND_MODULE_ENTRY() to assign a value
* to `.module_entry`.
*
* The DL_HANDLE is owned by the zend_extension for hybrid extensions, so
* the handle should be null for the module entry.
*/
zend_module_entry *module_entry;
void *reserved6;
void *reserved7;
void *reserved8;
Expand Down
41 changes: 31 additions & 10 deletions ext/standard/dl.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "php_globals.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "Zend/zend_API.h"
#include "Zend/zend_extensions.h"

#include "SAPI.h"

Expand Down Expand Up @@ -106,6 +108,28 @@ PHPAPI void *php_load_shlib(const char *path, char **errp)
}
/* }}} */

/* This helper handles the hybrid zend_{extension,module_entry} fallback path.
* It unloads the handle on failure but it does not free libpath. */
static zend_result php_try_load_hybrid_zend_extension(
DL_HANDLE handle, const char *filename, char *libpath, int error_type)
{
zend_extension *zend_extension_entry = (zend_extension *) DL_FETCH_SYMBOL(handle, "zend_extension_entry");
if (!zend_extension_entry) {
zend_extension_entry = (zend_extension *) DL_FETCH_SYMBOL(handle, "_zend_extension_entry");
}
if (zend_extension_entry && zend_extension_entry->module_entry) {
return zend_load_extension_handle(handle, libpath);
}
if (zend_extension_entry) {
php_error_docref(NULL, error_type, "Invalid library (appears to be a Zend Extension, try loading using zend_extension=%s from php.ini)", filename);
DL_UNLOAD(handle);
return FAILURE;
}
php_error_docref(NULL, error_type, "Invalid library (maybe not a PHP library) '%s'", filename);
DL_UNLOAD(handle);
return FAILURE;
}

/* {{{ php_load_extension */
PHPAPI int php_load_extension(const char *filename, int type, int start_now)
{
Expand Down Expand Up @@ -173,13 +197,12 @@ PHPAPI int php_load_extension(const char *filename, int type, int start_now)
efree(orig_libpath);
efree(err1);
}
efree(libpath);

#ifdef PHP_WIN32
if (!php_win32_image_compatible(handle, &err1)) {
php_error_docref(NULL, error_type, "%s", err1);
efree(err1);
DL_UNLOAD(handle);
efree(libpath);
return FAILURE;
}
#endif
Expand All @@ -195,15 +218,13 @@ PHPAPI int php_load_extension(const char *filename, int type, int start_now)
}

if (!get_module) {
if (DL_FETCH_SYMBOL(handle, "zend_extension_entry") || DL_FETCH_SYMBOL(handle, "_zend_extension_entry")) {
DL_UNLOAD(handle);
php_error_docref(NULL, error_type, "Invalid library (appears to be a Zend Extension, try loading using zend_extension=%s from php.ini)", filename);
return FAILURE;
}
DL_UNLOAD(handle);
php_error_docref(NULL, error_type, "Invalid library (maybe not a PHP library) '%s'", filename);
return FAILURE;
// If get_module still isn't found, maybe it's a zend_extension with a module entry.
zend_result result = php_try_load_hybrid_zend_extension(handle, filename, libpath, error_type);
efree(libpath);
return result;
}
efree(libpath);

module_entry = get_module();
if (zend_hash_str_exists(&module_registry, module_entry->name, strlen(module_entry->name))) {
zend_error(E_CORE_WARNING, "Module \"%s\" is already loaded", module_entry->name);
Expand Down
Loading