[PATCH 01/12] Support loading shared library archive members.

Michael Haubenwallner <[email protected]>
Newsgroups gmane.comp.gnu.libtool.patches
Message-ID <1349192093-24173-2-git-send-email-michael.haubenwallner@salomon.at>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.