0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-18 16:00:57 +01:00
construct/libltdl/ltdl.c
2016-01-05 20:23:56 -06:00

2464 lines
54 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ltdl.c -- system independent dlopen wrapper
Copyright (C) 1998, 1999, 2000, 2004, 2005, 2006,
2007, 2008, 2011 Free Software Foundation, Inc.
Written by Thomas Tanner, 1998
NOTE: The canonical source of this file is maintained with the
GNU Libtool package. Report bugs to bug-libtool@gnu.org.
GNU Libltdl is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
As a special exception to the GNU Lesser General Public License,
if you distribute this file as part of a program or library that
is built using GNU Libtool, you may include this file under the
same distribution terms that you use for the rest of that program.
GNU Libltdl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with GNU Libltdl; see the file COPYING.LIB. If not, a
copy can be downloaded from http://www.gnu.org/licenses/lgpl.html,
or obtained by writing to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "lt__private.h"
#include "lt_system.h"
#include "lt_dlloader.h"
/* --- MANIFEST CONSTANTS --- */
/* Standard libltdl search path environment variable name */
#undef LTDL_SEARCHPATH_VAR
#define LTDL_SEARCHPATH_VAR "LTDL_LIBRARY_PATH"
/* Standard libtool archive file extension. */
#undef LT_ARCHIVE_EXT
#define LT_ARCHIVE_EXT ".la"
/* max. filename length */
#if !defined(LT_FILENAME_MAX)
# define LT_FILENAME_MAX 1024
#endif
#if !defined(LT_LIBEXT)
# define LT_LIBEXT "a"
#endif
#if !defined(LT_LIBPREFIX)
# define LT_LIBPREFIX "lib"
#endif
/* This is the maximum symbol size that won't require malloc/free */
#undef LT_SYMBOL_LENGTH
#define LT_SYMBOL_LENGTH 128
/* This accounts for the _LTX_ separator */
#undef LT_SYMBOL_OVERHEAD
#define LT_SYMBOL_OVERHEAD 5
/* Various boolean flags can be stored in the flags field of an
lt_dlhandle... */
#define LT_DLIS_RESIDENT(handle) ((handle)->info.is_resident)
#define LT_DLIS_SYMGLOBAL(handle) ((handle)->info.is_symglobal)
#define LT_DLIS_SYMLOCAL(handle) ((handle)->info.is_symlocal)
static const char objdir[] = LT_OBJDIR;
static const char archive_ext[] = LT_ARCHIVE_EXT;
static const char libext[] = LT_LIBEXT;
static const char libprefix[] = LT_LIBPREFIX;
#if defined(LT_MODULE_EXT)
static const char shlib_ext[] = LT_MODULE_EXT;
#endif
/* If the loadable module suffix is not the same as the linkable
* shared library suffix, this will be defined. */
#if defined(LT_SHARED_EXT)
static const char shared_ext[] = LT_SHARED_EXT;
#endif
#if defined(LT_DLSEARCH_PATH)
static const char sys_dlsearch_path[] = LT_DLSEARCH_PATH;
#endif
/* --- DYNAMIC MODULE LOADING --- */
/* The type of a function used at each iteration of foreach_dirinpath(). */
typedef int foreach_callback_func (char *filename, void *data1,
void *data2);
/* foreachfile_callback itself calls a function of this type: */
typedef int file_worker_func (const char *filename, void *data);
static int foreach_dirinpath (const char *search_path,
const char *base_name,
foreach_callback_func *func,
void *data1, void *data2);
static int find_file_callback (char *filename, void *data1,
void *data2);
static int find_handle_callback (char *filename, void *data,
void *ignored);
static int foreachfile_callback (char *filename, void *data1,
void *data2);
static int canonicalize_path (const char *path, char **pcanonical);
static int argzize_path (const char *path,
char **pargz, size_t *pargz_len);
static FILE *find_file (const char *search_path,
const char *base_name, char **pdir);
static lt_dlhandle *find_handle (const char *search_path,
const char *base_name,
lt_dlhandle *handle,
lt_dladvise advise);
static int find_module (lt_dlhandle *handle, const char *dir,
const char *libdir, const char *dlname,
const char *old_name, int installed,
lt_dladvise advise);
static int has_library_ext (const char *filename);
static int load_deplibs (lt_dlhandle handle, char *deplibs);
static int trim (char **dest, const char *str);
static int try_dlopen (lt_dlhandle *handle,
const char *filename, const char *ext,
lt_dladvise advise);
static int tryall_dlopen (lt_dlhandle *handle,
const char *filename,
lt_dladvise padvise,
const lt_dlvtable *vtable);
static int unload_deplibs (lt_dlhandle handle);
static int lt_argz_insert (char **pargz, size_t *pargz_len,
char *before, const char *entry);
static int lt_argz_insertinorder (char **pargz, size_t *pargz_len,
const char *entry);
static int lt_argz_insertdir (char **pargz, size_t *pargz_len,
const char *dirnam, struct dirent *dp);
static int lt_dlpath_insertdir (char **ppath, char *before,
const char *dir);
static int list_files_by_dir (const char *dirnam,
char **pargz, size_t *pargz_len);
static int file_not_found (void);
#ifdef HAVE_LIBDLLOADER
static int loader_init_callback (lt_dlhandle handle);
#endif /* HAVE_LIBDLLOADER */
static int loader_init (lt_get_vtable *vtable_func,
lt_user_data data);
static char *user_search_path= 0;
static lt_dlhandle handles = 0;
static int initialized = 0;
/* Our memory failure callback sets the error message to be passed back
up to the client, so we must be careful to return from mallocation
callers if allocation fails (as this callback returns!!). */
void
lt__alloc_die_callback (void)
{
LT__SETERROR (NO_MEMORY);
}
#ifdef HAVE_LIBDLLOADER
/* This function is called to initialise each preloaded module loader,
and hook it into the list of loaders to be used when attempting to
dlopen an application module. */
static int
loader_init_callback (lt_dlhandle handle)
{
lt_get_vtable *vtable_func = (lt_get_vtable *) lt_dlsym (handle, "get_vtable");
return loader_init (vtable_func, 0);
}
#endif /* HAVE_LIBDLLOADER */
static int
loader_init (lt_get_vtable *vtable_func, lt_user_data data)
{
const lt_dlvtable *vtable = 0;
int errors = 0;
if (vtable_func)
{
vtable = (*vtable_func) (data);
}
/* lt_dlloader_add will LT__SETERROR if it fails. */
errors += lt_dlloader_add (vtable);
assert (errors || vtable);
if ((!errors) && vtable->dlloader_init)
{
if ((*vtable->dlloader_init) (vtable->dlloader_data))
{
LT__SETERROR (INIT_LOADER);
++errors;
}
}
return errors;
}
/* Bootstrap the loader loading with the preopening loader. */
#define get_vtable preopen_LTX_get_vtable
#define preloaded_symbols LT_CONC3(lt_, LTDLOPEN, _LTX_preloaded_symbols)
LT_BEGIN_C_DECLS
LT_SCOPE const lt_dlvtable * get_vtable (lt_user_data data);
LT_END_C_DECLS
#ifdef HAVE_LIBDLLOADER
extern LT_DLSYM_CONST lt_dlsymlist preloaded_symbols[];
#endif
/* Initialize libltdl. */
int
lt_dlinit (void)
{
int errors = 0;
/* Initialize only at first call. */
if (++initialized == 1)
{
lt__alloc_die = lt__alloc_die_callback;
handles = 0;
user_search_path = 0; /* empty search path */
/* First set up the statically loaded preload module loader, so
we can use it to preopen the other loaders we linked in at
compile time. */
errors += loader_init (get_vtable, 0);
/* Now open all the preloaded module loaders, so the application
can use _them_ to lt_dlopen its own modules. */
#ifdef HAVE_LIBDLLOADER
if (!errors)
{
errors += lt_dlpreload (preloaded_symbols);
}
if (!errors)
{
errors += lt_dlpreload_open (LT_STR(LTDLOPEN), loader_init_callback);
}
#endif /* HAVE_LIBDLLOADER */
}
#ifdef LT_DEBUG_LOADERS
lt_dlloader_dump();
#endif
return errors;
}
int
lt_dlexit (void)
{
/* shut down libltdl */
lt_dlloader *loader = 0;
lt_dlhandle handle = handles;
int errors = 0;
if (!initialized)
{
LT__SETERROR (SHUTDOWN);
++errors;
goto done;
}
/* shut down only at last call. */
if (--initialized == 0)
{
int level;
while (handles && LT_DLIS_RESIDENT (handles))
{
handles = handles->next;
}
/* close all modules */
for (level = 1; handle; ++level)
{
lt_dlhandle cur = handles;
int saw_nonresident = 0;
while (cur)
{
lt_dlhandle tmp = cur;
cur = cur->next;
if (!LT_DLIS_RESIDENT (tmp))
{
saw_nonresident = 1;
if (tmp->info.ref_count <= level)
{
if (lt_dlclose (tmp))
{
++errors;
}
/* Make sure that the handle pointed to by 'cur' still exists.
lt_dlclose recursively closes dependent libraries which removes
them from the linked list. One of these might be the one
pointed to by 'cur'. */
if (cur)
{
for (tmp = handles; tmp; tmp = tmp->next)
if (tmp == cur)
break;
if (! tmp)
cur = handles;
}
}
}
}
/* done if only resident modules are left */
if (!saw_nonresident)
break;
}
/* When removing loaders, we can only find out failure by testing
the error string, so avoid a spurious one from an earlier
failed command. */
if (!errors)
LT__SETERRORSTR (0);
/* close all loaders */
for (loader = (lt_dlloader *) lt_dlloader_next (NULL); loader;)
{
lt_dlloader *next = (lt_dlloader *) lt_dlloader_next (loader);
lt_dlvtable *vtable = (lt_dlvtable *) lt_dlloader_get (loader);
if ((vtable = lt_dlloader_remove ((char *) vtable->name)))
{
FREE (vtable);
}
else
{
/* ignore errors due to resident modules */
const char *err;
LT__GETERROR (err);
if (err)
++errors;
}
loader = next;
}
FREE(user_search_path);
}
done:
return errors;
}
/* Try VTABLE or, if VTABLE is NULL, all available loaders for FILENAME.
If the library is not successfully loaded, return non-zero. Otherwise,
the dlhandle is stored at the address given in PHANDLE. */
static int
tryall_dlopen (lt_dlhandle *phandle, const char *filename,
lt_dladvise advise, const lt_dlvtable *vtable)
{
lt_dlhandle handle = handles;
const char * saved_error = 0;
int errors = 0;
#ifdef LT_DEBUG_LOADERS
fprintf (stderr, "tryall_dlopen (%s, %s)\n",
filename ? filename : "(null)",
vtable ? vtable->name : "(ALL)");
#endif
LT__GETERROR (saved_error);
/* check whether the module was already opened */
for (;handle; handle = handle->next)
{
if ((handle->info.filename == filename) /* dlopen self: 0 == 0 */
|| (handle->info.filename && filename
&& streq (handle->info.filename, filename)))
{
break;
}
}
if (handle)
{
++handle->info.ref_count;
*phandle = handle;
goto done;
}
handle = *phandle;
if (filename)
{
/* Comment out the check of file permissions using access.
This call seems to always return -1 with error EACCES.
*/
/* We need to catch missing file errors early so that
file_not_found() can detect what happened.
if (access (filename, R_OK) != 0)
{
LT__SETERROR (FILE_NOT_FOUND);
++errors;
goto done;
} */
handle->info.filename = lt__strdup (filename);
if (!handle->info.filename)
{
++errors;
goto done;
}
}
else
{
handle->info.filename = 0;
}
{
lt_dlloader loader = lt_dlloader_next (0);
const lt_dlvtable *loader_vtable;
do
{
if (vtable)
loader_vtable = vtable;
else
loader_vtable = lt_dlloader_get (loader);
#ifdef LT_DEBUG_LOADERS
fprintf (stderr, "Calling %s->module_open (%s)\n",
(loader_vtable && loader_vtable->name) ? loader_vtable->name : "(null)",
filename ? filename : "(null)");
#endif
handle->module = (*loader_vtable->module_open) (loader_vtable->dlloader_data,
filename, advise);
#ifdef LT_DEBUG_LOADERS
fprintf (stderr, " Result: %s\n",
handle->module ? "Success" : "Failed");
#endif
if (handle->module != 0)
{
if (advise)
{
handle->info.is_resident = advise->is_resident;
handle->info.is_symglobal = advise->is_symglobal;
handle->info.is_symlocal = advise->is_symlocal;
}
break;
}
}
while (!vtable && (loader = lt_dlloader_next (loader)));
/* If VTABLE was given but couldn't open the module, or VTABLE wasn't
given but we exhausted all loaders without opening the module, bail
out! */
if ((vtable && !handle->module)
|| (!vtable && !loader))
{
FREE (handle->info.filename);
++errors;
goto done;
}
handle->vtable = loader_vtable;
}
LT__SETERRORSTR (saved_error);
done:
return errors;
}
static int
tryall_dlopen_module (lt_dlhandle *handle, const char *prefix,
const char *dirname, const char *dlname,
lt_dladvise advise)
{
int error = 0;
char *filename = 0;
size_t filename_len = 0;
size_t dirname_len = LT_STRLEN (dirname);
assert (handle);
assert (dirname);
assert (dlname);
#if defined(LT_DIRSEP_CHAR)
/* Only canonicalized names (i.e. with DIRSEP chars already converted)
should make it into this function: */
assert (strchr (dirname, LT_DIRSEP_CHAR) == 0);
#endif
if (dirname_len > 0)
if (dirname[dirname_len -1] == '/')
--dirname_len;
filename_len = dirname_len + 1 + LT_STRLEN (dlname);
/* Allocate memory, and combine DIRNAME and MODULENAME into it.
The PREFIX (if any) is handled below. */
filename = MALLOC (char, filename_len + 1);
if (!filename)
return 1;
sprintf (filename, "%.*s/%s", (int) dirname_len, dirname, dlname);
/* Now that we have combined DIRNAME and MODULENAME, if there is
also a PREFIX to contend with, simply recurse with the arguments
shuffled. Otherwise, attempt to open FILENAME as a module. */
if (prefix)
{
error += tryall_dlopen_module (handle, (const char *) 0,
prefix, filename, advise);
}
else if (tryall_dlopen (handle, filename, advise, 0) != 0)
{
++error;
}
FREE (filename);
return error;
}
static int
find_module (lt_dlhandle *handle, const char *dir, const char *libdir,
const char *dlname, const char *old_name, int installed,
lt_dladvise advise)
{
/* Try to open the old library first; if it was dlpreopened,
we want the preopened version of it, even if a dlopenable
module is available. */
if (old_name && tryall_dlopen (handle, old_name,
advise, lt_dlloader_find ("lt_preopen") ) == 0)
{
return 0;
}
/* Try to open the dynamic library. */
if (dlname)
{
/* try to open the installed module */
if (installed && libdir)
{
if (tryall_dlopen_module (handle, (const char *) 0,
libdir, dlname, advise) == 0)
return 0;
}
/* try to open the not-installed module */
if (!installed)
{
if (tryall_dlopen_module (handle, dir, objdir,
dlname, advise) == 0)
return 0;
}
/* maybe it was moved to another directory */
{
if (dir && (tryall_dlopen_module (handle, (const char *) 0,
dir, dlname, advise) == 0))
return 0;
}
}
return 1;
}
static int
canonicalize_path (const char *path, char **pcanonical)
{
char *canonical = 0;
assert (path && *path);
assert (pcanonical);
canonical = MALLOC (char, 1+ LT_STRLEN (path));
if (!canonical)
return 1;
{
size_t dest = 0;
size_t src;
for (src = 0; path[src] != LT_EOS_CHAR; ++src)
{
/* Path separators are not copied to the beginning or end of
the destination, or if another separator would follow
immediately. */
if (path[src] == LT_PATHSEP_CHAR)
{
if ((dest == 0)
|| (path[1+ src] == LT_PATHSEP_CHAR)
|| (path[1+ src] == LT_EOS_CHAR))
continue;
}
/* Anything other than a directory separator is copied verbatim. */
if ((path[src] != '/')
#if defined(LT_DIRSEP_CHAR)
&& (path[src] != LT_DIRSEP_CHAR)
#endif
)
{
canonical[dest++] = path[src];
}
/* Directory separators are converted and copied only if they are
not at the end of a path -- i.e. before a path separator or
NULL terminator. */
else if ((path[1+ src] != LT_PATHSEP_CHAR)
&& (path[1+ src] != LT_EOS_CHAR)
#if defined(LT_DIRSEP_CHAR)
&& (path[1+ src] != LT_DIRSEP_CHAR)
#endif
&& (path[1+ src] != '/'))
{
canonical[dest++] = '/';
}
}
/* Add an end-of-string marker at the end. */
canonical[dest] = LT_EOS_CHAR;
}
/* Assign new value. */
*pcanonical = canonical;
return 0;
}
static int
argzize_path (const char *path, char **pargz, size_t *pargz_len)
{
error_t error;
assert (path);
assert (pargz);
assert (pargz_len);
if ((error = argz_create_sep (path, LT_PATHSEP_CHAR, pargz, pargz_len)))
{
switch (error)
{
case ENOMEM:
LT__SETERROR (NO_MEMORY);
break;
default:
LT__SETERROR (UNKNOWN);
break;
}
return 1;
}
return 0;
}
/* Repeatedly call FUNC with each LT_PATHSEP_CHAR delimited element
of SEARCH_PATH and references to DATA1 and DATA2, until FUNC returns
non-zero or all elements are exhausted. If BASE_NAME is non-NULL,
it is appended to each SEARCH_PATH element before FUNC is called. */
static int
foreach_dirinpath (const char *search_path, const char *base_name,
foreach_callback_func *func, void *data1, void *data2)
{
int result = 0;
size_t filenamesize = 0;
size_t lenbase = LT_STRLEN (base_name);
size_t argz_len = 0;
char *argz = 0;
char *filename = 0;
char *canonical = 0;
if (!search_path || !*search_path)
{
LT__SETERROR (FILE_NOT_FOUND);
goto cleanup;
}
if (canonicalize_path (search_path, &canonical) != 0)
goto cleanup;
if (argzize_path (canonical, &argz, &argz_len) != 0)
goto cleanup;
{
char *dir_name = 0;
while ((dir_name = argz_next (argz, argz_len, dir_name)))
{
size_t lendir = LT_STRLEN (dir_name);
if (1+ lendir + lenbase >= filenamesize)
{
FREE (filename);
filenamesize = 1+ lendir + 1+ lenbase; /* "/d" + '/' + "f" + '\0' */
filename = MALLOC (char, filenamesize);
if (!filename)
goto cleanup;
}
assert (filenamesize > lendir);
strcpy (filename, dir_name);
if (base_name && *base_name)
{
if (filename[lendir -1] != '/')
filename[lendir++] = '/';
strcpy (filename +lendir, base_name);
}
if ((result = (*func) (filename, data1, data2)))
{
break;
}
}
}
cleanup:
FREE (argz);
FREE (canonical);
FREE (filename);
return result;
}
/* If FILEPATH can be opened, store the name of the directory component
in DATA1, and the opened FILE* structure address in DATA2. Otherwise
DATA1 is unchanged, but DATA2 is set to a pointer to NULL. */
static int
find_file_callback (char *filename, void *data1, void *data2)
{
char **pdir = (char **) data1;
FILE **pfile = (FILE **) data2;
int is_done = 0;
assert (filename && *filename);
assert (pdir);
assert (pfile);
if ((*pfile = fopen (filename, LT_READTEXT_MODE)))
{
char *dirend = strrchr (filename, '/');
if (dirend > filename)
*dirend = LT_EOS_CHAR;
FREE (*pdir);
*pdir = lt__strdup (filename);
is_done = (*pdir == 0) ? -1 : 1;
}
return is_done;
}
static FILE *
find_file (const char *search_path, const char *base_name, char **pdir)
{
FILE *file = 0;
foreach_dirinpath (search_path, base_name, find_file_callback, pdir, &file);
return file;
}
static int
find_handle_callback (char *filename, void *data, void *data2)
{
lt_dlhandle *phandle = (lt_dlhandle *) data;
int notfound = access (filename, R_OK);
lt_dladvise advise = (lt_dladvise) data2;
/* Bail out if file cannot be read... */
if (notfound)
return 0;
/* Try to dlopen the file, but do not continue searching in any
case. */
if (tryall_dlopen (phandle, filename, advise, 0) != 0)
*phandle = 0;
return 1;
}
/* If HANDLE was found return it, otherwise return 0. If HANDLE was
found but could not be opened, *HANDLE will be set to 0. */
static lt_dlhandle *
find_handle (const char *search_path, const char *base_name,
lt_dlhandle *phandle, lt_dladvise advise)
{
if (!search_path)
return 0;
if (!foreach_dirinpath (search_path, base_name, find_handle_callback,
phandle, advise))
return 0;
return phandle;
}
#if !defined(LTDL_DLOPEN_DEPLIBS)
static int
load_deplibs (lt_dlhandle handle, char * LT__UNUSED deplibs)
{
handle->depcount = 0;
return 0;
}
#else /* defined(LTDL_DLOPEN_DEPLIBS) */
static int
load_deplibs (lt_dlhandle handle, char *deplibs)
{
char *p, *save_search_path = 0;
int depcount = 0;
int i;
char **names = 0;
int errors = 0;
handle->depcount = 0;
if (!deplibs)
{
return errors;
}
++errors;
if (user_search_path)
{
save_search_path = lt__strdup (user_search_path);
if (!save_search_path)
goto cleanup;
}
/* extract search paths and count deplibs */
p = deplibs;
while (*p)
{
if (!isspace ((unsigned char) *p))
{
char *end = p+1;
while (*end && !isspace((unsigned char) *end))
{
++end;
}
if (strncmp(p, "-L", 2) == 0 || strncmp(p, "-R", 2) == 0)
{
char save = *end;
*end = 0; /* set a temporary string terminator */
if (lt_dladdsearchdir(p+2))
{
goto cleanup;
}
*end = save;
}
else
{
++depcount;
}
p = end;
}
else
{
++p;
}
}
if (!depcount)
{
errors = 0;
goto cleanup;
}
names = MALLOC (char *, depcount);
if (!names)
goto cleanup;
/* now only extract the actual deplibs */
depcount = 0;
p = deplibs;
while (*p)
{
if (isspace ((unsigned char) *p))
{
++p;
}
else
{
char *end = p+1;
while (*end && !isspace ((unsigned char) *end))
{
++end;
}
if (strncmp(p, "-L", 2) != 0 && strncmp(p, "-R", 2) != 0)
{
char *name;
char save = *end;
*end = 0; /* set a temporary string terminator */
if (strncmp(p, "-l", 2) == 0)
{
size_t name_len = 3+ /* "lib" */ LT_STRLEN (p + 2);
name = MALLOC (char, 1+ name_len);
if (name)
sprintf (name, "lib%s", p+2);
}
else
name = lt__strdup(p);
if (!name)
goto cleanup_names;
names[depcount++] = name;
*end = save;
}
p = end;
}
}
/* load the deplibs (in reverse order)
At this stage, don't worry if the deplibs do not load correctly,
they may already be statically linked into the loading application
for instance. There will be a more enlightening error message
later on if the loaded module cannot resolve all of its symbols. */
if (depcount)
{
lt_dlhandle cur = handle;
int j = 0;
cur->deplibs = MALLOC (lt_dlhandle, depcount);
if (!cur->deplibs)
goto cleanup_names;
for (i = 0; i < depcount; ++i)
{
cur->deplibs[j] = lt_dlopenext(names[depcount-1-i]);
if (cur->deplibs[j])
{
++j;
}
}
cur->depcount = j; /* Number of successfully loaded deplibs */
errors = 0;
}
cleanup_names:
for (i = 0; i < depcount; ++i)
{
FREE (names[i]);
}
cleanup:
FREE (names);
/* restore the old search path */
if (save_search_path) {
MEMREASSIGN (user_search_path, save_search_path);
}
return errors;
}
#endif /* defined(LTDL_DLOPEN_DEPLIBS) */
static int
unload_deplibs (lt_dlhandle handle)
{
int i;
int errors = 0;
lt_dlhandle cur = handle;
if (cur->depcount)
{
for (i = 0; i < cur->depcount; ++i)
{
if (!LT_DLIS_RESIDENT (cur->deplibs[i]))
{
errors += lt_dlclose (cur->deplibs[i]);
}
}
FREE (cur->deplibs);
}
return errors;
}
static int
trim (char **dest, const char *str)
{
/* remove the leading and trailing "'" from str
and store the result in dest */
const char *end = strrchr (str, '\'');
size_t len = LT_STRLEN (str);
char *tmp;
FREE (*dest);
if (!end || end == str)
return 1;
if (len > 3 && str[0] == '\'')
{
tmp = MALLOC (char, end - str);
if (!tmp)
return 1;
memcpy(tmp, &str[1], (end - str) - 1);
tmp[(end - str) - 1] = LT_EOS_CHAR;
*dest = tmp;
}
else
{
*dest = 0;
}
return 0;
}
/* Read the .la file FILE. */
static int
parse_dotla_file(FILE *file, char **dlname, char **libdir, char **deplibs,
char **old_name, int *installed)
{
int errors = 0;
size_t line_len = LT_FILENAME_MAX;
char * line = MALLOC (char, line_len);
if (!line)
{
LT__SETERROR (FILE_NOT_FOUND);
return 1;
}
while (!feof (file))
{
line[line_len-2] = '\0';
if (!fgets (line, (int) line_len, file))
{
break;
}
/* Handle the case where we occasionally need to read a line
that is longer than the initial buffer size.
Behave even if the file contains NUL bytes due to corruption. */
while (line[line_len-2] != '\0' && line[line_len-2] != '\n' && !feof (file))
{
line = REALLOC (char, line, line_len *2);
if (!line)
{
++errors;
goto cleanup;
}
line[line_len * 2 - 2] = '\0';
if (!fgets (&line[line_len -1], (int) line_len +1, file))
{
break;
}
line_len *= 2;
}
if (line[0] == '\n' || line[0] == '#')
{
continue;
}
#undef STR_DLNAME
#define STR_DLNAME "dlname="
if (strncmp (line, STR_DLNAME, sizeof (STR_DLNAME) - 1) == 0)
{
errors += trim (dlname, &line[sizeof (STR_DLNAME) - 1]);
}
#undef STR_OLD_LIBRARY
#define STR_OLD_LIBRARY "old_library="
else if (strncmp (line, STR_OLD_LIBRARY,
sizeof (STR_OLD_LIBRARY) - 1) == 0)
{
errors += trim (old_name, &line[sizeof (STR_OLD_LIBRARY) - 1]);
}
/* Windows native tools do not understand the POSIX paths we store
in libdir. */
#undef STR_LIBDIR
#define STR_LIBDIR "libdir="
else if (strncmp (line, STR_LIBDIR, sizeof (STR_LIBDIR) - 1) == 0)
{
errors += trim (libdir, &line[sizeof(STR_LIBDIR) - 1]);
#ifdef __WINDOWS__
/* Disallow following unix-style paths on MinGW. */
if (*libdir && (**libdir == '/' || **libdir == '\\'))
**libdir = '\0';
#endif
}
#undef STR_DL_DEPLIBS
#define STR_DL_DEPLIBS "dependency_libs="
else if (strncmp (line, STR_DL_DEPLIBS,
sizeof (STR_DL_DEPLIBS) - 1) == 0)
{
errors += trim (deplibs, &line[sizeof (STR_DL_DEPLIBS) - 1]);
}
else if (streq (line, "installed=yes\n"))
{
*installed = 1;
}
else if (streq (line, "installed=no\n"))
{
*installed = 0;
}
#undef STR_LIBRARY_NAMES
#define STR_LIBRARY_NAMES "library_names="
else if (!*dlname && strncmp (line, STR_LIBRARY_NAMES,
sizeof (STR_LIBRARY_NAMES) - 1) == 0)
{
char *last_libname;
errors += trim (dlname, &line[sizeof (STR_LIBRARY_NAMES) - 1]);
if (!errors
&& *dlname
&& (last_libname = strrchr (*dlname, ' ')) != 0)
{
last_libname = lt__strdup (last_libname + 1);
if (!last_libname)
{
++errors;
goto cleanup;
}
MEMREASSIGN (*dlname, last_libname);
}
}
if (errors)
break;
}
cleanup:
FREE (line);
return errors;
}
/* Try to open FILENAME as a module. */
static int
try_dlopen (lt_dlhandle *phandle, const char *filename, const char *ext,
lt_dladvise advise)
{
const char * saved_error = 0;
char * archive_name = 0;
char * canonical = 0;
char * base_name = 0;
char * dir = 0;
char * name = 0;
char * attempt = 0;
int errors = 0;
lt_dlhandle newhandle;
assert (phandle);
assert (*phandle == 0);
#ifdef LT_DEBUG_LOADERS
fprintf (stderr, "try_dlopen (%s, %s)\n",
filename ? filename : "(null)",
ext ? ext : "(null)");
#endif
LT__GETERROR (saved_error);
/* dlopen self? */
if (!filename)
{
*phandle = (lt_dlhandle) lt__zalloc (sizeof (struct lt__handle));
if (*phandle == 0)
return 1;
newhandle = *phandle;
/* lt_dlclose()ing yourself is very bad! Disallow it. */
newhandle->info.is_resident = 1;
if (tryall_dlopen (&newhandle, 0, advise, 0) != 0)
{
FREE (*phandle);
return 1;
}
goto register_handle;
}
assert (filename && *filename);
if (ext)
{
attempt = MALLOC (char, LT_STRLEN (filename) + LT_STRLEN (ext) + 1);
if (!attempt)
return 1;
sprintf(attempt, "%s%s", filename, ext);
}
else
{
attempt = lt__strdup (filename);
if (!attempt)
return 1;
}
/* Doing this immediately allows internal functions to safely
assume only canonicalized paths are passed. */
if (canonicalize_path (attempt, &canonical) != 0)
{
++errors;
goto cleanup;
}
/* If the canonical module name is a path (relative or absolute)
then split it into a directory part and a name part. */
base_name = strrchr (canonical, '/');
if (base_name)
{
size_t dirlen = (1+ base_name) - canonical;
dir = MALLOC (char, 1+ dirlen);
if (!dir)
{
++errors;
goto cleanup;
}
strncpy (dir, canonical, dirlen);
dir[dirlen] = LT_EOS_CHAR;
++base_name;
}
else
MEMREASSIGN (base_name, canonical);
assert (base_name && *base_name);
ext = strrchr (base_name, '.');
if (!ext)
{
ext = base_name + LT_STRLEN (base_name);
}
/* extract the module name from the file name */
name = MALLOC (char, ext - base_name + 1);
if (!name)
{
++errors;
goto cleanup;
}
/* canonicalize the module name */
{
int i;
for (i = 0; i < ext - base_name; ++i)
{
if (isalnum ((unsigned char)(base_name[i])))
{
name[i] = base_name[i];
}
else
{
name[i] = '_';
}
}
name[ext - base_name] = LT_EOS_CHAR;
}
/* Before trawling through the filesystem in search of a module,
check whether we are opening a preloaded module. */
if (!dir)
{
const lt_dlvtable *vtable = lt_dlloader_find ("lt_preopen");
if (vtable)
{
/* libprefix + name + "." + libext + NULL */
archive_name = MALLOC (char, strlen (libprefix) + LT_STRLEN (name) + strlen (libext) + 2);
*phandle = (lt_dlhandle) lt__zalloc (sizeof (struct lt__handle));
if ((*phandle == NULL) || (archive_name == NULL))
{
++errors;
goto cleanup;
}
newhandle = *phandle;
/* Preloaded modules are always named according to their old
archive name. */
if (strncmp(name, "lib", 3) == 0)
{
sprintf (archive_name, "%s%s.%s", libprefix, name + 3, libext);
}
else
{
sprintf (archive_name, "%s.%s", name, libext);
}
if (tryall_dlopen (&newhandle, archive_name, advise, vtable) == 0)
{
goto register_handle;
}
/* If we're still here, there was no matching preloaded module,
so put things back as we found them, and continue searching. */
FREE (*phandle);
newhandle = NULL;
}
}
/* If we are allowing only preloaded modules, and we didn't find
anything yet, give up on the search here. */
if (advise && advise->try_preload_only)
{
goto cleanup;
}
/* Check whether we are opening a libtool module (.la extension). */
if (ext && streq (ext, archive_ext))
{
/* this seems to be a libtool module */
FILE * file = 0;
char * dlname = 0;
char * old_name = 0;
char * libdir = 0;
char * deplibs = 0;
/* if we can't find the installed flag, it is probably an
installed libtool archive, produced with an old version
of libtool */
int installed = 1;
/* Now try to open the .la file. If there is no directory name
component, try to find it first in user_search_path and then other
prescribed paths. Otherwise (or in any case if the module was not
yet found) try opening just the module name as passed. */
if (!dir)
{
const char *search_path = user_search_path;
if (search_path)
file = find_file (user_search_path, base_name, &dir);
if (!file)
{
search_path = getenv (LTDL_SEARCHPATH_VAR);
if (search_path)
file = find_file (search_path, base_name, &dir);
}
#if defined(LT_MODULE_PATH_VAR)
if (!file)
{
search_path = getenv (LT_MODULE_PATH_VAR);
if (search_path)
file = find_file (search_path, base_name, &dir);
}
#endif
#if defined(LT_DLSEARCH_PATH)
if (!file && *sys_dlsearch_path)
{
file = find_file (sys_dlsearch_path, base_name, &dir);
}
#endif
}
else
{
file = fopen (attempt, LT_READTEXT_MODE);
}
/* If we didn't find the file by now, it really isn't there. Set
the status flag, and bail out. */
if (!file)
{
LT__SETERROR (FILE_NOT_FOUND);
++errors;
goto cleanup;
}
/* read the .la file */
if (parse_dotla_file(file, &dlname, &libdir, &deplibs,
&old_name, &installed) != 0)
++errors;
fclose (file);
/* allocate the handle */
*phandle = (lt_dlhandle) lt__zalloc (sizeof (struct lt__handle));
if (*phandle == 0)
++errors;
if (errors)
{
FREE (dlname);
FREE (old_name);
FREE (libdir);
FREE (deplibs);
FREE (*phandle);
goto cleanup;
}
assert (*phandle);
if (load_deplibs (*phandle, deplibs) == 0)
{
newhandle = *phandle;
/* find_module may replace newhandle */
if (find_module (&newhandle, dir, libdir, dlname, old_name,
installed, advise))
{
unload_deplibs (*phandle);
++errors;
}
}
else
{
++errors;
}
FREE (dlname);
FREE (old_name);
FREE (libdir);
FREE (deplibs);
if (errors)
{
FREE (*phandle);
goto cleanup;
}
if (*phandle != newhandle)
{
unload_deplibs (*phandle);
}
}
else
{
/* not a libtool module */
*phandle = (lt_dlhandle) lt__zalloc (sizeof (struct lt__handle));
if (*phandle == 0)
{
++errors;
goto cleanup;
}
newhandle = *phandle;
/* If the module has no directory name component, try to find it
first in user_search_path and then other prescribed paths.
Otherwise (or in any case if the module was not yet found) try
opening just the module name as passed. */
if ((dir || (!find_handle (user_search_path, base_name,
&newhandle, advise)
&& !find_handle (getenv (LTDL_SEARCHPATH_VAR), base_name,
&newhandle, advise)
#if defined(LT_MODULE_PATH_VAR)
&& !find_handle (getenv (LT_MODULE_PATH_VAR), base_name,
&newhandle, advise)
#endif
#if defined(LT_DLSEARCH_PATH)
&& !find_handle (sys_dlsearch_path, base_name,
&newhandle, advise)
#endif
)))
{
if (tryall_dlopen (&newhandle, attempt, advise, 0) != 0)
{
newhandle = NULL;
}
}
if (!newhandle)
{
FREE (*phandle);
++errors;
goto cleanup;
}
}
register_handle:
MEMREASSIGN (*phandle, newhandle);
if ((*phandle)->info.ref_count == 0)
{
(*phandle)->info.ref_count = 1;
MEMREASSIGN ((*phandle)->info.name, name);
(*phandle)->next = handles;
handles = *phandle;
}
LT__SETERRORSTR (saved_error);
cleanup:
FREE (dir);
FREE (attempt);
FREE (name);
if (!canonical) /* was MEMREASSIGNed */
FREE (base_name);
FREE (canonical);
FREE (archive_name);
return errors;
}
/* If the last error message stored was `FILE_NOT_FOUND', then return
non-zero. */
static int
file_not_found (void)
{
const char *error = 0;
LT__GETERROR (error);
if (error == LT__STRERROR (FILE_NOT_FOUND))
return 1;
return 0;
}
/* Unless FILENAME already bears a suitable library extension, then
return 0. */
static int
has_library_ext (const char *filename)
{
const char * ext = 0;
assert (filename);
ext = strrchr (filename, '.');
if (ext && ((streq (ext, archive_ext))
#if defined(LT_MODULE_EXT)
|| (streq (ext, shlib_ext))
#endif
#if defined(LT_SHARED_EXT)
|| (streq (ext, shared_ext))
#endif
))
{
return 1;
}
return 0;
}
/* Initialise and configure a user lt_dladvise opaque object. */
int
lt_dladvise_init (lt_dladvise *padvise)
{
lt_dladvise advise = (lt_dladvise) lt__zalloc (sizeof (struct lt__advise));
*padvise = advise;
return (advise ? 0 : 1);
}
int
lt_dladvise_destroy (lt_dladvise *padvise)
{
if (padvise)
FREE(*padvise);
return 0;
}
int
lt_dladvise_ext (lt_dladvise *padvise)
{
assert (padvise && *padvise);
(*padvise)->try_ext = 1;
return 0;
}
int
lt_dladvise_resident (lt_dladvise *padvise)
{
assert (padvise && *padvise);
(*padvise)->is_resident = 1;
return 0;
}
int
lt_dladvise_local (lt_dladvise *padvise)
{
assert (padvise && *padvise);
(*padvise)->is_symlocal = 1;
return 0;
}
int
lt_dladvise_global (lt_dladvise *padvise)
{
assert (padvise && *padvise);
(*padvise)->is_symglobal = 1;
return 0;
}
int
lt_dladvise_preload (lt_dladvise *padvise)
{
assert (padvise && *padvise);
(*padvise)->try_preload_only = 1;
return 0;
}
/* Libtool-1.5.x interface for loading a new module named FILENAME. */
lt_dlhandle
lt_dlopen (const char *filename)
{
return lt_dlopenadvise (filename, NULL);
}
/* If FILENAME has an ARCHIVE_EXT or MODULE_EXT extension, try to
open the FILENAME as passed. Otherwise try appending ARCHIVE_EXT,
and if a file is still not found try again with MODULE_EXT appended
instead. */
lt_dlhandle
lt_dlopenext (const char *filename)
{
lt_dlhandle handle = 0;
lt_dladvise advise;
if (!lt_dladvise_init (&advise) && !lt_dladvise_ext (&advise))
handle = lt_dlopenadvise (filename, advise);
lt_dladvise_destroy (&advise);
return handle;
}
lt_dlhandle
lt_dlopenadvise (const char *filename, lt_dladvise advise)
{
lt_dlhandle handle = 0;
int errors = 0;
const char * saved_error = 0;
LT__GETERROR (saved_error);
/* Can't have symbols hidden and visible at the same time! */
if (advise && advise->is_symlocal && advise->is_symglobal)
{
LT__SETERROR (CONFLICTING_FLAGS);
return 0;
}
if (!filename
|| !advise
|| !advise->try_ext
|| has_library_ext (filename))
{
/* Just incase we missed a code path in try_dlopen() that reports
an error, but forgot to reset handle... */
if (try_dlopen (&handle, filename, NULL, advise) != 0)
return 0;
return handle;
}
else if (filename && *filename)
{
/* First try appending ARCHIVE_EXT. */
errors += try_dlopen (&handle, filename, archive_ext, advise);
/* If we found FILENAME, stop searching -- whether we were able to
load the file as a module or not. If the file exists but loading
failed, it is better to return an error message here than to
report FILE_NOT_FOUND when the alternatives (foo.so etc) are not
in the module search path. */
if (handle || ((errors > 0) && !file_not_found ()))
return handle;
#if defined(LT_MODULE_EXT)
/* Try appending SHLIB_EXT. */
LT__SETERRORSTR (saved_error);
errors = try_dlopen (&handle, filename, shlib_ext, advise);
/* As before, if the file was found but loading failed, return now
with the current error message. */
if (handle || ((errors > 0) && !file_not_found ()))
return handle;
#endif
#if defined(LT_SHARED_EXT)
/* Try appending SHARED_EXT. */
LT__SETERRORSTR (saved_error);
errors = try_dlopen (&handle, filename, shared_ext, advise);
/* As before, if the file was found but loading failed, return now
with the current error message. */
if (handle || ((errors > 0) && !file_not_found ()))
return handle;
#endif
}
/* Still here? Then we really did fail to locate any of the file
names we tried. */
LT__SETERROR (FILE_NOT_FOUND);
return 0;
}
static int
lt_argz_insert (char **pargz, size_t *pargz_len, char *before,
const char *entry)
{
error_t error;
/* Prior to Sep 8, 2005, newlib had a bug where argz_insert(pargz,
pargz_len, NULL, entry) failed with EINVAL. */
if (before)
error = argz_insert (pargz, pargz_len, before, entry);
else
error = argz_append (pargz, pargz_len, entry, 1 + strlen (entry));
if (error)
{
switch (error)
{
case ENOMEM:
LT__SETERROR (NO_MEMORY);
break;
default:
LT__SETERROR (UNKNOWN);
break;
}
return 1;
}
return 0;
}
static int
lt_argz_insertinorder (char **pargz, size_t *pargz_len, const char *entry)
{
char *before = 0;
assert (pargz);
assert (pargz_len);
assert (entry && *entry);
if (*pargz)
while ((before = argz_next (*pargz, *pargz_len, before)))
{
int cmp = strcmp (entry, before);
if (cmp < 0) break;
if (cmp == 0) return 0; /* No duplicates! */
}
return lt_argz_insert (pargz, pargz_len, before, entry);
}
static int
lt_argz_insertdir (char **pargz, size_t *pargz_len, const char *dirnam,
struct dirent *dp)
{
char *buf = 0;
size_t buf_len = 0;
char *end = 0;
size_t end_offset = 0;
size_t dir_len = 0;
int errors = 0;
assert (pargz);
assert (pargz_len);
assert (dp);
dir_len = LT_STRLEN (dirnam);
end = dp->d_name + D_NAMLEN(dp);
/* Ignore version numbers. */
{
char *p;
for (p = end; p -1 > dp->d_name; --p)
if (strchr (".0123456789", p[-1]) == 0)
break;
if (*p == '.')
end = p;
}
/* Ignore filename extension. */
{
char *p;
for (p = end -1; p > dp->d_name; --p)
if (*p == '.')
{
end = p;
break;
}
}
/* Prepend the directory name. */
end_offset = end - dp->d_name;
buf_len = dir_len + 1+ end_offset;
buf = MALLOC (char, 1+ buf_len);
if (!buf)
return ++errors;
assert (buf);
strcpy (buf, dirnam);
strcat (buf, "/");
strncat (buf, dp->d_name, end_offset);
buf[buf_len] = LT_EOS_CHAR;
/* Try to insert (in order) into ARGZ/ARGZ_LEN. */
if (lt_argz_insertinorder (pargz, pargz_len, buf) != 0)
++errors;
FREE (buf);
return errors;
}
static int
list_files_by_dir (const char *dirnam, char **pargz, size_t *pargz_len)
{
DIR *dirp = 0;
int errors = 0;
assert (dirnam && *dirnam);
assert (pargz);
assert (pargz_len);
assert (dirnam[LT_STRLEN(dirnam) -1] != '/');
dirp = opendir (dirnam);
if (dirp)
{
struct dirent *dp = 0;
while ((dp = readdir (dirp)))
if (dp->d_name[0] != '.')
if (lt_argz_insertdir (pargz, pargz_len, dirnam, dp))
{
++errors;
break;
}
closedir (dirp);
}
else
++errors;
return errors;
}
/* If there are any files in DIRNAME, call the function passed in
DATA1 (with the name of each file and DATA2 as arguments). */
static int
foreachfile_callback (char *dirname, void *data1, void *data2)
{
file_worker_func *func = *(file_worker_func **) data1;
int is_done = 0;
char *argz = 0;
size_t argz_len = 0;
if (list_files_by_dir (dirname, &argz, &argz_len) != 0)
goto cleanup;
if (!argz)
goto cleanup;
{
char *filename = 0;
while ((filename = argz_next (argz, argz_len, filename)))
if ((is_done = (*func) (filename, data2)))
break;
}
cleanup:
FREE (argz);
return is_done;
}
/* Call FUNC for each unique extensionless file in SEARCH_PATH, along
with DATA. The filenames passed to FUNC would be suitable for
passing to lt_dlopenext. The extensions are stripped so that
individual modules do not generate several entries (e.g. libfoo.la,
libfoo.so, libfoo.so.1, libfoo.so.1.0.0). If SEARCH_PATH is NULL,
then the same directories that lt_dlopen would search are examined. */
int
lt_dlforeachfile (const char *search_path,
int (*func) (const char *filename, void *data),
void *data)
{
int is_done = 0;
file_worker_func **fpptr = &func;
if (search_path)
{
/* If a specific path was passed, search only the directories
listed in it. */
is_done = foreach_dirinpath (search_path, 0,
foreachfile_callback, fpptr, data);
}
else
{
/* Otherwise search the default paths. */
is_done = foreach_dirinpath (user_search_path, 0,
foreachfile_callback, fpptr, data);
if (!is_done)
{
is_done = foreach_dirinpath (getenv(LTDL_SEARCHPATH_VAR), 0,
foreachfile_callback, fpptr, data);
}
#if defined(LT_MODULE_PATH_VAR)
if (!is_done)
{
is_done = foreach_dirinpath (getenv(LT_MODULE_PATH_VAR), 0,
foreachfile_callback, fpptr, data);
}
#endif
#if defined(LT_DLSEARCH_PATH)
if (!is_done && *sys_dlsearch_path)
{
is_done = foreach_dirinpath (sys_dlsearch_path, 0,
foreachfile_callback, fpptr, data);
}
#endif
}
return is_done;
}
int
lt_dlclose (lt_dlhandle handle)
{
lt_dlhandle cur, last;
int errors = 0;
/* check whether the handle is valid */
last = cur = handles;
while (cur && handle != cur)
{
last = cur;
cur = cur->next;
}
if (!cur)
{
LT__SETERROR (INVALID_HANDLE);
++errors;
goto done;
}
cur = handle;
cur->info.ref_count--;
/* Note that even with resident modules, we must track the ref_count
correctly incase the user decides to reset the residency flag
later (even though the API makes no provision for that at the
moment). */
if (cur->info.ref_count <= 0 && !LT_DLIS_RESIDENT (cur))
{
lt_user_data data = cur->vtable->dlloader_data;
if (cur != handles)
{
last->next = cur->next;
}
else
{
handles = cur->next;
}
errors += cur->vtable->module_close (data, cur->module);
errors += unload_deplibs (handle);
/* It is up to the callers to free the data itself. */
FREE (cur->interface_data);
FREE (cur->info.filename);
FREE (cur->info.name);
FREE (cur);
goto done;
}
if (LT_DLIS_RESIDENT (handle))
{
LT__SETERROR (CLOSE_RESIDENT_MODULE);
++errors;
}
done:
return errors;
}
void *
lt_dlsym (lt_dlhandle place, const char *symbol)
{
size_t lensym;
char lsym[LT_SYMBOL_LENGTH];
char *sym;
void *address;
lt_user_data data;
lt_dlhandle handle;
if (!place)
{
LT__SETERROR (INVALID_HANDLE);
return 0;
}
handle = place;
if (!symbol)
{
LT__SETERROR (SYMBOL_NOT_FOUND);
return 0;
}
lensym = LT_STRLEN (symbol) + LT_STRLEN (handle->vtable->sym_prefix)
+ LT_STRLEN (handle->info.name);
if (lensym + LT_SYMBOL_OVERHEAD < LT_SYMBOL_LENGTH)
{
sym = lsym;
}
else
{
sym = MALLOC (char, lensym + LT_SYMBOL_OVERHEAD + 1);
if (!sym)
{
LT__SETERROR (BUFFER_OVERFLOW);
return 0;
}
}
data = handle->vtable->dlloader_data;
if (handle->info.name)
{
const char *saved_error;
LT__GETERROR (saved_error);
/* this is a libtool module */
if (handle->vtable->sym_prefix)
{
strcpy(sym, handle->vtable->sym_prefix);
strcat(sym, handle->info.name);
}
else
{
strcpy(sym, handle->info.name);
}
strcat(sym, "_LTX_");
strcat(sym, symbol);
/* try "modulename_LTX_symbol" */
address = handle->vtable->find_sym (data, handle->module, sym);
if (address)
{
if (sym != lsym)
{
FREE (sym);
}
return address;
}
LT__SETERRORSTR (saved_error);
}
/* otherwise try "symbol" */
if (handle->vtable->sym_prefix)
{
strcpy(sym, handle->vtable->sym_prefix);
strcat(sym, symbol);
}
else
{
strcpy(sym, symbol);
}
address = handle->vtable->find_sym (data, handle->module, sym);
if (sym != lsym)
{
FREE (sym);
}
return address;
}
const char *
lt_dlerror (void)
{
const char *error;
LT__GETERROR (error);
LT__SETERRORSTR (0);
return error;
}
static int
lt_dlpath_insertdir (char **ppath, char *before, const char *dir)
{
int errors = 0;
char *canonical = 0;
char *argz = 0;
size_t argz_len = 0;
assert (ppath);
assert (dir && *dir);
if (canonicalize_path (dir, &canonical) != 0)
{
++errors;
goto cleanup;
}
assert (canonical && *canonical);
/* If *PPATH is empty, set it to DIR. */
if (*ppath == 0)
{
assert (!before); /* BEFORE cannot be set without PPATH. */
assert (dir); /* Without DIR, don't call this function! */
*ppath = lt__strdup (dir);
if (*ppath == 0)
++errors;
goto cleanup;
}
assert (ppath && *ppath);
if (argzize_path (*ppath, &argz, &argz_len) != 0)
{
++errors;
goto cleanup;
}
/* Convert BEFORE into an equivalent offset into ARGZ. This only works
if *PPATH is already canonicalized, and hence does not change length
with respect to ARGZ. We canonicalize each entry as it is added to
the search path, and don't call this function with (uncanonicalized)
user paths, so this is a fair assumption. */
if (before)
{
assert (*ppath <= before);
assert ((int) (before - *ppath) <= (int) strlen (*ppath));
before = before - *ppath + argz;
}
if (lt_argz_insert (&argz, &argz_len, before, dir) != 0)
{
++errors;
goto cleanup;
}
argz_stringify (argz, argz_len, LT_PATHSEP_CHAR);
MEMREASSIGN(*ppath, argz);
cleanup:
FREE (argz);
FREE (canonical);
return errors;
}
int
lt_dladdsearchdir (const char *search_dir)
{
int errors = 0;
if (search_dir && *search_dir)
{
if (lt_dlpath_insertdir (&user_search_path, 0, search_dir) != 0)
++errors;
}
return errors;
}
int
lt_dlinsertsearchdir (const char *before, const char *search_dir)
{
int errors = 0;
if (before)
{
if ((before < user_search_path)
|| (before >= user_search_path + LT_STRLEN (user_search_path)))
{
LT__SETERROR (INVALID_POSITION);
return 1;
}
}
if (search_dir && *search_dir)
{
if (lt_dlpath_insertdir (&user_search_path,
(char *) before, search_dir) != 0)
{
++errors;
}
}
return errors;
}
int
lt_dlsetsearchpath (const char *search_path)
{
int errors = 0;
FREE (user_search_path);
if (!search_path || !LT_STRLEN (search_path))
{
return errors;
}
if (canonicalize_path (search_path, &user_search_path) != 0)
++errors;
return errors;
}
const char *
lt_dlgetsearchpath (void)
{
const char *saved_path;
saved_path = user_search_path;
return saved_path;
}
int
lt_dlmakeresident (lt_dlhandle handle)
{
int errors = 0;
if (!handle)
{
LT__SETERROR (INVALID_HANDLE);
++errors;
}
else
{
handle->info.is_resident = 1;
}
return errors;
}
int
lt_dlisresident (lt_dlhandle handle)
{
if (!handle)
{
LT__SETERROR (INVALID_HANDLE);
return -1;
}
return LT_DLIS_RESIDENT (handle);
}
/* --- MODULE INFORMATION --- */
typedef struct {
const char *id_string;
lt_dlhandle_interface *iface;
} lt__interface_id;
lt_dlinterface_id
lt_dlinterface_register (const char *id_string, lt_dlhandle_interface *iface)
{
lt__interface_id *interface_id = (lt__interface_id *) lt__malloc (sizeof *interface_id);
/* If lt__malloc fails, it will LT__SETERROR (NO_MEMORY), which
can then be detected with lt_dlerror() if we return 0. */
if (interface_id)
{
interface_id->id_string = lt__strdup (id_string);
if (!interface_id->id_string)
FREE (interface_id);
else
interface_id->iface = iface;
}
return (lt_dlinterface_id) interface_id;
}
void lt_dlinterface_free (lt_dlinterface_id key)
{
lt__interface_id *interface_id = (lt__interface_id *)key;
FREE (interface_id->id_string);
FREE (interface_id);
}
void *
lt_dlcaller_set_data (lt_dlinterface_id key, lt_dlhandle handle, void *data)
{
int n_elements = 0;
void *stale = (void *) 0;
lt_dlhandle cur = handle;
int i;
if (cur->interface_data)
while (cur->interface_data[n_elements].key)
++n_elements;
for (i = 0; i < n_elements; ++i)
{
if (cur->interface_data[i].key == key)
{
stale = cur->interface_data[i].data;
break;
}
}
/* Ensure that there is enough room in this handle's interface_data
array to accept a new element (and an empty end marker). */
if (i == n_elements)
{
lt_interface_data *temp
= REALLOC (lt_interface_data, cur->interface_data, 2+ n_elements);
if (!temp)
{
stale = 0;
goto done;
}
cur->interface_data = temp;
/* We only need this if we needed to allocate a new interface_data. */
cur->interface_data[i].key = key;
cur->interface_data[1+ i].key = 0;
}
cur->interface_data[i].data = data;
done:
return stale;
}
void *
lt_dlcaller_get_data (lt_dlinterface_id key, lt_dlhandle handle)
{
void *result = (void *) 0;
lt_dlhandle cur = handle;
/* Locate the index of the element with a matching KEY. */
if (cur->interface_data)
{
int i;
for (i = 0; cur->interface_data[i].key; ++i)
{
if (cur->interface_data[i].key == key)
{
result = cur->interface_data[i].data;
break;
}
}
}
return result;
}
const lt_dlinfo *
lt_dlgetinfo (lt_dlhandle handle)
{
if (!handle)
{
LT__SETERROR (INVALID_HANDLE);
return 0;
}
return &(handle->info);
}
lt_dlhandle
lt_dlhandle_iterate (lt_dlinterface_id iface, lt_dlhandle place)
{
lt_dlhandle handle = place;
lt__interface_id *iterator = (lt__interface_id *) iface;
assert (iface); /* iface is a required argument */
if (!handle)
handle = handles;
else
handle = handle->next;
/* advance while the interface check fails */
while (handle && iterator->iface
&& ((*iterator->iface) (handle, iterator->id_string) != 0))
{
handle = handle->next;
}
return handle;
}
lt_dlhandle
lt_dlhandle_fetch (lt_dlinterface_id iface, const char *module_name)
{
lt_dlhandle handle = 0;
assert (iface); /* iface is a required argument */
while ((handle = lt_dlhandle_iterate (iface, handle)))
{
lt_dlhandle cur = handle;
if (cur && cur->info.name && streq (cur->info.name, module_name))
break;
}
return handle;
}
int
lt_dlhandle_map (lt_dlinterface_id iface,
int (*func) (lt_dlhandle handle, void *data), void *data)
{
lt__interface_id *iterator = (lt__interface_id *) iface;
lt_dlhandle cur = handles;
assert (iface); /* iface is a required argument */
while (cur)
{
int errorcode = 0;
/* advance while the interface check fails */
while (cur && iterator->iface
&& ((*iterator->iface) (cur, iterator->id_string) != 0))
{
cur = cur->next;
}
if ((errorcode = (*func) (cur, data)) != 0)
return errorcode;
}
return 0;
}