changeset 5067:d64dc195fe92

6603313 dlclose() can fail to unload objects after fix for 6573641
author rie
date Mon, 17 Sep 2007 11:32:50 -0700
parents be9489708c48
children 06d88145c7f7
files usr/src/cmd/sgs/include/conv.h usr/src/cmd/sgs/include/debug.h usr/src/cmd/sgs/include/rtld.h usr/src/cmd/sgs/libconv/common/group.c usr/src/cmd/sgs/libconv/common/group.msg usr/src/cmd/sgs/liblddbg/common/files.c usr/src/cmd/sgs/liblddbg/common/liblddbg.msg usr/src/cmd/sgs/packages/common/SUNWonld-README usr/src/cmd/sgs/rtld/common/_rtld.h usr/src/cmd/sgs/rtld/common/analyze.c usr/src/cmd/sgs/rtld/common/cap.c usr/src/cmd/sgs/rtld/common/dlfcns.c usr/src/cmd/sgs/rtld/common/elf.c usr/src/cmd/sgs/rtld/common/remove.c usr/src/cmd/sgs/rtld/mdbmod/common/rtld.c usr/src/cmd/sgs/rtld/mdbmod/common/rtld.msg
diffstat 16 files changed, 278 insertions(+), 181 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/sgs/include/conv.h	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/include/conv.h	Mon Sep 17 11:32:50 2007 -0700
@@ -246,7 +246,7 @@
 
 
 /* conv_grphdl_flags() */
