AIX supports shared objects as members of archive libraries, loadable with dlopen using the RTLD_MEMBER flag. * libltdl/ltdl.c: For AIX, define LT_SHARED_LIB_MEMBER to hold the parentheses used to identify the shared archive member. (lt_dlopenadvise) When LT_SHARED_LIB_MEMBER is defined, allow for specifying an archive member as the to-be-loaded shared object. * libltdl/loaders/dlopen.c: (vm_open) Use RTLD_MEMBER flag for dlopen when the filename does specify an archive member between "()". --- libltdl/loaders/dlopen.c | 18 ++++ libltdl/ltdl.c | 202 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 178 insertions(+), 42 deletions(-)
diff --git a/libltdl/loaders/dlopen.c b/libltdl/loaders/dlopen.c index e8180e1..a71f5e0 100644 --- a/libltdl/loaders/dlopen.c +++ b/libltdl/loaders/dlopen.c @@ -168,6 +168,9 @@ vm_open (lt_user_data LT__UNUSED loader_data, const char *filename, { int module_flags = LT_LAZY_OR_NOW; lt_module module; +#ifdef RTLD_MEMBER + int len = LT_STRLEN (filename); +#endif if (advise) { @@ -191,6 +194,21 @@ vm_open (lt_user_data LT__UNUSED loader_data, const char *filename, #endif } +#ifdef RTLD_MEMBER /* AIX */ + if (len) + { + /* Advise loading an archive member only if the filename + really contains both the opening and closing parent. */ + const char *closing = strrchr(filename ? filename : "", ')'); + if (closing && STREQ(closing, ")")) + { + const char *opening = strrchr(filename, '('); + if (opening && strchr(opening, '/') == NULL) + module_flags |= RTLD_MEMBER; + } + } +#endif + module = dlopen (filename, module_flags); if (!module) diff --git a/libltdl/ltdl.c b/libltdl/ltdl.c index 0dea0c8..c958d79 100644 --- a/libltdl/ltdl.c +++ b/libltdl/ltdl.c @@ -58,6 +58,12 @@ or obtained by writing to the Free Software Foundation, Inc., # define LT_LIBPREFIX "lib" #endif +/* how shared archive members are specified, seen on AIX only */ +#undef LT_SHARED_LIB_MEMBER +#if defined _AIX +# define LT_SHARED_LIB_MEMBER "()" /* need the parentheses only */ +#endif + /* This is the maximum symbol size that won't require malloc/free */ #undef LT_SYMBOL_LENGTH #define LT_SYMBOL_LENGTH 128 @@ -77,6 +83,9 @@ 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_SHARED_LIB_MEMBER +static const char shared_lib_member[] = LT_SHARED_LIB_MEMBER; +#endif #if defined LT_MODULE_EXT static const char shlib_ext[] = LT_MODULE_EXT; #endif @@ -127,7 +136,6 @@ 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, @@ -582,6 +590,97 @@ find_module (lt_dlhandle *handle, const char *dir, const char *libdir, return 1; } +/* Identify extension and optional shared archive member of a library FILENAME. + Sets *PMEMBER to the member specification found, or end of FILENAME. + Sets *PEXT to the extension found, or to *PMEMBER - may be end of FILENAME. + When FILENAME is NULL, *PEXT and *PMEMBER are set to NULL. + With ANY_EXT, FILENAME must not containy any path, and '.' is used to + search for the extension. */ +static void +identify_library_ext_member(const char *filename, + const char **pext, const char **pmember, int any_ext) +{ + size_t len; + size_t extlen; + + assert (pext); + assert (pmember); + + len = LT_STRLEN (filename); /* enough to work with filename == NULL */ + + *pmember = filename + len; /* member specification is identified later */ + + extlen = LT_STRLEN(archive_ext); + if (len > extlen && STREQ (filename + len - extlen, archive_ext)) + { + /* with libtool archive, member specification is unsupported */ + *pext = filename + len - extlen; + return; + } + +#if defined LT_SHARED_LIB_MEMBER + /* Take first and last char of shared_lib_member as the parentheses, + and ensure there is something in between the parentheses. */ + extlen = LT_STRLEN(shared_lib_member); + if (len > 3 && extlen >= 2 + && filename[len-1] == shared_lib_member[extlen-1]) /* closing */ + { + const char *member; + member = strrchr(filename, shared_lib_member[0]); /* opening */ + if (member && member <= filename + len - 3) /* something in between */ + { + *pmember = member; + len = member - filename; /* Ignore member for the extension search. */ + } + } +#endif + +#if defined LT_MODULE_EXT + extlen = LT_STRLEN(shlib_ext); + if (len > extlen && strncmp(filename + len - extlen, shlib_ext, extlen) == 0) + { + *pext = filename + len - extlen; + return; + } +#endif +#if defined LT_SHARED_EXT + extlen = LT_STRLEN(shared_ext); + if (len > extlen && strncmp(filename + len - extlen, shared_ext, extlen) == 0) + { + *pext = filename + len - extlen; + return; + } +#endif + +#if defined LT_SHARED_LIB_MEMBER + /* AIX allows for shared objects in libNAME.a archive too */ + extlen = LT_STRLEN(libext); /* libext lacks leading '.' */ + if (len > (extlen + 1) + && filename[len - extlen - 1] == '.' + && strncmp(filename + len - extlen, libext, extlen) == 0) + { + *pext = filename + len - extlen - 1; + return; + } +#endif + + /* maybe an archive member, but no extension found so far */ + *pext = *pmember; + + if (len > 1 && any_ext) { + /* search for last '.' before member, */ + const char *ext = filename; + while((ext = strchr(ext, '.')) != NULL) + { + if (ext >= *pmember) /* found '.' in the member name */ + break; + *pext = ext; + ++ext; + } + } + + return; +} static int canonicalize_path (const char *path, char **pcanonical) @@ -782,9 +881,27 @@ static int find_handle_callback (char *filename, void *data, void *data2) { lt_dlhandle *phandle = (lt_dlhandle *) data; - int notfound = access (filename, R_OK); + int notfound; lt_dladvise advise = (lt_dladvise) data2; +#if defined LT_SHARED_LIB_MEMBER + const char *ext; + const char *member; + identify_library_ext_member(filename, &ext, &member, 0); + /* Omit the shared archive member name, which is something like + "(shr.o)" on AIX, when checking if the file is access-ible. + But there must be some filename before the member. */ + if (*member && member > filename) + { + /* cut off member for filesystem check */ + filename[member - filename] = LT_EOS_CHAR; + notfound = access (filename, R_OK); + /* restore member for dlopen */ + filename[member - filename] = shared_lib_member[0]; + } + else +#endif + notfound = access (filename, R_OK); /* Bail out if file cannot be read... */ if (notfound) return 0; @@ -1168,6 +1285,7 @@ try_dlopen (lt_dlhandle *phandle, const char *filename, const char *ext, char * attempt = 0; int errors = 0; lt_dlhandle newhandle; + const char * member; assert (phandle); assert (*phandle == 0); @@ -1205,11 +1323,22 @@ try_dlopen (lt_dlhandle *phandle, const char *filename, const char *ext, if (ext) { - attempt = MALLOC (char, LT_STRLEN (filename) + LT_STRLEN (ext) + 1); + const char *tmpext; + int filenamelen, extlen; + + /* Use ext and/or member when specified as argument. */ + + identify_library_ext_member(filename, &tmpext, &member, 0); + + filenamelen = tmpext - filename; + extlen = ext ? strlen (ext) : (member - tmpext); + + attempt = MALLOC (char, filenamelen + extlen + strlen (member) + 1); if (!attempt) return 1; - sprintf(attempt, "%s%s", filename, ext); + sprintf(attempt, "%.*s%.*s%s", filenamelen, filename, + extlen, ext ? ext : tmpext, member); } else { @@ -1250,14 +1379,10 @@ try_dlopen (lt_dlhandle *phandle, const char *filename, const char *ext, assert (base_name && *base_name); - ext = strrchr (base_name, '.'); - if (!ext) - { - ext = base_name + LT_STRLEN (base_name); - } + identify_library_ext_member(base_name, &ext, &member, 1); /* any extension */ /* extract the module name from the file name */ - name = MALLOC (char, ext - base_name + 1); + name = MALLOC (char, ext - base_name + strlen (member) + 1); if (!name) { ++errors; @@ -1266,7 +1391,7 @@ try_dlopen (lt_dlhandle *phandle, const char *filename, const char *ext, /* canonicalize the module name */ { - int i; + int i, j; for (i = 0; i < ext - base_name; ++i) { if (isalnum ((unsigned char)(base_name[i]))) @@ -1278,7 +1403,20 @@ try_dlopen (lt_dlhandle *phandle, const char *filename, const char *ext, name[i] = '_'; } } - name[ext - base_name] = LT_EOS_CHAR; + /* Need member name for unique identification of shared objects. + NOTE: dlpreopen is static only, without a shared member. */ + for (j = 0; member[j] != LT_EOS_CHAR; ++j, ++i) + { + if (isalnum ((unsigned char)(member[j]))) + { + name[i] = member[j]; + } + else + { + name[i] = '_'; + } + } + name[i] = LT_EOS_CHAR; } /* Before trawling through the file system in search of a module, @@ -1330,8 +1468,10 @@ try_dlopen (lt_dlhandle *phandle, const char *filename, const char *ext, goto cleanup; } - /* Check whether we are opening a libtool module (.la extension). */ - if (ext && STREQ (ext, archive_ext)) + /* Check whether we are opening a libtool module (.la extension). + An archive member is not allowed this way: STREQ filters that, + as the member immediately follows the extension in base_name. */ + if (STREQ (ext, archive_ext)) { /* this seems to be a libtool module */ FILE * file = 0; @@ -1534,33 +1674,6 @@ file_not_found (void) } -/* 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 @@ -1651,6 +1764,8 @@ lt_dlopenadvise (const char *filename, lt_dladvise advise) lt_dlhandle handle = 0; int errors = 0; const char * saved_error = 0; + const char * ext; + const char * member; LT__GETERROR (saved_error); @@ -1661,10 +1776,13 @@ lt_dlopenadvise (const char *filename, lt_dladvise advise) return 0; } + identify_library_ext_member(filename, &ext, &member, 0); + if (!filename || !advise || !advise->try_ext - || has_library_ext (filename)) + || member > ext /* filename has a known extension */ + ) { /* Just incase we missed a code path in try_dlopen() that reports an error, but forgot to reset handle... */ -- 1.7.3.4