changeset 10580:030e7fb9c81f

6850768 ld option to autogenerate wrappers/interposers similar to GNU ld --wrap PSARC/2009/493 ld -z wrap option
author Ali Bahrami <Ali.Bahrami@Sun.COM>
date Fri, 18 Sep 2009 09:00:12 -0600
parents 1cdfcf4400ac
children c2151e45dda5
files usr/src/cmd/sgs/include/debug.h usr/src/cmd/sgs/include/libld.h usr/src/cmd/sgs/libld/Makefile.com usr/src/cmd/sgs/libld/common/_libld.h usr/src/cmd/sgs/libld/common/args.c usr/src/cmd/sgs/libld/common/ldmain.c usr/src/cmd/sgs/libld/common/libld.msg usr/src/cmd/sgs/libld/common/syms.c usr/src/cmd/sgs/libld/common/util.c usr/src/cmd/sgs/libld/common/wrap.c usr/src/cmd/sgs/liblddbg/common/liblddbg.msg usr/src/cmd/sgs/liblddbg/common/llib-llddbg usr/src/cmd/sgs/liblddbg/common/mapfile-vers usr/src/cmd/sgs/liblddbg/common/syms.c usr/src/cmd/sgs/packages/common/SUNWonld-README
diffstat 15 files changed, 342 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/sgs/include/debug.h	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/include/debug.h	Fri Sep 18 09:00:12 2009 -0600
@@ -424,6 +424,7 @@
 #define	Dbg_syms_spec_title	Dbg64_syms_spec_title
 #define	Dbg_syms_updated	Dbg64_syms_updated
 #define	Dbg_syms_up_title	Dbg64_syms_up_title
+#define	Dbg_syms_wrap		Dbg64_syms_wrap
 
 #define	Dbg_util_call_array	Dbg64_util_call_array
 #define	Dbg_util_call_fini	Dbg64_util_call_fini
@@ -632,6 +633,7 @@
 #define	Dbg_syms_spec_title	Dbg32_syms_spec_title
 #define	Dbg_syms_updated	Dbg32_syms_updated
 #define	Dbg_syms_up_title	Dbg32_syms_up_title
+#define	Dbg_syms_wrap		Dbg32_syms_wrap
 
 #define	Dbg_util_call_array	Dbg32_util_call_array
 #define	Dbg_util_call_fini	Dbg32_util_call_fini
@@ -896,6 +898,7 @@
 extern	void	Dbg_syms_spec_title(Lm_list *);
 extern	void	Dbg_syms_updated(Ofl_desc *, Sym_desc *, const char *);
 extern	void	Dbg_syms_up_title(Lm_list *);
+extern	void	Dbg_syms_wrap(Lm_list *, Word, const char *, const char *);
 
 extern	void	Dbg_tls_modactivity(Lm_list *, void *, uint_t);
 extern	void	Dbg_tls_static_block(Lm_list *, void *, ulong_t, ulong_t);
--- a/usr/src/cmd/sgs/include/libld.h	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/include/libld.h	Fri Sep 18 09:00:12 2009 -0600
@@ -150,6 +150,20 @@
 } Rlxrel_cache;
 
 /*
+ * Nodes in an ofl_wrap AVL tree
+ *
+ * wsn_name is the name of the symbol to be wrapped. wsn_wrapname is used
+ * when we need to refer to the wrap symbol, and consists of the symbol
+ * name with a __wrap_ prefix.
+ */
+typedef struct wrap_sym_node {
+	avl_node_t	wsn_avlnode;	/* AVL book-keeping */
+	const char	*wsn_name;	/* Symbol name: XXX */
+	const char	*wsn_wrapname;	/* Wrap symbol name: __wrap_XXX */
+} WrapSymNode;
+
+
+/*
  * Output file processing structure
  */
 typedef Lword ofl_flag_t;
@@ -304,6 +318,7 @@
 					/*	sloppy_comdat_reloc() */
 	APlist		*ofl_maptext;	/* mapfile added text sections */
 	APlist		*ofl_mapdata;	/* mapfile added data sections */
+	avl_tree_t	*ofl_wrap;	/* -z wrap symbols  */
 };
 
 #define	FLG_OF_DYNAMIC	0x00000001	/* generate dynamic output module */