-#define	CONV_GRPHDL_FLAGS_BUFSIZE	80
+#define	CONV_GRPHDL_FLAGS_BUFSIZE	82
 
 typedef union {
 	Conv32_inv_buf_t	inv_buf;
@@ -260,7 +260,7 @@
 
 
 /* conv_grpdesc_flags() */
-#define	CONV_GRPDESC_FLAGS_BUFSIZE	80
+#define	CONV_GRPDESC_FLAGS_BUFSIZE	92
 
 typedef union {
 	Conv32_inv_buf_t	inv_buf;
--- a/usr/src/cmd/sgs/include/debug.h	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/include/debug.h	Mon Sep 17 11:32:50 2007 -0700
@@ -57,7 +57,7 @@
 /*
  * Define Dbg_*() interface flags.  These flags direct the debugging routine to
  * generate different diagnostics, thus the strings themselves are maintained
- * in this library.
+ * in the debugging library.
  */
 #define	DBG_SUP_ENVIRON		1
 #define	DBG_SUP_CMDLINE		2
@@ -98,13 +98,27 @@
 #define	DBG_SYM_REDUCE_GLOBAL	1	/* reporting global symbols to local */
 #define	DBG_SYM_REDUCE_RETAIN	2	/* reporting non reduced local syms */
 
-#define	DBG_DEP_CREATE		1	/* Group handle operations */
-#define	DBG_DEP_ADD		2
-#define	DBG_DEP_DELETE		3
-#define	DBG_DEP_REMOVE		4
-#define	DBG_DEP_REMAIN		5
-#define	DBG_DEP_ORPHAN		6
-#define	DBG_DEP_REINST		7
+/*
+ * Group handle operations - passed to Dbg_file_hdl_title().  Indicate why
+ * handle dependencies are being manipulated.
+ */
+#define	DBG_HDL_CREATE		0	/* handle creation */
+#define	DBG_HDL_ADD		1	/* addition to existing handle */
+#define	DBG_HDL_DELETE		2	/* deletion from a handle */
+#define	DBG_HDL_ORPHAN		3	/* handle being moved to orphan list */
+#define	DBG_HDL_REINST		4	/* handle reinstated from orphan list */
+
+/*
+ * Group handle dependency operations - passed to Dbg_file_hdl_action().
+ * Indicates depdenencies assocation to handle.
+ */
+#define	DBG_DEP_ADD		0	/* dependency added */
+#define	DBG_DEP_UPDATE		1	/* dependency updated */
+#define	DBG_DEP_DELETE		2	/* dependency deleted */
+#define	DBG_DEP_REMOVE		3	/* dependency removed from handle */
+#define	DBG_DEP_REMAIN		4	/* dependency must remain on handle */
+#define	DBG_DEP_ORPHAN		5	/* dependency must remain an orphan */
+#define	DBG_DEP_REINST		6	/* dependency reinstated from orphan */
 
 #define	DBG_BINFO_FOUND		0x0001	/* information regarding binding */
 #define	DBG_BINFO_DIRECT	0x0002	/* bound directly */
--- a/usr/src/cmd/sgs/include/rtld.h	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/include/rtld.h	Mon Sep 17 11:32:50 2007 -0700
@@ -268,7 +268,7 @@
 	 * END: Exposed to rtld_db - don't move, don't delete
 	 */
 	Alist		*lm_rti;	/* list of RTLDINFO tables */
-	Audit_list	*lm_alp;	/* audit list descripter */
+	Audit_list	*lm_alp;	/* audit list descriptor */
 	avl_tree_t	*lm_fpavl;	/* avl tree of objects loaded */
 	Alist		*lm_lists;	/* active and pending link-map lists */
 	char		***lm_environ;	/* pointer to environment array */
@@ -385,38 +385,76 @@
  */
 #define	LML_TFLG_AUD_MASK	0xfff00000	/* audit interfaces mask */
 
-
 /*
- * Information for dlopen(), dlsym(), and dlclose() on libraries linked by rtld.
- * Each shared object referred from a dlopen call has an associated group
- * handle structure returned that describes a group of one or more objects.
+ * Define a Group Handle.
+ *
+ * The capability of ld.so.1 to associate a group of objects, look for symbols
+ * within that group, ensure that groups are isolated from one another (with
+ * regard to relocations), and to unload a group, centers around a handle.  This
+ * data structure is tracked from the link-map HANDLE(), and is the structure
+ * returned from dlopen(), and similar object loading capabilities such as
+ * filter/filtee processing.
+ *
+ * A handle keeps track of all the dependencies of the associated object.
+ * These dependencies may be added as objects are lazily loaded.  The core
+ * dependencies on the handle are the ldd(1) list of the associated object.
+ * The object assigned the handle, and the parent (or caller) who requested the
+ * handle are also maintained as dependencies on the handle.
+ *
+ * Presently, an object may have two handles, one requested with RTLD_FIRST
+ * and one without.
+ *
+ * A handle may be referenced by any number of parents (callers).  A reference
+ * count tracks the number.  A dlclose() operation drops the reference count,
+ * and when the count is zero, the handle is used to determine the family of
+ * objects to unload.  As bindings may occur to objects on the handle from
+ * other handles, it may not be possible to remove a complete family of
+ * objects or that handle itself.  Handles in this state are moved to an orphan
+ * list.  A handle on the orphan list is taken off the orphan list if the
+ * associated object is reopened.  Otherwise, the handle remains on the orphan
+ * list for the duration of the process.  The orphan list is inspected any time
+ * objects are unloaded, to determine if the orphaned objects can also be
+ * unloaded.
+ *
+ * Objects can be dlopened using RTLD_NOW.  This attribute requires that all
+ * relocations of the object, and its dependencies are processed immediately,
+ * before return to the caller.  Typically, an object is loaded without
+ * RTLD_NOW, and procedure linkage relocations are satisfied when their
+ * associated function is first called.  If an object is already loaded, and an
+ * RTLD_NOW request is made, then the object, and its dependencies, most undergo
+ * additional relocation processing.   This promotion from lazy binding to
+ * immediate binding is carried out using handles, as the handle defines the
+ * dependencies that must be processed.  A temporary handle is created for this
+ * purpose, and is discarded immediately after the promotion operation has been
+ * completed.
  */
 typedef struct {
 	Alist		*gh_depends;	/* handle dependency list */
 	Rt_map		*gh_ownlmp;	/* handle owners link-map */
 	Lm_list		*gh_ownlml;	/* handle owners link-map list */
 	uint_t		gh_refcnt;	/* handle reference count */
-	uint_t		gh_flags;	/* handle flags */
+	uint_t		gh_flags;	/* handle flags (GPH_ values) */
 } Grp_hdl;
 
 #define	GPH_ZERO	0x0001		/* special handle for dlopen(0) */
 #define	GPH_LDSO	0x0002		/* special handle for ld.so.1 */
 #define	GPH_FIRST	0x0004		/* dlsym() can only use originating */
 					/*	dependency */
-#define	GPH_PARENT	0x0008		/* assign caller as a parent */
-#define	GPH_FILTEE	0x0010		/* handle used to specify a filtee */
-#define	GPH_INITIAL	0x0020		/* handle is initialized */
-#define	GPH_NOPENDLAZY	0x0040		/* no pending lazy dependencies */
+#define	GPH_FILTEE	0x0008		/* handle used to specify a filtee */
+#define	GPH_INITIAL	0x0010		/* handle is initialized */
+#define	GPH_NOPENDLAZY	0x0020		/* no pending lazy dependencies */
 					/*	remain for this handle */
 
 /*
- * A group descriptor.  A group handle (Grp_hdl) refers to a group of objects,
- * each object, and its relationship to the handle, is maintained within a
- * group descriptor.
+ * Define a Group Descriptor.
+ *
+ * Each dependency associated with a group handle is maintained by a group
+ * descriptor.  The descriptor defines the associated dependency together with
+ * flags that indicate how the dependency can be used.
  */
 typedef struct {
 	Rt_map *	gd_depend;	/* dependency */
-	uint_t		gd_flags;	/* dependency flags */
+	uint_t		gd_flags;	/* dependency flags (GPD_ values) */
 } Grp_desc;
 
 #define	GPD_DLSYM	0x0001		/* dependency available to dlsym() */
@@ -426,6 +464,8 @@
 					/*	should be added to handle */
 #define	GPD_PARENT	0x0008		/* dependency is a parent */
 #define	GPD_FILTER	0x0010		/* dependency is our filter */
+#define	GPD_PROMOTE	0x0020		/* dependency is our RTLD_NOW */
+					/*	promoter */
 #define	GPD_REMOVE	0x1000		/* descriptor is a candidate for */
 					/*	removal from the group */
 
@@ -689,7 +729,7 @@
 #define	FL1_RT_OBJAFLTR	0x00002000	/*	or auxiliary filter */
 #define	FL1_RT_SYMSFLTR	0x00004000	/* symbol is acting as a standard */
 #define	FL1_RT_SYMAFLTR	0x00008000	/*	or auxiliary filter */
-#define	MSK_RT_FILTER	0x0000f000	/* mask for all filter possibilites */
+#define	MSK_RT_FILTER	0x0000f000	/* mask for all filter possibilities */
 
 #define	FL1_RT_TLSADD	0x00010000	/* objects TLS has been registered */
 #define	FL1_RT_TLSSTAT	0x00020000	/* object requires static TLS */
--- a/usr/src/cmd/sgs/libconv/common/group.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/libconv/common/group.c	Mon Sep 17 11:32:50 2007 -0700
@@ -34,9 +34,9 @@
 		MSG_GPH_ZERO_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		MSG_GPH_LDSO_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		MSG_GPH_FIRST_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
-		MSG_GPH_PARENT_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		MSG_GPH_FILTEE_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		MSG_GPH_INITIAL_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+		MSG_GPH_NOPENDLAZY_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		CONV_INV_BUFSIZE	+ CONV_EXPN_FIELD_DEF_SUFFIX_SIZE
 
 /*
@@ -62,9 +62,9 @@
 		{ GPH_ZERO,		MSG_ORIG(MSG_GPH_ZERO) },
 		{ GPH_LDSO,		MSG_ORIG(MSG_GPH_LDSO) },
 		{ GPH_FIRST,		MSG_ORIG(MSG_GPH_FIRST) },
-		{ GPH_PARENT,		MSG_ORIG(MSG_GPH_PARENT) },
 		{ GPH_FILTEE,		MSG_ORIG(MSG_GPH_FILTEE) },
 		{ GPH_INITIAL,		MSG_ORIG(MSG_GPH_INITIAL) },
+		{ GPH_NOPENDLAZY,	MSG_ORIG(MSG_GPH_NOPENDLAZY) },
 		{ 0,			0 }
 	};
 	static CONV_EXPN_FIELD_ARG conv_arg = {
@@ -86,6 +86,7 @@
 		MSG_GPD_ADDEPS_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		MSG_GPD_PARENT_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		MSG_GPD_FILTER_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+		MSG_GPD_PROMOTE_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		MSG_GPD_REMOVE_SIZE	+ CONV_EXPN_FIELD_DEF_SEP_SIZE + \
 		CONV_INV_BUFSIZE	+ CONV_EXPN_FIELD_DEF_SUFFIX_SIZE
 
@@ -114,6 +115,7 @@
 		{ GPD_ADDEPS,		MSG_ORIG(MSG_GPD_ADDEPS) },
 		{ GPD_PARENT,		MSG_ORIG(MSG_GPD_PARENT) },
 		{ GPD_FILTER,		MSG_ORIG(MSG_GPD_FILTER) },
+		{ GPD_PROMOTE,		MSG_ORIG(MSG_GPD_PROMOTE) },
 		{ GPD_REMOVE,		MSG_ORIG(MSG_GPD_REMOVE) },
 		{ 0,			0 }
 	};
--- a/usr/src/cmd/sgs/libconv/common/group.msg	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/libconv/common/group.msg	Mon Sep 17 11:32:50 2007 -0700
@@ -28,15 +28,16 @@
 @ MSG_GPH_ZERO		"GPH_ZERO"
 @ MSG_GPH_LDSO		"GPH_LDSO"
 @ MSG_GPH_FIRST		"GPH_FIRST"
-@ MSG_GPH_PARENT	"GPH_PARENT"
 @ MSG_GPH_FILTEE	"GPH_FILTEE"
 @ MSG_GPH_INITIAL	"GPH_INITIAL"
+@ MSG_GPH_NOPENDLAZY	"GPH_NOPENDLAZY"
 
 @ MSG_GPD_DLSYM		"GPD_DLSYM"
 @ MSG_GPD_RELOC		"GPD_RELOC"
 @ MSG_GPD_ADDEPS	"GPD_ADDEPS"
 @ MSG_GPD_PARENT	"GPD_PARENT"
 @ MSG_GPD_FILTER	"GPD_FILTER"
+@ MSG_GPD_PROMOTE	"GPD_PROMOTE"
 @ MSG_GPD_REMOVE	"GPD_REMOVE"
 
 @ MSG_GBL_NULL		""
--- a/usr/src/cmd/sgs/liblddbg/common/files.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/liblddbg/common/files.c	Mon Sep 17 11:32:50 2007 -0700
@@ -137,45 +137,28 @@
 void
 Dbg_file_hdl_title(int type)
 {
+	static const Msg titles[] = {
+		MSG_FIL_HDL_CREATE,	/* MSG_INTL(MSG_FIL_HDL_CREATE) */
+		MSG_FIL_HDL_ADD,	/* MSG_INTL(MSG_FIL_HDL_ADD) */
+		MSG_FIL_HDL_DELETE,	/* MSG_INTL(MSG_FIL_HDL_DELETE) */
+		MSG_FIL_HDL_ORPHAN,	/* MSG_INTL(MSG_FIL_HDL_ORPHAN) */
+		MSG_FIL_HDL_REINST,	/* MSG_INTL(MSG_FIL_HDL_REINST) */
+	};
+
 	if (DBG_NOTCLASS(DBG_C_FILES))
 		return;
 	if (DBG_NOTDETAIL())
 		return;
 
-	hdl_title = 1;
-
 	/*
-	 * Establish a binding title for later use in Dbg_file_bind_entry.
-	 * These are to be used with the MSG_INTL() macro.
-	 *
-	 * Note: The following are to convince chkmsg.sh that these
-	 * messages are actually used:
-	 *
-	 *	MSG_INTL(MSG_FIL_HDL_CREATE)
-	 *	MSG_INTL(MSG_FIL_HDL_ADD)
-	 *	MSG_INTL(MSG_FIL_HDL_DELETE)
-	 *	MSG_INTL(MSG_FIL_HDL_ORPHAN)
-	 *	MSG_INTL(MSG_FIL_HDL_REINST)
+	 * Establish a binding title for later use in Dbg_file_hdl_action.
 	 */
-	switch (type) {
-	case DBG_DEP_CREATE:
-		hdl_str = MSG_FIL_HDL_CREATE;
-		break;
-	case DBG_DEP_ADD:
-		hdl_str = MSG_FIL_HDL_ADD;
-		break;
-	case DBG_DEP_DELETE:
-		hdl_str = MSG_FIL_HDL_DELETE;
-		break;
-	case DBG_DEP_ORPHAN:
-		hdl_str = MSG_FIL_HDL_ORPHAN;
-		break;
-	case DBG_DEP_REINST:
-		hdl_str = MSG_FIL_HDL_REINST;
-		break;
-	default:
+	if (type <= DBG_HDL_REINST) {
+		hdl_str = titles[type];
+		hdl_title = 1;
+	} else {
 		hdl_str = 0;
-		break;
+		hdl_title = 0;
 	}
 }
 
@@ -215,6 +198,15 @@
 	Lm_list		*lml = LIST(lmp);
 	Msg		str;
 
+	static const Msg fmt[] = {
+		MSG_FIL_DEP_ADD,	/* MSG_INTL(MSG_FIL_DEP_ADD) */
+		MSG_FIL_DEP_UPDATE,	/* MSG_INTL(MSG_FIL_DEP_UPDATE) */
+		MSG_FIL_DEP_DELETE,	/* MSG_INTL(MSG_FIL_DEP_DELETE) */
+		MSG_FIL_DEP_REMOVE,	/* MSG_INTL(MSG_FIL_DEP_REMOVE) */
+		MSG_FIL_DEP_REMAIN,	/* MSG_INTL(MSG_FIL_DEP_REMAIN) */
+		MSG_FIL_DEP_ORPHAN,	/* MSG_INTL(MSG_FIL_DEP_ORPHAN) */
+		MSG_FIL_DEP_REINST,	/* MSG_INTL(MSG_FIL_DEP_REINST) */
+	};
 	if (DBG_NOTCLASS(DBG_C_FILES))
 		return;
 	if (DBG_NOTDETAIL())
@@ -240,40 +232,14 @@
 	}
 
 	/*
-	 * Note: The following are to convince chkmsg.sh that these
-	 * messages are actually used:
-	 *
-	 *	MSG_INTL(MSG_FIL_DEP_ADD)
-	 *	MSG_INTL(MSG_FIL_DEP_DELETE)
-	 *	MSG_INTL(MSG_FIL_DEP_REMOVE)
-	 *	MSG_INTL(MSG_FIL_DEP_REMAIN)
-	 *	MSG_INTL(MSG_FIL_DEP_ORPHAN)
-	 *	MSG_INTL(MSG_FIL_DEP_REINST)
+	 * Establish a binding descriptor format string.
 	 */
