changeset 3787:321c4f513519

6527318 dlopen(name, RTLD_NOLOAD) returns handle for unloaded library
author rie
date Fri, 09 Mar 2007 09:44:28 -0800
parents 59f947543bd9
children 9947e7abaaca
files usr/src/cmd/sgs/include/rtld.h usr/src/cmd/sgs/librtld/common/dynamic.c usr/src/cmd/sgs/packages/common/SUNWonld-README usr/src/cmd/sgs/rtld/common/analyze.c usr/src/cmd/sgs/rtld/common/dlfcns.c
diffstat 5 files changed, 109 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/sgs/include/rtld.h	Fri Mar 09 08:35:59 2007 -0800
+++ b/usr/src/cmd/sgs/include/rtld.h	Fri Mar 09 09:44:28 2007 -0800
@@ -833,7 +833,7 @@
 
 extern Pltbindtype	elf_plt_write(uintptr_t, uintptr_t, void *, uintptr_t,
 			    Xword);
-extern Rt_map		*is_so_loaded(Lm_list *, const char *, int);
+extern Rt_map		*is_so_loaded(Lm_list *, const char *);
 extern Sym		*lookup_sym(Slookup *, Rt_map **, uint_t *);
 extern int		rt_dldump(Rt_map *, const char *, int, Addr);
 