--- a/usr/src/cmd/sgs/libld/Makefile.com	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/libld/Makefile.com	Fri Sep 18 09:00:12 2009 -0600
@@ -33,14 +33,14 @@
 		libs32.o	files32.o	map32.o		order32.o \
 		outfile32.o	place32.o	relocate32.o	resolve32.o \
 		sections32.o	sunwmove32.o	support32.o	syms32.o \
-		update32.o	unwind32.o	version32.o
+		update32.o	unwind32.o	version32.o	wrap32.o
 
 COMOBJS64 =	args64.o	entry64.o	exit64.o	groups64.o \
 		ldentry64.o	ldlibs64.o	ldmachdep64.o	ldmain64.o \
 		libs64.o	files64.o	map64.o		order64.o \
 		outfile64.o 	place64.o	relocate64.o	resolve64.o \
 		sections64.o	sunwmove64.o	support64.o	syms64.o \
-		update64.o	unwind64.o	version64.o
+		update64.o	unwind64.o	version64.o	wrap64.o
 
 TOOLOBJS =	alist.o		assfail.o	findprime.o	string_table.o \
 		strhash.o
--- a/usr/src/cmd/sgs/libld/common/_libld.h	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/libld/common/_libld.h	Fri Sep 18 09:00:12 2009 -0600
@@ -616,6 +616,7 @@
 #define	ld_vers_promote		ld64_vers_promote
 #define	ld_vers_sym_process	ld64_vers_sym_process
 #define	ld_vers_verify		ld64_vers_verify
+#define	ld_wrap_enter		ld64_wrap_enter
 
 #else
 
@@ -703,6 +704,7 @@
 #define	ld_vers_promote		ld32_vers_promote
 #define	ld_vers_sym_process	ld32_vers_sym_process
 #define	ld_vers_verify		ld32_vers_verify
+#define	ld_wrap_enter		ld32_wrap_enter
 
 #endif
 
@@ -831,6 +833,7 @@
 			    Ofl_desc *);
 extern int		ld_vers_sym_process(Lm_list *, Is_desc *, Ifl_desc *);
 extern int		ld_vers_verify(Ofl_desc *);
+extern WrapSymNode	*ld_wrap_enter(Ofl_desc *, const char *);
 
 extern uintptr_t	add_regsym(Sym_desc *, Ofl_desc *);
 extern Word		hashbkts(Word);
--- a/usr/src/cmd/sgs/libld/common/args.c	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/libld/common/args.c	Fri Sep 18 09:00:12 2009 -0600
@@ -55,6 +55,21 @@
  *    -z nosighandler		suppress the registration of the signal handler
  *				used to manage SIGBUS.
  */
+
+/*
+ * The following flags are committed, and will not be removed, but are
+ * not publically documented, either because they are obsolete, or because
+ * they exist to work around defects in other software and are not of
+ * sufficient interest otherwise.
+ *
+ *    OPTION			MEANING
+ *
+ *    -Wl,...			compiler drivers and configuration tools
+ *				have been known to pass this compiler option
+ *				to ld(1).  Strip off the "-Wl," prefix and
+ *			        process the remainder (...) as a normal option.
+ */
+
 #include	<sys/link.h>
 #include	<stdio.h>
 #include	<fcntl.h>
@@ -198,6 +213,7 @@
 	(void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZT));
 	(void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTO));
 	(void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTW));
+	(void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZWRAP));
 	(void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZV));
 }
 
@@ -1299,6 +1315,17 @@
 					/* Don't report cascading errors */
 					ofl->ofl_ars_gsandx = -1;
 				}
+
+			/*
+			 * If -z wrap is seen, enter the symbol to be wrapped
+			 * into the wrap AVL tree.
+			 */
+			} else if (strncmp(optarg, MSG_ORIG(MSG_ARG_WRAP),
+			    MSG_ARG_WRAP_SIZE) == 0) {
+				if (ld_wrap_enter(ofl,
+				    optarg + MSG_ARG_WRAP_SIZE) == NULL)
+					return (S_ERROR);
+
 			/*
 			 * The following options just need validation as they
 			 * are interpreted on the second pass through the
--- a/usr/src/cmd/sgs/libld/common/ldmain.c	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/libld/common/ldmain.c	Fri Sep 18 09:00:12 2009 -0600
@@ -404,9 +404,8 @@
 	 * our memory consumption and freeing are doing.  We should be able to
 	 * free all the memory that has been allocated as part of the link-edit
 	 * process.
-	 *
-	 * ofl_cleanup(ofl);
 	 */
+	/* ld_ofl_cleanup(ofl); */
 	return (0);
 }
 