-	switch (type) {
-	case DBG_DEP_ADD:
-		str = MSG_FIL_DEP_ADD;
-		break;
-	case DBG_DEP_DELETE:
-		str = MSG_FIL_DEP_DELETE;
-		break;
-	case DBG_DEP_REMOVE:
-		str = MSG_FIL_DEP_REMOVE;
-		break;
-	case DBG_DEP_REMAIN:
-		str = MSG_FIL_DEP_REMAIN;
-		break;
-	case DBG_DEP_ORPHAN:
-		str = MSG_FIL_DEP_ORPHAN;
-		break;
-	case DBG_DEP_REINST:
-		str = MSG_FIL_DEP_REINST;
-		break;
-	default:
+	if (type > DBG_DEP_REINST)
 		return;
-	}
 
-	if ((type == DBG_DEP_ADD) && flags)
+	str = fmt[type];
+
+	if (((type == DBG_DEP_ADD) || (type == DBG_DEP_UPDATE)) && flags)
 		group = conv_grpdesc_flags(flags, &grpdesc_flags_buf);
 	else
 		group = MSG_ORIG(MSG_STR_EMPTY);
@@ -283,7 +249,6 @@
 		mode = MSG_ORIG(MSG_MODE_GLOBNODEL);
 	else if (MODE(lmp) & RTLD_GLOBAL)
 		mode = MSG_ORIG(MSG_MODE_GLOB);