--- a/usr/src/cmd/sgs/librtld/common/dynamic.c	Fri Mar 09 08:35:59 2007 -0800
+++ b/usr/src/cmd/sgs/librtld/common/dynamic.c	Fri Mar 09 09:44:28 2007 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Update any dynamic entry offsets.  One issue with dynamic entries is that
@@ -71,8 +71,8 @@
 				 * to, undo any lazy-loading position flag.
 				 */
 				if (dlmp = is_so_loaded(LIST(lmp),
-				    (strs + dyn->d_un.d_val), 1)) {
-					Bnd_desc **	bdpp;
+				    (strs + dyn->d_un.d_val))) {
+					Bnd_desc	**bdpp;
 					Aliste		off;
 
 					for (ALIST_TRAVERSE(DEPENDS(lmp), off,
--- a/usr/src/cmd/sgs/packages/common/SUNWonld-README	Fri Mar 09 08:35:59 2007 -0800
+++ b/usr/src/cmd/sgs/packages/common/SUNWonld-README	Fri Mar 09 09:44:28 2007 -0800
@@ -1196,3 +1196,4 @@
 6524709 executables using /usr/lib/libc.so.1 as the ELF interpreter dump core
 	(link-editor components only)
 6531189 SEGV in dladdr()
+6527318 dlopen(name, RTLD_NOLOAD) returns handle for unloaded library
--- a/usr/src/cmd/sgs/rtld/common/analyze.c	Fri Mar 09 08:35:59 2007 -0800
+++ b/usr/src/cmd/sgs/rtld/common/analyze.c	Fri Mar 09 09:44:28 2007 -0800
@@ -700,17 +700,16 @@
 	return (0);
 }
 
-
 /*
- * Function that determines whether a file name has already been loaded; if so,
- * returns a pointer to its link map structure; else returns a NULL pointer.
+ * Helper routine for is_so_matched() that consolidates matching a path name,
+ * or file name component of a link-map name.
  */
 static int
-_is_so_matched(const char *name, const char *str, int base)
+_is_so_matched(const char *name, const char *str, int path)
 {
 	const char	*_str;
 
-	if (base && ((_str = strrchr(str, '/')) != NULL))
+	if ((path == 0) && ((_str = strrchr(str, '/')) != NULL))
 		_str++;
 	else
 		_str = str;
@@ -718,64 +717,137 @@
 	return (strcmp(name, _str));
 }
 
+/*
+ * Determine whether a search name matches one of the names associated with a
+ * link-map.  A link-map contains several names:
+ *
+ *  .	a NAME() - typically the full pathname of an object that has been
+ *	loaded.  For example, when looking for the dependency "libc.so.1", a
+ * 	search path is applied, with the eventual NAME() being "/lib/ld.so.1".
+ *	The name of the executable is typically a simple filename, such as
+ *	"main", as this is the name passed to exec() to start the process.
+ *
+ *  .	a PATHNAME() - this is maintained if the resolved NAME() is different
+ * 	to NAME(), ie. the original name is a symbolic link.  This is also
+ * 	the resolved full pathname for a dynamic executable.
+ *
+ *  .	a list of ALIAS() names - these are alternative names by which the
+ *	object has been found, ie. when dependencies are loaded through a
+ * 	variety of different symbolic links.
+ *
+ * The name pattern matching can differ depending on whether we are looking
+ * for a full path name (path != 0), or a simple file name (path == 0).  Full
+ * path names typically match NAME() or PATHNAME() entries, so these link-map
+ * names are inspected first when a full path name is being searched for.
+ * Simple file names typically match ALIAS() names, so these link-map names are
+ * inspected first when a simple file name is being searched for.
+ *
+ * For all full path name searches, the link-map names are taken as is.  For
+ * simple file name searches, only the file name component of any link-map
+ * names are used for comparison.
+ */
 static Rt_map *
-is_so_matched(Rt_map *lmp, const char *name, int base)
+is_so_matched(Rt_map *lmp, const char *name, int path)
 {
 	Aliste		off;
 	const char	**cpp;
 
 	/*
+	 * A pathname is typically going to match a NAME() or PATHNAME(), so
+	 * check these first.
+	 */
+	if (path) {
+		if (strcmp(name, NAME(lmp)) == 0)
+			return (lmp);
+
+		if (PATHNAME(lmp) != NAME(lmp)) {
+			if (strcmp(name, PATHNAME(lmp)) == 0)
+				return (lmp);
+		}
+	}
+
+	/*
 	 * Typically, dependencies are specified as simple file names
 	 * (DT_NEEDED == libc.so.1), which are expanded to full pathnames to
 	 * open the file.  The full pathname is NAME(), and the original name
-	 * is maintained on the ALIAS() list. Look through the ALIAS list first,
-	 * as this is most likely to match other dependency uses.
+	 * is maintained on the ALIAS() list.
+	 *
+	 * If this is a simple filename, or a pathname has failed to match the
+	 * NAME() and PATHNAME() check above, look through the ALIAS() list.
 	 */
 	for (ALIST_TRAVERSE(ALIAS(lmp), off, cpp)) {
-		if (_is_so_matched(name, *cpp, base) == 0)
+		/*
+		 * If we're looking for a simple filename, _is_so_matched()
+		 * will reduce the ALIAS name to its simple name.
+		 */
+		if (_is_so_matched(name, *cpp, path) == 0)
 			return (lmp);
 	}
 
 	/*
-	 * Finally compare full paths, this is sometimes useful for catching
-	 * filter names, or for those that dlopen() the dynamic executable.
+	 * Finally, if this is a simple file name, and any ALIAS() search has
+	 * been completed, match the simple file name of NAME() and PATHNAME().
 	 */
-	if (_is_so_matched(name, NAME(lmp), base) == 0)
-		return (lmp);
+	if (path == 0) {
+		if (_is_so_matched(name, NAME(lmp), 0) == 0)
+			return (lmp);
 
-	if (PATHNAME(lmp) != NAME(lmp)) {
-		if (_is_so_matched(name, PATHNAME(lmp), base) == 0)
-			return (lmp);
+		if (PATHNAME(lmp) != NAME(lmp)) {
+			if (_is_so_matched(name, PATHNAME(lmp), 0) == 0)
+				return (lmp);
+		}
 	}
+
 	return (0);
 }
 
+/*
+ * Files are opened by ld.so.1 to satisfy dependencies, filtees and dlopen()
+ * requests.  Each request investigates the file based upon the callers
+ * environment, and once a full path name has been established a check is made
+ * against the FullpathNode AVL tree and a device/inode check, to ensure the
+ * same file isn't mapped multiple times.  See file_open().
+ *
+ * However, there are one of two cases where a test for an existing file name
+ * needs to be carried out, such as dlopen(NOLOAD) requests, dldump() requests,
+ * and as a final fallback to dependency loading.  These requests are handled
+ * by is_so_loaded().
+ *
+ * A traversal through the callers link-map list is carried out, and from each
+ * link-map, a comparison is made against all of the various names by which the
+ * object has been referenced.  The subroutine, is_so_matched() compares the
+ * link-map names against the name being searched for.  Whether the search name
+ * is a full path name or a simple file name, governs what comparisons are made.
+ *
+ * A full path name, which is a fully resolved path name that starts with a "/"
+ * character, or a relative path name that includes a "/" character, must match
+ * the link-map names explicitly.  A simple file name, which is any name *not*
+ * containing a "/" character, are matched against the file name component of
+ * any link-map names.
+ */
 Rt_map *
-is_so_loaded(Lm_list *lml, const char *name, int base)
+is_so_loaded(Lm_list *lml, const char *name)
 {
 	Rt_map		*lmp;
-	const char	*_name;
 	avl_index_t	where;
 	Lm_cntl		*lmc;
 	Aliste		off;
+	int		path = 0;
 
 	/*
-	 * If we've been asked to do a basename search, first determine if
-	 * the pathname is registered in the FullpathNode AVL tree.
+	 * If the name is a full path name, first determine if the path name is
+	 * registered in the FullpathNode AVL tree.
 	 */
-	if (base && (name[0] == '/') &&
+	if ((name[0] == '/') &&
 	    ((lmp = fpavl_loaded(lml, name, &where)) != NULL) &&
 	    ((FLAGS(lmp) & (FLG_RT_OBJECT | FLG_RT_DELETE)) == 0))
 		return (lmp);
 
 	/*
-	 * If we've been asked to do a basename search reduce the input name
-	 * to its basename.
+	 * Determine whether the name is a simple file name, or a path name.
 	 */
-	if (base && ((_name = strrchr(name, '/')) != NULL))
-		_name++;
-	else
-		_name = name;
+	if (strchr(name, '/'))
+		path++;
 
 	/*
 	 * Loop through the callers link-map lists.
@@ -785,14 +857,13 @@
 			if (FLAGS(lmp) & (FLG_RT_OBJECT | FLG_RT_DELETE))
 				continue;
 
-			if (is_so_matched(lmp, _name, base))
+			if (is_so_matched(lmp, name, path))
 				return (lmp);
 		}
 	}
 	return ((Rt_map *)0);
 }
 
-
 /*
  * Tracing is enabled by the LD_TRACE_LOADED_OPTIONS environment variable which
  * is normally set from ldd(1).  For each link map we load, print the load name
@@ -1712,7 +1783,7 @@
 		DBG_CALL(Dbg_libs_find(lml, oname));
 
 #if	!defined(ISSOLOAD_BASENAME_DISABLED)
-		if ((nlmp = is_so_loaded(lml, oname, 0)))
+		if ((nlmp = is_so_loaded(lml, oname)))
 			return (nlmp);
 #endif
 		/*
@@ -1757,7 +1828,7 @@
 		 * already been opened using its full pathname).
 		 */
 		if (nfdp->fd_nname == 0)
-			return (is_so_loaded(lml, oname, 1));
+			return (is_so_loaded(lml, oname));
 	}
 
 	/*
@@ -2100,8 +2171,7 @@
 		 * has already been loaded.
 		 */
 		/* LINTED */
-		if ((nlmp = is_so_loaded(lml, name, 0)) ||
-		    (nlmp = is_so_loaded(lml, name, 1))) {
+		if (nlmp = is_so_loaded(lml, name)) {
 			if ((lml->lm_flags & LML_FLG_TRC_VERBOSE) &&
 			    ((FLAGS1(clmp) & FL1_RT_LDDSTUB) == 0)) {
 				(void) printf(MSG_INTL(MSG_LDD_FIL_FIND), name,
--- a/usr/src/cmd/sgs/rtld/common/dlfcns.c	Fri Mar 09 08:35:59 2007 -0800
+++ b/usr/src/cmd/sgs/rtld/common/dlfcns.c	Fri Mar 09 09:44:28 2007 -0800
@@ -1479,10 +1479,7 @@
 	 * have to be revisited.
 	 */
 	if (ipath) {
-		if ((lmp = is_so_loaded(&lml_main, ipath, 0)) == 0)
-			lmp = is_so_loaded(&lml_main, ipath, 1);
-
-		if (lmp == 0) {
+		if ((lmp = is_so_loaded(&lml_main, ipath)) == 0) {
 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_GEN_NOFILE),
 			    ipath);
 			return (1);