--- a/usr/src/cmd/sgs/libld/common/libld.msg	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/libld/common/libld.msg	Fri Sep 18 09:00:12 2009 -0600
@@ -228,6 +228,8 @@
 			 text\n"
 @ MSG_ARG_DETAIL_ZTW	"\t[-z textwarn]\twarn if there are relocations \
 			 against text\n"
+@ MSG_ARG_DETAIL_ZWRAP	"\t[-z wrap=symbol], [-wrap=symbol], [--wrap=symbol]\n\
+			 \t\t\twrap symbol references\n"
 @ MSG_ARG_DETAIL_ZV	"\t[-z verbose]\t\
 			 generate warnings for suspicious processings\n"
 
@@ -652,11 +654,14 @@
 @ MSG_STR_ISALIST	"$ISALIST"
 @ MSG_STR_OSNAME	"$OSNAME"
 @ MSG_STR_OSREL		"$OSREL"
+@ MSG_STR_UU_REAL_U	"__real_"
+@ MSG_STR_UU_WRAP_U	"__wrap_"
 
 @ MSG_FMT_ARMEM		"%s(%s)"
 @ MSG_FMT_COLPATH	"%s:%s"
 @ MSG_FMT_SYMNAM	"`%s'"
 @ MSG_FMT_NULLSYMNAM	"%s[%d]"
+@ MSG_FMT_STRCAT	"%s%s"
 
 @ MSG_PTH_RTLD		"/usr/lib/ld.so.1"
 
@@ -1210,6 +1215,7 @@
 @ MSG_ARG_NOSIGHANDLER	"nosighandler"
 @ MSG_ARG_GLOBAUDIT	"globalaudit"
 @ MSG_ARG_TARGET	"target="
+@ MSG_ARG_WRAP		"wrap="
 @ MSG_ARG_HELP		"help"
 @ MSG_ARG_GROUP		"group"
 @ MSG_ARG_REDUCE	"reduce"
@@ -1242,6 +1248,7 @@
 @ MSG_ARG_T_UNDEF	"-undefined"
 @ MSG_ARG_T_VERSION	"-version"
 @ MSG_ARG_T_WHOLEARC	"-whole-archive"
+@ MSG_ARG_T_WRAP	"-wrap"
 @ MSG_ARG_T_OPAR	"("
 @ MSG_ARG_T_CPAR	")"
 
--- a/usr/src/cmd/sgs/libld/common/syms.c	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/libld/common/syms.c	Fri Sep 18 09:00:12 2009 -0600
@@ -2312,6 +2312,48 @@
 		}
 
 		/*
+		 * The '-z wrap=XXX' option emulates the GNU ld --wrap=XXX
+		 * option. When XXX is the symbol to be wrapped:
+		 *
+		 * -	An undefined reference to XXX is converted to __wrap_XXX
+		 * -	An undefined reference to __real_XXX is converted to XXX
+		 *
+		 * The idea is that the user can supply a wrapper function
+		 * __wrap_XXX that does some work, and then uses the name
+		 * __real_XXX to pass the call on to the real function. The
+		 * wrapper objects are linked with the original unmodified
+		 * objects to produce a wrapped version of the output object.
+		 */
+		if (ofl->ofl_wrap && name[0] && (shndx == SHN_UNDEF)) {
+			WrapSymNode wsn, *wsnp;
+
+			/*
+			 * If this is the __real_XXX form, advance the
+			 * pointer to reference the wrapped name.
+			 */
+			wsn.wsn_name = name;
+			if ((*name == '_') &&
+			    (strncmp(name, MSG_ORIG(MSG_STR_UU_REAL_U),
+			    MSG_STR_UU_REAL_U_SIZE) == 0))
+				wsn.wsn_name += MSG_STR_UU_REAL_U_SIZE;
+
+			/*
+			 * Is this symbol in the wrap AVL tree? If so, map
+			 * XXX to __wrap_XXX, and __real_XXX to XXX. Note that
+			 * wsn.wsn_name will equal the current value of name
+			 * if the __real_ prefix is not present.
+			 */
+			if ((wsnp = avl_find(ofl->ofl_wrap, &wsn, 0)) != NULL) {
+				const char *old_name = name;
+
+				name = (wsn.wsn_name == name) ?
+				    wsnp->wsn_wrapname : wsn.wsn_name;
+				DBG_CALL(Dbg_syms_wrap(ofl->ofl_lml, ndx,
+				    old_name, name));
+			}
+		}
+
+		/*
 		 * Determine and validate the symbols binding.
 		 */
 		bind = ELF_ST_BIND(sym->st_info);