-
 	else if (MODE(lmp) & RTLD_NODELETE)
 		mode = MSG_ORIG(MSG_MODE_NODEL);
 	else
--- a/usr/src/cmd/sgs/liblddbg/common/liblddbg.msg	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/liblddbg/common/liblddbg.msg	Mon Sep 17 11:32:50 2007 -0700
@@ -408,6 +408,7 @@
 @ MSG_FIL_DEP_ENT	"  file=%s;  depends on:"
 
 @ MSG_FIL_DEP_ADD	"    file=%s;  object added  %s  %s"
+@ MSG_FIL_DEP_UPDATE	"    file=%s;  object updated  %s  %s"
 @ MSG_FIL_DEP_DELETE	"    file=%s;  object deleting  %s"
 @ MSG_FIL_DEP_REMOVE	"    file=%s;  object removed from handle  %s"
 @ MSG_FIL_DEP_REMAIN	"    file=%s;  object must remain on handle  %s"
@@ -415,15 +416,15 @@
 @ MSG_FIL_DEP_REINST	"    file=%s;  object reinstated  %s"
 
 @ MSG_FIL_HDL_CREATE	"handle=%s;  creating:"
-@ MSG_FIL_HDL_COLLECT	"handle=%s;  collected for possible removal  %s"
-@ MSG_FIL_HDL_RETAIN	"handle=%s;  externally referenced from %s: handle \
-			 retained"
 @ MSG_FIL_HDL_ADD	"handle=%s;  adding dependent objects:"
 @ MSG_FIL_HDL_DELETE	"handle=%s;  inspecting for deletion:"
 @ MSG_FIL_HDL_ORPHAN	"handle=%s;  deletion cannot be completed: moving to \
 			 orphan list:"
 @ MSG_FIL_HDL_REINST	"handle=%s;  reinstating from orphan list:"
 
+@ MSG_FIL_HDL_COLLECT	"handle=%s;  collected for possible removal  %s"
+@ MSG_FIL_HDL_RETAIN	"handle=%s;  externally referenced from %s: handle \
+			 retained"
 @ MSG_FIL_DEL_RESCAN	"pending deletions; rescanning orphan list for \
 			 available deletions"
 
--- a/usr/src/cmd/sgs/packages/common/SUNWonld-README	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/packages/common/SUNWonld-README	Mon Sep 17 11:32:50 2007 -0700
@@ -1256,3 +1256,6 @@
 	shared object reference
 6595139 various applications should export yy* global variables for libl
 6597841 gelf_getdyn() reads one too many dynamic entries
+6602294 ps_pbrandname breaks apps linked directly against librtld_db
+	(link-editor components only)
+6603313 dlclose() can fail to unload objects after fix for 6573641
--- a/usr/src/cmd/sgs/rtld/common/_rtld.h	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/common/_rtld.h	Mon Sep 17 11:32:50 2007 -0700
@@ -552,7 +552,7 @@
 extern uint_t		expand(char **, size_t *, char **, uint_t, uint_t,
 			    Rt_map *);
 extern Pnode		*expand_paths(Rt_map *, const char *, uint_t, uint_t);
