1 /*-------------------------------------------------------------------------
2 * C-Pluff, a plug-in framework for C
3 * Copyright 2007 Johannes Lehtinen
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *-----------------------------------------------------------------------*/
25 * Dynamic plug-in symbols
29 #ifndef _POSIX_C_SOURCE
30 // Defining _POSIX_C_SOURCE macro with 200809L (or greater) as value
31 // causes header files to expose definitions
32 // corresponding to the POSIX.1-2008 base
33 // specification (excluding the XSI extension).
34 // For POSIX.1-2008 base specification,
35 // Refer http://pubs.opengroup.org/stage7tc1/
37 // For this specific file, see use of strndup,
38 // Refer http://man7.org/linux/man-pages/man3/strdup.3.html
39 #define _POSIX_C_SOURCE 200809L
46 #include "../kazlib/hash.h"
53 /* ------------------------------------------------------------------------
55 * ----------------------------------------------------------------------*/
57 /// Information about symbol providing plug-in
58 typedef struct symbol_provider_info_t {
60 // The providing plug-in
63 // Whether there is also an import dependency for the plug-in
66 // Total symbol usage count
69 } symbol_provider_info_t;
71 /// Information about used symbol
72 typedef struct symbol_info_t {
77 // Information about providing plug-in
78 symbol_provider_info_t *provider_info;
83 /* ------------------------------------------------------------------------
84 * Function definitions
85 * ----------------------------------------------------------------------*/
87 CP_C_API cp_status_t cp_define_symbol(cp_context_t *context, const char *name, void *ptr) {
88 cp_status_t status = CP_OK;
90 CHECK_NOT_NULL(context);
92 // CHECK_NOT_NULL(ptr);
93 if (context->plugin == NULL) {
94 cpi_fatalf(_("Only plug-ins can define context specific symbols."));
97 cpi_lock_context(context);
98 cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
102 // Create a symbol hash if necessary
103 if (context->plugin->defined_symbols == NULL) {
104 if ((context->plugin->defined_symbols = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
105 status = CP_ERR_RESOURCE;
110 // Check for a previously defined symbol
111 if (hash_lookup(context->plugin->defined_symbols, name) != NULL) {
112 status = CP_ERR_CONFLICT;
116 // Insert the symbol into the symbol hash
118 if (n == NULL || !hash_alloc_insert(context->plugin->defined_symbols, n, ptr)) {
120 status = CP_ERR_RESOURCE;
127 if (status != CP_OK) {
129 case CP_ERR_RESOURCE:
130 cpi_errorf(context, N_("Plug-in %s could not define symbol %s due to insufficient memory."), context->plugin->plugin->identifier, name);
132 case CP_ERR_CONFLICT:
133 cpi_errorf(context, N_("Plug-in %s tried to redefine symbol %s."), context->plugin->plugin->identifier, name);
139 cpi_unlock_context(context);
144 CP_C_API void * cp_resolve_symbol(cp_context_t *context, const char *id, const char *name, cp_status_t *error) {
145 cp_status_t status = CP_OK;
146 int error_reported = 1;
149 symbol_info_t *symbol_info = NULL;
150 symbol_provider_info_t *provider_info = NULL;
151 cp_plugin_t *pp = NULL;
153 CHECK_NOT_NULL(context);
155 CHECK_NOT_NULL(name);
157 // Resolve the symbol
158 cpi_lock_context(context);
159 cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER | CPI_CF_STOP, __func__);
162 // Allocate space for symbol hashes, if necessary
163 if (context->resolved_symbols == NULL) {
164 context->resolved_symbols = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
166 if (context->symbol_providers == NULL) {
167 context->symbol_providers = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
169 if (context->resolved_symbols == NULL
170 || context->symbol_providers == NULL) {
171 status = CP_ERR_RESOURCE;
175 // Look up the symbol defining plug-in
176 node = hash_lookup(context->env->plugins, id);
178 cpi_warnf(context, N_("Symbol %s in unknown plug-in %s could not be resolved."), name, id);
179 status = CP_ERR_UNKNOWN;
182 pp = hnode_get(node);
184 // Make sure the plug-in has been started
185 if ((status = cpi_start_plugin(context, pp)) != CP_OK) {
186 cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved because the plug-in could not be started."), name, id);
191 // Check for a context specific symbol
192 if (pp->defined_symbols != NULL && (node = hash_lookup(pp->defined_symbols, name)) != NULL) {
193 symbol = hnode_get(node);
196 // Fall back to global symbols, if necessary
197 if (symbol == NULL && pp->runtime_lib != NULL) {
198 symbol = DLSYM(pp->runtime_lib, name);
200 if (symbol == NULL) {
201 const char *error = DLERROR();
203 error = _("Unspecified error.");
205 cpi_warnf(context, N_("Symbol %s in plug-in %s could not be resolved: %s"), name, id, error);
206 status = CP_ERR_UNKNOWN;
210 // Lookup or initialize symbol provider information
211 if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
212 provider_info = hnode_get(node);
214 if ((provider_info = malloc(sizeof(symbol_provider_info_t))) == NULL) {
215 status = CP_ERR_RESOURCE;
218 memset(provider_info, 0, sizeof(symbol_provider_info_t));
219 provider_info->plugin = pp;
220 provider_info->imported = (context->plugin == NULL || cpi_ptrset_contains(context->plugin->imported, pp));
221 if (!hash_alloc_insert(context->symbol_providers, pp, provider_info)) {
222 status = CP_ERR_RESOURCE;
227 // Lookup or initialize symbol information
228 if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
229 symbol_info = hnode_get(node);
231 if ((symbol_info = malloc(sizeof(symbol_info_t))) == NULL) {
232 status = CP_ERR_RESOURCE;
235 memset(symbol_info, 0, sizeof(symbol_info_t));
236 symbol_info->provider_info = provider_info;
237 if (!hash_alloc_insert(context->resolved_symbols, symbol, symbol_info)) {
238 status = CP_ERR_RESOURCE;
243 // Add dependencies (for plug-in)
244 if (provider_info != NULL
245 && !provider_info->imported
246 && provider_info->usage_count == 0) {
247 if (!cpi_ptrset_add(context->plugin->imported, pp)) {
248 status = CP_ERR_RESOURCE;
251 if (!cpi_ptrset_add(pp->importing, context->plugin)) {
252 cpi_ptrset_remove(context->plugin->imported, pp);
253 status = CP_ERR_RESOURCE;
256 cpi_debugf(context, "A dynamic dependency was created from plug-in %s to plug-in %s.", context->plugin->plugin->identifier, pp->plugin->identifier);
259 // Increase usage counts
260 symbol_info->usage_count++;
261 provider_info->usage_count++;
263 if (cpi_is_logged(context, CP_LOG_DEBUG)) {
265 /* TRANSLATORS: First %s is the context owner */
266 cpi_debugf(context, "%s resolved symbol %s defined by plug-in %s.", cpi_context_owner(context, owner, sizeof(owner)), name, id);
271 if (symbol_info != NULL && symbol_info->usage_count == 0) {
272 if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
273 hash_delete_free(context->resolved_symbols, node);
277 if (provider_info != NULL && provider_info->usage_count == 0) {
278 if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
279 hash_delete_free(context->symbol_providers, node);
284 // Report insufficient memory error
285 if (status == CP_ERR_RESOURCE && !error_reported) {
286 cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved due to insufficient memory."), name, id);
288 cpi_unlock_context(context);
299 CP_C_API void cp_release_symbol(cp_context_t *context, const void *ptr) {
301 symbol_info_t *symbol_info;
302 symbol_provider_info_t *provider_info;
304 CHECK_NOT_NULL(context);
307 cpi_lock_context(context);
308 cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
311 // Look up the symbol
312 if ((node = hash_lookup(context->resolved_symbols, ptr)) == NULL) {
313 cpi_errorf(context, N_("Could not release unknown symbol at address %p."), ptr);
316 symbol_info = hnode_get(node);
317 provider_info = symbol_info->provider_info;
319 // Decrease usage count
320 assert(symbol_info->usage_count > 0);
321 symbol_info->usage_count--;
322 assert(provider_info->usage_count > 0);
323 provider_info->usage_count--;
325 // Check if the symbol is not being used anymore
326 if (symbol_info->usage_count == 0) {
327 hash_delete_free(context->resolved_symbols, node);
329 if (cpi_is_logged(context, CP_LOG_DEBUG)) {
331 /* TRANSLATORS: First %s is the context owner */
332 cpi_debugf(context, _("%s released the symbol at address %p defined by plug-in %s."), cpi_context_owner(context, owner, sizeof(owner)), ptr, provider_info->plugin->plugin->identifier);
336 // Check if the symbol providing plug-in is not being used anymore
337 if (provider_info->usage_count == 0) {
338 node = hash_lookup(context->symbol_providers, provider_info->plugin);
339 assert(node != NULL);
340 hash_delete_free(context->symbol_providers, node);
341 if (!provider_info->imported) {
342 cpi_ptrset_remove(context->plugin->imported, provider_info->plugin);
343 cpi_ptrset_remove(provider_info->plugin->importing, context->plugin);
344 cpi_debugf(context, _("A dynamic dependency from plug-in %s to plug-in %s was removed."), context->plugin->plugin->identifier, provider_info->plugin->plugin->identifier);
350 cpi_unlock_context(context);