--- a/usr/src/cmd/sgs/libld/common/util.c	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/libld/common/util.c	Fri Sep 18 09:00:12 2009 -0600
@@ -279,13 +279,48 @@
 }
 
 /*
+ * The GNU ld '-wrap=XXX' and '--wrap=XXX' options correspond to our
+ * '-z wrap=XXX'. When str2chr() does this conversion, we end up with
+ * the return character set to 'z' and optarg set to 'XXX'. This callback
+ * changes optarg to include the missing wrap= prefix.
+ *
+ * exit:
+ *	Returns c on success, or '?' on error.
+ */
+static int
+str2chr_wrap_cb(int c)
+{
+	char    *str;
+	size_t  len = MSG_ARG_WRAP_SIZE + strlen(optarg) + 1;
+
+	if ((str = libld_malloc(len)) == NULL)
+		return ('?');
+	(void) snprintf(str, len, MSG_ORIG(MSG_FMT_STRCAT),
+	    MSG_ORIG(MSG_ARG_WRAP), optarg);
+	optarg = str;
+	return (c);
+}
+
+/*
  * Determine whether this string, possibly with an associated option, should be
  * translated to an option character.  If so, update the optind and optarg
  * as described for short options in getopt(3c).
+ *
+ * entry:
+ *	lml - Link map list for debug messages
+ *	ndx - Starting optind for current item
+ *	argc, argv - Command line arguments
+ *	arg - Option to be examined
+ *	c, opt - Option character (c) and corresponding long name (opt)
+ *	optsz - 0 if option does not accept a value. If option does
+ *		accept a value, strlen(opt), giving the offset to the
+ *		value if the option and value are combined in one string.
+ *	cbfunc - NULL, or pointer to function to call if a translation is
+ *		successful.
  */
 static int
 str2chr(Lm_list *lml, int ndx, int argc, char **argv, char *arg, int c,
-    const char *opt, size_t optsz)
+    const char *opt, size_t optsz, int cbfunc(int))
 {
 	if (optsz == 0) {
 		/*
@@ -331,6 +366,10 @@
 					return ('?');
 			}
 		}
+
+		if (cbfunc != NULL)
+			c = (*cbfunc)(c);
+
 		return (c);
 	}
 	return (0);
@@ -362,20 +401,28 @@
 			/* Translate -rpath <optarg> to -R <optarg> */
 			if ((c = str2chr(lml, ndx, argc, argv, arg, 'R',
 			    MSG_ORIG(MSG_ARG_T_RPATH),
-			    MSG_ARG_T_RPATH_SIZE)) != 0) {
+			    MSG_ARG_T_RPATH_SIZE, NULL)) != 0) {
 				return (c);
 			}
 			break;
 		case 's':
 			/* Translate -shared to -G */
 			if ((c = str2chr(lml, ndx, argc, argv, arg, 'G',
-			    MSG_ORIG(MSG_ARG_T_SHARED), 0)) != 0) {
+			    MSG_ORIG(MSG_ARG_T_SHARED), 0, NULL)) != 0) {
 				return (c);
 
 			/* Translate -soname <optarg> to -h <optarg> */
 			} else if ((c = str2chr(lml, ndx, argc, argv, arg, 'h',
 			    MSG_ORIG(MSG_ARG_T_SONAME),
-			    MSG_ARG_T_SONAME_SIZE)) != 0) {
+			    MSG_ARG_T_SONAME_SIZE, NULL)) != 0) {
+				return (c);
+			}
+			break;
+		case 'w':
+			/* Translate -wrap to -z wrap= */
+			if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
+			    MSG_ORIG(MSG_ARG_T_WRAP) + 1,
+			    MSG_ARG_T_WRAP_SIZE - 1, str2chr_wrap_cb)) != 0) {
 				return (c);
 			}
 			break;