-extern void		free_hdl(Grp_hdl *);
+extern void		free_hdl(Grp_hdl *, Rt_map *, uint_t);
 extern void		file_notfound(Lm_list *, const char *, Rt_map *,
 			    uint_t, Rej_desc *);
 extern int		find_path(Lm_list *, const char *, Rt_map *, uint_t,
@@ -567,7 +567,8 @@
 extern Lmid_t		get_linkmap_id(Lm_list *);
 extern Pnode		*get_next_dir(Pnode **, Rt_map *, uint_t);
 extern int		hdl_add(Grp_hdl *, Rt_map *, uint_t);
-extern Grp_hdl		*hdl_create(Lm_list *, Rt_map *, Rt_map *, uint_t);
+extern Grp_hdl		*hdl_create(Lm_list *, Rt_map *, Rt_map *, uint_t,
+			    uint_t, uint_t);
 extern int		hdl_initialize(Grp_hdl *, Rt_map *, int, int);
 extern int		hwcap_check(Rej_desc *, Ehdr *);
 extern Pnode 		*hwcap_filtees(Pnode **, Aliste, Lm_cntl *, Dyninfo *,
--- a/usr/src/cmd/sgs/rtld/common/analyze.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/common/analyze.c	Mon Sep 17 11:32:50 2007 -0700
@@ -1068,7 +1068,7 @@
 
 	/*
 	 * If this is an auditor, it will have been opened on a new link-map.
-	 * To prevent multiple occurrances of the same auditor on multiple
+	 * To prevent multiple occurrences of the same auditor on multiple
 	 * link-maps, search the head of each link-map list and see if this
 	 * object is already loaded as an auditor.
 	 */
@@ -1212,7 +1212,7 @@
 		 * silent failure, where no rejection message is created, free
 		 * the original name to simplify the life of the caller.  For
 		 * any other reference that expands to a directory, fall through
-		 * to contruct a meaningful rejection message.
+		 * to construct a meaningful rejection message.
 		 */
 		if ((flags & FLG_RT_HWCAP) &&
 		    ((status.st_mode & S_IFMT) == S_IFDIR)) {
@@ -1717,7 +1717,7 @@
 		return (lml_rtld.lm_head);
 
 	/*
-	 * If this isn't a hardware capabilites pathname, which is already a
+	 * If this isn't a hardware capabilities pathname, which is already a
 	 * full, duplicated pathname, determine whether the pathname contains
 	 * a slash, and if not determine the input filename (for max path
 	 * length verification).
@@ -1770,7 +1770,7 @@
 
 		/*
 		 * If the name and resolved pathname differ, duplicate the path
-		 * name once more to provide for genric cleanup by the caller.
+		 * name once more to provide for generic cleanup by the caller.
 		 */
 		if (nfdp->fd_pname && (nfdp->fd_nname != nfdp->fd_pname)) {
 			char	*pname;
@@ -2015,12 +2015,39 @@
 	 */
 	if (((FLAGS(nlmp) | flags) & FLG_RT_HANDLE) || (promote &&
 	    (FLAGS(nlmp) & FLG_RT_ANALYZED))) {
-		uint_t	oflags, hflags = 0;
+		uint_t	oflags, hflags = 0, cdflags;
+
+		/*
+		 * Establish any flags for the handle (Grp_hdl).
+		 *
+		 *  .	Use of the RTLD_FIRST flag indicates that only the first
+		 *	dependency on the handle (the new object) can be used
+		 *	to satisfy dlsym() requests.
+		 */
+		if (nmode & RTLD_FIRST)
+			hflags = GPH_FIRST;
 
+		/*
+		 * Establish the flags for this callers dependency descriptor
+		 * (Grp_desc).
+		 *
+		 *  .	The creation of a handle associated a descriptor for the
+		 *	new object and descriptor for the parent (caller).
+		 *	Typically, the handle is created for dlopen() or for
+		 *	filtering.  A handle may also be created to promote
+		 *	the callers modes (RTLD_NOW) to the new object.  In this
+		 *	latter case, the handle/descriptor are torn down once
+		 *	the mode propagation has occurred.
+		 *
+		 *  .	Use of the RTLD_PARENT flag indicates that the parent
+		 *	can be relocated against.
+		 */
+		if (((FLAGS(nlmp) | flags) & FLG_RT_HANDLE) == 0)
+			cdflags = GPD_PROMOTE;
+		else
+			cdflags = GPD_PARENT;
 		if (nmode & RTLD_PARENT)
-			hflags |=  GPH_PARENT;
-		if (nmode & RTLD_FIRST)
-			hflags |=  GPH_FIRST;
+			cdflags |= GPD_RELOC;
 
 		/*
 		 * Now that a handle is being created, remove this state from
@@ -2030,7 +2057,9 @@
 		oflags = FLAGS(nlmp);
 		FLAGS(nlmp) &= ~FLG_RT_HANDLE;
 
-		if ((ghp = hdl_create(lml, nlmp, clmp, hflags)) == 0)
+		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
+		if ((ghp = hdl_create(lml, nlmp, clmp, hflags,
+		    (GPD_DLSYM | GPD_RELOC | GPD_ADDEPS), cdflags)) == 0)
 			return (0);
 
 		/*
@@ -2043,16 +2072,25 @@
 			*hdl = ghp;
 
 		/*
-		 * If we were asked to create a handle, we're done.  Otherwise,
-		 * remove the handle. The handle was only used to establish this
-		 * objects dependencies and promote any modes, so we don't want
-		 * this handle preventing the objects deletion.  Fall through to
-		 * carry out any group processing.
+		 * If we were asked to create a handle, we're done.
 		 */
 		if ((oflags | flags) & FLG_RT_HANDLE)
 			return (1);
 
-		free_hdl(ghp);
+		/*
+		 * If the handle was created to promote modes from the parent
+		 * (caller) to the new object, then this relationship needs to
+		 * be removed to ensure the handle doesn't prevent the new
+		 * objects from being deleted if required.  If the parent is
+		 * the only dependency on the handle, then the handle can be
+		 * completely removed.  However, the handle may have already
+		 * existed, in which case only the parent descriptor can be
+		 * deleted from the handle, or at least the GPD_PROMOTE flag
+		 * removed from the descriptor.
+		 *
+		 * Fall through to carry out any group processing.
+		 */
+		free_hdl(ghp, clmp, GPD_PROMOTE);
 	}
 
 	/*
@@ -2067,7 +2105,7 @@
 	 * Traverse the list of groups our caller is a member of and add this
 	 * new link-map to those groups.
 	 */
-	DBG_CALL(Dbg_file_hdl_title(DBG_DEP_ADD));
+	DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
 	for (ALIST_TRAVERSE(GROUPS(clmp), off, ghpp)) {
 		Aliste		off1;
 		Grp_desc	*gdp;
--- a/usr/src/cmd/sgs/rtld/common/cap.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/common/cap.c	Mon Sep 17 11:32:50 2007 -0700
@@ -403,7 +403,7 @@
 		 * association provides sufficient information to tear down the
 		 * filter and filtee if necessary.
 		 */
-		DBG_CALL(Dbg_file_hdl_title(DBG_DEP_ADD));
+		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
 		if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER) == 0))
 			nlmp = 0;
 
--- a/usr/src/cmd/sgs/rtld/common/dlfcns.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/common/dlfcns.c	Mon Sep 17 11:32:50 2007 -0700
@@ -135,6 +135,7 @@
 	Grp_desc	*gdp;
 	Aliste		off;
 	int		found = ALE_CREATE;
+	uint_t		oflags;
 
 	/*
 	 * Make sure this dependency hasn't already been recorded.
@@ -170,11 +171,17 @@
 			return (0);
 	}
 
+	oflags = gdp->gd_flags;
 	gdp->gd_flags |= flags;
 
-	if (found == ALE_CREATE)
-		DBG_CALL(Dbg_file_hdl_action(ghp, lmp, DBG_DEP_ADD, flags));
-
+	if (DBG_ENABLED) {
+		if (found == ALE_CREATE)
+			DBG_CALL(Dbg_file_hdl_action(ghp, lmp, DBG_DEP_ADD,
+			    gdp->gd_flags));
+		else if (gdp->gd_flags != oflags)
+			DBG_CALL(Dbg_file_hdl_action(ghp, lmp, DBG_DEP_UPDATE,
+			    gdp->gd_flags));
+	}
 	return (found);
 }
 
@@ -205,10 +212,10 @@
  * Create a handle.
  */
 Grp_hdl *
-hdl_create(Lm_list *lml, Rt_map *nlmp, Rt_map *clmp, uint_t flags)
+hdl_create(Lm_list *lml, Rt_map *nlmp, Rt_map *clmp, uint_t hflags,
+    uint_t ndflags, uint_t cdflags)
 {
 	Grp_hdl	*ghp = 0, **ghpp;
-	uint_t	hflags;
 	Alist	**alpp;
 	Aliste	off;
 
@@ -216,29 +223,29 @@
 	 * For dlopen(0) the handle is maintained as part of the link-map list,
 	 * otherwise it is associated with the referenced link-map.
 	 */
-	if (flags & GPH_ZERO)
+	if (hflags & GPH_ZERO)
 		alpp = &(lml->lm_handle);
 	else
 		alpp = &(HANDLES(nlmp));
 
 	/*
-	 * Objects can contain multiple handles depending on the flags supplied.
-	 * Most RTLD flags pertain to the object itself and the bindings that it
-	 * can achieve.  Multiple handles for these flags don't make sense.  But
-	 * if the flag determines how the handle might be used, then multiple
-	 * handles may exist.  Presently this only makes sense for RTLD_FIRST.
-	 * Determine if an appropriate handle already exists.
+	 * Objects can contain multiple handles depending on the handle flags
+	 * supplied.  Most RTLD flags pertain to the object itself and the
+	 * bindings that it can achieve.  Multiple handles for these flags
+	 * don't make sense.  But if the flag determines how the handle might
+	 * be used, then multiple handles may exist.  Presently this only makes
+	 * sense for RTLD_FIRST.  Determine if an appropriate handle already
+	 * exists.
 	 */
-	hflags = flags & GPH_FIRST;
 	for (ALIST_TRAVERSE(*alpp, off, ghpp)) {
-		if (((*ghpp)->gh_flags & GPH_FIRST) == hflags) {
+		if (((*ghpp)->gh_flags & GPH_FIRST) == (hflags & GPH_FIRST)) {
 			ghp = *ghpp;
 			break;
 		}
 	}
 
 	if (ghp == 0) {
-		DBG_CALL(Dbg_file_hdl_title(DBG_DEP_CREATE));
+		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_CREATE));
 
 		/*
 		 * If this is the first dlopen() request for this handle
@@ -263,7 +270,7 @@
 			FLAGS1(nlmp) |= FL1_RT_USED;
 
 		ghp->gh_refcnt = 1;
-		ghp->gh_flags = flags;
+		ghp->gh_flags = hflags;
 
 		/*
 		 * A dlopen(0) handle is identified by the GPH_ZERO flag, the
@@ -273,25 +280,14 @@
 		 * entire link-map list provides for searching all objects with
 		 * GLOBAL visibility.
 		 */
-		if (flags & GPH_ZERO) {
+		if (hflags & GPH_ZERO) {
 			ghp->gh_ownlmp = lml->lm_head;
 			ghp->gh_ownlml = lml;
 		} else {
-			uint_t	hflags = (GPD_DLSYM | GPD_RELOC);
-
 			ghp->gh_ownlmp = nlmp;
 			ghp->gh_ownlml = LIST(nlmp);
 
-			/*
-			 * As an optimization, a handle for ld.so.1 itself
-			 * (required for libdl's filtering mechanism) shouldn't
-			 * search any dependencies of ld.so.1.  Omitting
-			 * GPD_ADDEPS prevents the addition of any ld.so.1
-			 * dependencies to this handle.
-			 */
-			if ((flags & GPH_LDSO) == 0)
-				hflags |= GPD_ADDEPS;
-			if (hdl_add(ghp, nlmp, hflags) == 0)
+			if (hdl_add(ghp, nlmp, ndflags) == 0)
 				return (0);
 		}
 	} else {
@@ -322,7 +318,7 @@
 				Aliste		off;
 				Grp_desc	*gdp;
 
-				DBG_CALL(Dbg_file_hdl_title(DBG_DEP_REINST));
+				DBG_CALL(Dbg_file_hdl_title(DBG_HDL_REINST));
 				for (ALIST_TRAVERSE(ghp->gh_depends, off, gdp))
 					DBG_CALL(Dbg_file_hdl_action(ghp,
 					    gdp->gd_depend, DBG_DEP_REINST, 0));
@@ -331,26 +327,13 @@
 	}
 
 	/*
-	 * Keep track of the parent (caller).  If this request stems from a
-	 * dlopen(..., RTLD_PARENT) call, then the parent is made available to
-	 * satisfy relocation bindings.  Note, that individual, explicit,
-	 * direct bindings can also be made to the parent should this object
-	 * have been built defining PARENT symbol references.  Also note that a
-	 * parent doesn't provide symbols to dlsym() requests, so it isn't
-	 * necessary to add the parents dependencies to the handle.
-	 *
-	 * As this object could be opened by different parents, this test is
-	 * carried out every time a handle is requested.
+	 * Keep track of the parent (caller).  As this object could be opened
+	 * by different parents, this processing is carried out every time a
+	 * handle is requested.
 	 */
-	if (clmp) {
-		if (flags & GPH_PARENT)
-			flags = (GPD_PARENT | GPD_RELOC);
-		else
-			flags = GPD_PARENT;
+	if (clmp && (hdl_add(ghp, clmp, cdflags) == 0))
+		return (0);
 
-		if (hdl_add(ghp, clmp, flags) == 0)
-			return (0);
-	}
 	return (ghp);
 }
 
@@ -382,7 +365,7 @@
 		return (1);
 	}
 
-	DBG_CALL(Dbg_file_hdl_title(DBG_DEP_ADD));
+	DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
 	for (ALIST_TRAVERSE(ghp->gh_depends, off, gdp)) {
 		Rt_map *	lmp = gdp->gd_depend;
 		Aliste		off1;
@@ -616,15 +599,37 @@
 	 */
 	if (path == 0) {
 		Grp_hdl *ghp;
-		uint_t	hflags = GPH_ZERO;
+		uint_t	hflags = GPH_ZERO, cdflags = GPD_PARENT;
 		int	promote = 0;
 
-		if (mode & RTLD_PARENT)
-			hflags |=  GPH_PARENT;
+		/*
+		 * Establish any flags for the handle (Grp_hdl).
+		 *
+		 *  .	This is a dummy handle (0) that provides for a dynamic
+		 *	search of all global objects within the process.
+		 *
+		 *  .   Use of the RTLD_FIRST flag indicates that only the first
+		 *	dependency on the handle (the new object) can be used
+		 *	to satisfy dlsym() requests.
+		 */
 		if (mode & RTLD_FIRST)
-			hflags |=  GPH_FIRST;
+			hflags |= GPH_FIRST;
 
-		if ((ghp = hdl_create(lml, 0, clmp, hflags)) == 0)
+		/*
+		 * Establish the flags for this callers dependency descriptor
+		 * (Grp_desc).
+		 *
+		 *  .	The explicit creation of a handle creates a descriptor
+		 *	for the new object and the parent (caller),
+		 *
+		 *  .	Use of the RTLD_PARENT flag indicates that the parent
+		 *	can be relocated against.
+		 */
+		if (mode & RTLD_PARENT)
+			cdflags |= GPD_RELOC;
+
+		if ((ghp = hdl_create(lml, 0, clmp, hflags,
+		    (GPD_DLSYM | GPD_RELOC | GPD_ADDEPS), cdflags)) == 0)
 			return (0);
 
 		/*
--- a/usr/src/cmd/sgs/rtld/common/elf.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/common/elf.c	Mon Sep 17 11:32:50 2007 -0700
@@ -1484,12 +1484,18 @@
 			if (strcmp(filtee, MSG_ORIG(MSG_PTH_RTLD)) == 0) {
 #endif
 				/*
-				 * Create an association between ld.so.1 and
-				 * the filter.
+				 * Create an association between ld.so.1 and the
+				 * filter.  As an optimization, a handle for
+				 * ld.so.1 itself (required for the dlopen()
+				 * family filtering mechanism) shouldn't search
+				 * any dependencies of ld.so.1.  Omitting
+				 * GPD_ADDEPS prevents the addition of any
+				 * ld.so.1 dependencies to this handle.
 				 */
 				nlmp = lml_rtld.lm_head;
 				if ((ghp = hdl_create(&lml_rtld, nlmp, ilmp,
-				    (GPH_LDSO | GPH_FIRST | GPH_FILTEE))) == 0)
+				    (GPH_LDSO | GPH_FIRST | GPH_FILTEE),
+				    (GPD_DLSYM | GPD_RELOC), GPD_PARENT)) == 0)
 					nlmp = 0;
 
 				/*
@@ -1588,7 +1594,7 @@
 				 * to tear down the filter and filtee if
 				 * necessary.
 				 */
-				DBG_CALL(Dbg_file_hdl_title(DBG_DEP_ADD));
+				DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
 				if (nlmp && ghp &&
 				    (hdl_add(ghp, ilmp, GPD_FILTER) == 0))
 					nlmp = 0;
--- a/usr/src/cmd/sgs/rtld/common/remove.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/common/remove.c	Mon Sep 17 11:32:50 2007 -0700
@@ -788,12 +788,13 @@
  * by another user).
  */
 void
-free_hdl(Grp_hdl *ghp)
+free_hdl(Grp_hdl *ghp, Rt_map *clmp, uint_t cdflags)
 {
+	Grp_desc	*gdp;
+	Aliste		off;
+
 	if (--(ghp->gh_refcnt) == 0) {
-		Grp_desc	*gdp;
 		uintptr_t	ndx;
-		Aliste		off;
 
 		for (ALIST_TRAVERSE(ghp->gh_depends, off, gdp)) {
 			Rt_map	*lmp = gdp->gd_depend;
@@ -809,6 +810,25 @@
 		list_delete(&hdl_list[ndx], ghp);
 
 		(void) free(ghp);
+
+	} else if (clmp) {
+		/*
+		 * It's possible that an RTLD_NOW promotion (via GPD_PROMOTE)
+		 * has associated a caller with a handle that is already in use.
+		 * In this case, find the caller and either remove the caller
+		 * from the handle, or if the caller is used for any other
+		 * reason, clear the promotion flag.
+		 */
+		for (ALIST_TRAVERSE(ghp->gh_depends, off, gdp)) {
+			if (gdp->gd_depend != clmp)
+				continue;
+
+			if (gdp->gd_flags == cdflags)
+				(void) alist_delete(ghp->gh_depends, 0, &off);
+			else
+				gdp->gd_flags &= ~cdflags;
+			return;
+		}
 	}
 }
 
@@ -861,7 +881,8 @@
 		 * Establish a handle, and should anything fail, fall through
 		 * to remove the link-map control list.
 		 */
-		if (((ghp = hdl_create(lml, lmc->lc_head, 0, 0)) == 0) ||
+		if (((ghp =
+		    hdl_create(lml, lmc->lc_head, 0, 0, GPD_ADDEPS, 0)) == 0) ||
 		    (hdl_initialize(ghp, lmc->lc_head, 0, 0) == 0))
 			lmc->lc_flags &= ~LMC_FLG_RELOCATING;
 	} else {
@@ -877,7 +898,7 @@
 
 		if (ghp) {
 			ghp->gh_refcnt = 1;
-			free_hdl(ghp);
+			free_hdl(ghp, 0, 0);
 		}
 		return;
 	}
@@ -1012,7 +1033,7 @@
 		return (0);
 	}
 