@@ -384,7 +431,8 @@
 			 * Translate -( to -z rescan-start
 			 */
 			if ((c = str2chr(lml, ndx, argc, argv,
-			    arg, 'z', MSG_ORIG(MSG_ARG_T_OPAR), 0)) != 0) {
+			    arg, 'z', MSG_ORIG(MSG_ARG_T_OPAR), 0, NULL)) !=
+			    0) {
 				optarg = (char *)MSG_ORIG(MSG_ARG_RESCAN_START);
 				return (c);
 			}
@@ -394,7 +442,8 @@
 			 * Translate -) to -z rescan-end
 			 */
 			if ((c = str2chr(lml, ndx, argc, argv,
-			    arg, 'z', MSG_ORIG(MSG_ARG_T_CPAR), 0)) != 0) {
+			    arg, 'z', MSG_ORIG(MSG_ARG_T_CPAR), 0, NULL)) !=
+			    0) {
 				optarg = (char *)MSG_ORIG(MSG_ARG_RESCAN_END);
 				return (c);
 			}
@@ -407,7 +456,8 @@
 				 * -zmuldefs
 				 */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
-				    MSG_ORIG(MSG_ARG_T_MULDEFS), 0)) != 0) {
+				    MSG_ORIG(MSG_ARG_T_MULDEFS), 0, NULL)) !=
+				    0) {
 					optarg =
 					    (char *)MSG_ORIG(MSG_ARG_MULDEFS);
 					return (c);
@@ -418,7 +468,7 @@
 				 */
 				} else if ((c = str2chr(lml, argc, ndx, argv,
 				    arg, 'f', MSG_ORIG(MSG_ARG_T_AUXFLTR),
-				    MSG_ARG_T_AUXFLTR_SIZE)) != 0) {
+				    MSG_ARG_T_AUXFLTR_SIZE, NULL)) != 0) {
 					return (c);
 				}
 				break;
@@ -429,7 +479,7 @@
 				 */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'I',
 				    MSG_ORIG(MSG_ARG_T_INTERP),
-				    MSG_ARG_T_INTERP_SIZE)) != 0) {
+				    MSG_ARG_T_INTERP_SIZE, NULL)) != 0) {
 					return (c);
 				}
 				break;
@@ -437,15 +487,15 @@
 				/* Translate --entry <optarg> to -e <optarg> */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'e',
 				    MSG_ORIG(MSG_ARG_T_ENTRY),
-				    MSG_ARG_T_ENTRY_SIZE)) != 0) {
+				    MSG_ARG_T_ENTRY_SIZE, NULL)) != 0) {
 					return (c);
 				}
 				/*
 				 * Translate --end-group to -z rescan-end
 				 */
 				if ((c = str2chr(lml, ndx, argc, argv,
-				    arg, 'z',
-				    MSG_ORIG(MSG_ARG_T_ENDGROUP), 0)) != 0) {
+				    arg, 'z', MSG_ORIG(MSG_ARG_T_ENDGROUP),
+				    0, NULL)) != 0) {
 					optarg = (char *)
 					    MSG_ORIG(MSG_ARG_RESCAN_END);
 					return (c);
@@ -455,14 +505,15 @@
 				/* Translate --filter <optarg> to -F <optarg> */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'F',
 				    MSG_ORIG(MSG_ARG_T_STDFLTR),
-				    MSG_ARG_T_STDFLTR_SIZE)) != 0) {
+				    MSG_ARG_T_STDFLTR_SIZE, NULL)) != 0) {
 					return (c);
 				}
 				break;
 			case 'h':
 				/* Translate --help to -zhelp */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
-				    MSG_ORIG(MSG_ARG_T_HELP), 0)) != 0) {
+				    MSG_ORIG(MSG_ARG_T_HELP), 0, NULL)) !=
+				    0) {
 					optarg = (char *)MSG_ORIG(MSG_ARG_HELP);
 					return (c);
 				}
@@ -473,7 +524,7 @@
 				 */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'l',
 				    MSG_ORIG(MSG_ARG_T_LIBRARY),
-				    MSG_ARG_T_LIBRARY_SIZE)) != 0) {
+				    MSG_ARG_T_LIBRARY_SIZE, NULL)) != 0) {
 					return (c);
 
 				/*
@@ -482,14 +533,15 @@
 				 */
 				} else if ((c = str2chr(lml, ndx, argc, argv,
 				    arg, 'L', MSG_ORIG(MSG_ARG_T_LIBPATH),
-				    MSG_ARG_T_LIBPATH_SIZE)) != 0) {
+				    MSG_ARG_T_LIBPATH_SIZE, NULL)) != 0) {
 					return (c);
 				}
 				break;
 			case 'n':
 				/* Translate --no-undefined to -zdefs */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
-				    MSG_ORIG(MSG_ARG_T_NOUNDEF), 0)) != 0) {
+				    MSG_ORIG(MSG_ARG_T_NOUNDEF), 0, NULL)) !=
+				    0) {
 					optarg = (char *)MSG_ORIG(MSG_ARG_DEFS);
 					return (c);
 
@@ -498,8 +550,8 @@
 				 * -z defaultextract
 				 */
 				} else if ((c = str2chr(lml, ndx, argc, argv,
-				    arg, 'z',
-				    MSG_ORIG(MSG_ARG_T_NOWHOLEARC), 0)) != 0) {
+				    arg, 'z', MSG_ORIG(MSG_ARG_T_NOWHOLEARC),
+				    0, NULL)) != 0) {
 					optarg =
 					    (char *)MSG_ORIG(MSG_ARG_DFLEXTRT);
 					return (c);
@@ -509,29 +561,31 @@
 				/* Translate --output <optarg> to -o <optarg> */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'o',
 				    MSG_ORIG(MSG_ARG_T_OUTPUT),
-				    MSG_ARG_T_OUTPUT_SIZE)) != 0) {
+				    MSG_ARG_T_OUTPUT_SIZE, NULL)) != 0) {
 					return (c);
 				}
 				break;
 			case 'r':
 				/* Translate --relocatable to -r */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'r',
-				    MSG_ORIG(MSG_ARG_T_RELOCATABLE), 0)) != 0) {
+				    MSG_ORIG(MSG_ARG_T_RELOCATABLE), 0,
+				    NULL)) != 0) {
 					return (c);
 				}
 				break;
 			case 's':
 				/* Translate --strip-all to -s */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 's',
-				    MSG_ORIG(MSG_ARG_T_STRIP), 0)) != 0) {
+				    MSG_ORIG(MSG_ARG_T_STRIP), 0, NULL)) !=
+				    0) {
 					return (c);
 				}
 				/*
 				 * Translate --start-group to -z rescan-start
 				 */
 				if ((c = str2chr(lml, ndx, argc, argv,
-				    arg, 'z',
-				    MSG_ORIG(MSG_ARG_T_STARTGROUP), 0)) != 0) {
+				    arg, 'z', MSG_ORIG(MSG_ARG_T_STARTGROUP),
+				    0, NULL)) != 0) {
 					optarg = (char *)
 					    MSG_ORIG(MSG_ARG_RESCAN_START);
 					return (c);
@@ -544,14 +598,15 @@
 				 */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'u',
 				    MSG_ORIG(MSG_ARG_T_UNDEF),
-				    MSG_ARG_T_UNDEF_SIZE)) != 0) {
+				    MSG_ARG_T_UNDEF_SIZE, NULL)) != 0) {
 					return (c);
 				}
 				break;
 			case 'v':
 				/* Translate --version to -V */
 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'V',
-				    MSG_ORIG(MSG_ARG_T_VERSION), 0)) != 0) {
+				    MSG_ORIG(MSG_ARG_T_VERSION), 0, NULL)) !=
+				    0) {
 					return (c);
 				}
 				break;
@@ -560,17 +615,27 @@
 				 * Translate --whole-archive to -z alltextract
 				 */
 				if ((c = str2chr(lml, ndx, argc, argv,
-				    arg, 'z',
-				    MSG_ORIG(MSG_ARG_T_WHOLEARC), 0)) != 0) {
+				    arg, 'z', MSG_ORIG(MSG_ARG_T_WHOLEARC),
+				    0, NULL)) != 0) {
 					optarg =
 					    (char *)MSG_ORIG(MSG_ARG_ALLEXTRT);
 					return (c);
 				}