-	DBG_CALL(Dbg_file_hdl_title(DBG_DEP_DELETE));
+	DBG_CALL(Dbg_file_hdl_title(DBG_HDL_DELETE));
 
 	/*
 	 * Traverse the groups we've collected to determine if any filtees are
@@ -1243,7 +1264,7 @@
 	for (ALIST_TRAVERSE(ghalp, off1, ghpp)) {
 		Grp_hdl	*ghp = *ghpp;
 
-		DBG_CALL(Dbg_file_hdl_title(DBG_DEP_DELETE));
+		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_DELETE));
 
 		for (ALIST_TRAVERSE(ghp->gh_depends, off2, gdp)) {
 			int	flag;
@@ -1496,7 +1517,7 @@
 			(void) list_append(&hdl_list[HDLIST_ORP], ghp);
 
 			if (DBG_ENABLED) {
-				DBG_CALL(Dbg_file_hdl_title(DBG_DEP_ORPHAN));
+				DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ORPHAN));
 				for (ALIST_TRAVERSE(ghp->gh_depends, off1, gdp))
 					DBG_CALL(Dbg_file_hdl_action(ghp,
 					    gdp->gd_depend, DBG_DEP_ORPHAN, 0));
--- a/usr/src/cmd/sgs/rtld/mdbmod/common/rtld.c	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/mdbmod/common/rtld.c	Mon Sep 17 11:32:50 2007 -0700
@@ -150,7 +150,6 @@
 	{ MSG_ORIG(MSG_GPH_ZERO), GPH_ZERO, GPH_ZERO },
 	{ MSG_ORIG(MSG_GPH_LDSO), GPH_LDSO, GPH_LDSO },
 	{ MSG_ORIG(MSG_GPH_FIRST), GPH_FIRST, GPH_FIRST },
-	{ MSG_ORIG(MSG_GPH_PARENT), GPH_PARENT, GPH_PARENT },
 	{ MSG_ORIG(MSG_GPH_FILTEE), GPH_FILTEE, GPH_FILTEE },
 	{ MSG_ORIG(MSG_GPH_INITIAL), GPH_INITIAL, GPH_INITIAL },
 	{ MSG_ORIG(MSG_GPH_NOPENDLAZY), GPH_NOPENDLAZY, GPH_NOPENDLAZY },
@@ -163,6 +162,7 @@
 	{ MSG_ORIG(MSG_GPD_ADDEPS), GPD_ADDEPS, GPD_ADDEPS },
 	{ MSG_ORIG(MSG_GPD_PARENT), GPD_PARENT, GPD_PARENT },
 	{ MSG_ORIG(MSG_GPD_FILTER), GPD_FILTER, GPD_FILTER },
+	{ MSG_ORIG(MSG_GPD_PROMOTE), GPD_PROMOTE, GPD_PROMOTE },
 	{ MSG_ORIG(MSG_GPD_REMOVE), GPD_REMOVE, GPD_REMOVE },
 	{ NULL, 0, 0}
 };
--- a/usr/src/cmd/sgs/rtld/mdbmod/common/rtld.msg	Mon Sep 17 11:28:46 2007 -0700
+++ b/usr/src/cmd/sgs/rtld/mdbmod/common/rtld.msg	Mon Sep 17 11:32:50 2007 -0700
@@ -134,7 +134,6 @@
 @ MSG_GPH_ZERO		"ZERO"
 @ MSG_GPH_LDSO		"LD.SO.1"
 @ MSG_GPH_FIRST		"FIRST-ONLY"
-@ MSG_GPH_PARENT	"PARENT-REQUIRED"
 @ MSG_GPH_FILTEE	"FILTEE"
 @ MSG_GPH_INITIAL	"INITIALIZED"
 @ MSG_GPH_NOPENDLAZY	"NO-PENDING-LAZY-DEPENDENCIES"
@@ -145,6 +144,7 @@
 @ MSG_GPD_PARENT	"PARENT"
 @ MSG_GPD_FILTER	"FILTER"
 @ MSG_GPD_REMOVE	"REMOVAL-CANDIDATE"
+@ MSG_GPD_PROMOTE	"RTLD_NOW-PROMOTER"
 
 @ MSG_LFL_BASELM	"BASELM"
 @ MSG_LFL_RTLDLM	"RTLDLM"