+				/*
+				 * Translate --wrap to -z wrap=
+				 */
+				if ((c = str2chr(lml, ndx, argc, argv,
+				    arg, 'z', MSG_ORIG(MSG_ARG_T_WRAP),
+				    MSG_ARG_T_WRAP_SIZE, str2chr_wrap_cb)) !=
+				    0) {
+					return (c);
+				}
 				break;
 			}
 			break;
 		}
 	}
+
 	if ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != -1) {
 		/*
 		 * It is possible that a "-Wl," argument has been used to
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/sgs/libld/common/wrap.c	Fri Sep 18 09:00:12 2009 -0600
@@ -0,0 +1,131 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "msg.h"
+#include "_libld.h"
+
+/*
+ * GNU ld --wrap support, also known as -z wrap.
+ *
+ * We maintain an AVL tree of wrapped symbol names. Every undefined
+ * symbol is tested against this tree, and those that match have
+ * their names modified to produce the wrapping effect:
+ *
+ * -	An undefined reference to XXX is converted to __wrap_XXX
+ * -	An undefined reference to __real_XXX is converted to XXX
+ *
+ * This operation has a cost, but that is mitigated by two factors:
+ *
+ * -	This is a test feature, not used for production code, so somewhat
+ *	longer link times are tolerable.
+ * -	The cost of this feature is only paid when it is used. Otherwise,
+ *	the sole overhead is the cost of testing the NULL AVL tree pointer
+ *	during symbol processing.
+ */
+
+
+/*
+ * AVL comparison function for WrapSymNode items.
+ *
+ * entry:
+ *	n1, n2 - pointers to nodes to be compared
+ *
+ * exit:
+ *	Returns -1 if (n1 < n2), 0 if they are equal, and 1 if (n1 > n2)
+ */
+static int
+wrap_cmp(const void *n1, const void *n2)
+{
+	int		rc;
+
+	rc = strcmp(((WrapSymNode *)n1)->wsn_name,
+	    ((WrapSymNode *)n2)->wsn_name);
+
+	if (rc > 0)
+		return (1);
+	if (rc < 0)
+		return (-1);
+	return (0);
+}
+
+/*
+ * Enter a -z wrap symbol into the ofl_wrap AVL tree
+ *
+ * entry:
+ *	ofl - Output file descriptor
+ *	name - Name of symbol to be entered. Caller must ensure that
+ *		memory used to hold name remains available for the life
+ *		of the link-edit process.
+ *
+ * exit:
+ *	On success, updates ofl->wrap_cache with a pointer to the
+ *	resulting WrapSymNode, and returns that pointer. On failure,
+ *	returns NULL.
+ */
+WrapSymNode *
+ld_wrap_enter(Ofl_desc *ofl, const char *name)
+{
+	WrapSymNode	*wsnp, wsn;
+	avl_index_t	where;
+	size_t		name_len, wrapname_len;
+	char		*tmpname;
+
+	/* If this is the first wrap symbol, create the AVL tree */
+	if (ofl->ofl_wrap == NULL) {
+		ofl->ofl_wrap = libld_calloc(1, sizeof (*ofl->ofl_wrap));
+		if (ofl->ofl_wrap == NULL)
+			return (NULL);
+		avl_create(ofl->ofl_wrap, wrap_cmp, sizeof (WrapSymNode),
+		    SGSOFFSETOF(WrapSymNode, wsn_avlnode));
+	}
+
+	/* Have we already entered this one? */
+	wsn.wsn_name = name;
+	if ((wsnp = avl_find(ofl->ofl_wrap, &wsn, &where)) != NULL)
+		return (wsnp);
+
+	/*
+	 * Allocate a new node, along with room for the wrapped name.
+	 * Since strings have byte alignment, we can allocate it immediately
+	 * following the AVL node without the need for alignment padding.
+	 */
+	name_len = strlen(wsn.wsn_name);
+	wrapname_len = MSG_STR_UU_WRAP_U_SIZE + name_len + 1;
+	if ((wsnp = libld_calloc(1, sizeof (*wsnp) + wrapname_len)) == NULL)
+		return (NULL);
+	wsnp->wsn_name = name;
+
+	wsnp->wsn_wrapname = tmpname = (char *)(wsnp + 1);
+	(void) snprintf(tmpname, wrapname_len, MSG_ORIG(MSG_FMT_STRCAT),
+	    MSG_ORIG(MSG_STR_UU_WRAP_U), name);
+
+	/* Insert the new node */
+	avl_insert(ofl->ofl_wrap, wsnp, where);
+	return (wsnp);
+}
--- a/usr/src/cmd/sgs/liblddbg/common/liblddbg.msg	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/liblddbg/common/liblddbg.msg	Fri Sep 18 09:00:12 2009 -0600
@@ -953,6 +953,7 @@
 			 discarded file=%s"
 @ MSG_SYM_DISCARD_DUP	"symbol[%d]=%s;  discarded duplicate: originates from \
 			 file=%s"
+@ MSG_SYM_WRAP		"symbol[%d]=%s renamed to %s (-z wrap)"
 
 @ MSG_SYM_AOUT		"symbol=%s;  (original AOUT name)"
 @ MSG_SYM_LOOKUP	"symbol=%s;  lookup in file=%s  [ %s ]"
--- a/usr/src/cmd/sgs/liblddbg/common/llib-llddbg	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/liblddbg/common/llib-llddbg	Fri Sep 18 09:00:12 2009 -0600
@@ -404,6 +404,8 @@
 void	Dbg64_syms_updated(Ofl_desc *, Sym_desc *, const char *);
 void	Dbg32_syms_up_title(Lm_list *);
 void	Dbg64_syms_up_title(Lm_list *);
+void	Dbg32_syms_wrap(Lm_list *, Elf32_Word, const char *, const char *);
+void	Dbg64_syms_wrap(Lm_list *, Elf64_Word, const char *, const char *);
 
 void	Dbg32_util_broadcast(Rt_map *);
 void	Dbg64_util_broadcast(Rt_map *);
--- a/usr/src/cmd/sgs/liblddbg/common/mapfile-vers	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/liblddbg/common/mapfile-vers	Fri Sep 18 09:00:12 2009 -0600
@@ -41,7 +41,7 @@
 # MAPFILE HEADER END
 #
 
-SUNWprivate_4.73 {
+SUNWprivate_4.74 {
 	global:
 		dbg_desc = NODIRECT;	# interposed - ld.so.1(1)
 		dbg_print = NODIRECT;	# interposed - ld(1) and ld.so.1(1)
@@ -399,6 +399,8 @@
 		Dbg64_syms_updated;
 		Dbg32_syms_up_title;
 		Dbg64_syms_up_title;
+		Dbg32_syms_wrap;
+		Dbg64_syms_wrap;
 
 		Dbg_tls_modactivity;
 		Dbg_tls_static_block;
--- a/usr/src/cmd/sgs/liblddbg/common/syms.c	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/liblddbg/common/syms.c	Fri Sep 18 09:00:12 2009 -0600
@@ -284,6 +284,16 @@
 }
 
 void
+Dbg_syms_wrap(Lm_list *lml, Word ndx, const char *orig_name, const char *name)
+{
+	if (DBG_NOTCLASS(DBG_C_SYMBOLS))
+		return;
+
+	dbg_print(lml, MSG_INTL(MSG_SYM_WRAP), EC_WORD(ndx),
+	    Dbg_demangle_name(orig_name), Dbg_demangle_name(name));
+}
+
+void
 Dbg_syms_sec_title(Lm_list *lml)
 {
 	if (DBG_NOTCLASS(DBG_C_SYMBOLS))
--- a/usr/src/cmd/sgs/packages/common/SUNWonld-README	Fri Sep 18 01:55:30 2009 -0700
+++ b/usr/src/cmd/sgs/packages/common/SUNWonld-README	Fri Sep 18 09:00:12 2009 -0600
@@ -1531,3 +1531,5 @@
 6834197 ld pukes when given an empty plate
 6516644 per-symbol filtering shouldn't be allowed in executables
 6878605 ld should accept '%' syntax when matching input SHT_PROGBITS sections
+6850768 ld option to autogenerate wrappers/interposers similar to GNU ld --wrap
+	PSARC/2009/493 ld -z wrap option