xref: /illumos-gate/usr/src/cmd/sgs/ld/common/ld.c (revision 012e6ce7)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
55aefb655Srie  * Common Development and Distribution License (the "License").
65aefb655Srie  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
215aefb655Srie 
227c478bd9Sstevel@tonic-gate /*
231007fd6fSAli Bahrami  * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
24*012e6ce7SDan Cross  * Copyright 2023 Oxide Computer Company
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
27*012e6ce7SDan Cross #include <ctype.h>
28*012e6ce7SDan Cross #include <stdio.h>
29*012e6ce7SDan Cross #include <stdlib.h>
30*012e6ce7SDan Cross #include <unistd.h>
31*012e6ce7SDan Cross #include <stdarg.h>
32*012e6ce7SDan Cross #include <stdbool.h>
33*012e6ce7SDan Cross #include <string.h>
34*012e6ce7SDan Cross #include <strings.h>
35*012e6ce7SDan Cross #include <errno.h>
36*012e6ce7SDan Cross #include <fcntl.h>
37*012e6ce7SDan Cross #include <libintl.h>
38*012e6ce7SDan Cross #include <locale.h>
39*012e6ce7SDan Cross #include <fcntl.h>
40*012e6ce7SDan Cross #include <ar.h>
41*012e6ce7SDan Cross #include <gelf.h>
42*012e6ce7SDan Cross #include "conv.h"
43*012e6ce7SDan Cross #include "libld.h"
44*012e6ce7SDan Cross #include "machdep.h"
45*012e6ce7SDan Cross #include "msg.h"
46*012e6ce7SDan Cross 
47*012e6ce7SDan Cross typedef int (*ld_main_f)(int, char *[], Half);
48*012e6ce7SDan Cross 
49*012e6ce7SDan Cross static const char *errstr[ERR_NUM];
50*012e6ce7SDan Cross 
51*012e6ce7SDan Cross static void
init_strings(void)52*012e6ce7SDan Cross init_strings(void)
53*012e6ce7SDan Cross {
54*012e6ce7SDan Cross 	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
55*012e6ce7SDan Cross 	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
56*012e6ce7SDan Cross 
57*012e6ce7SDan Cross 	/*
58*012e6ce7SDan Cross 	 * For error types we issue a prefix for, make sure the necessary
59*012e6ce7SDan Cross 	 * string has been internationalized and is ready.
60*012e6ce7SDan Cross 	 */
61*012e6ce7SDan Cross 	errstr[ERR_WARNING_NF] = MSG_INTL(MSG_ERR_WARNING);
62*012e6ce7SDan Cross 	errstr[ERR_WARNING] = MSG_INTL(MSG_ERR_WARNING);
63*012e6ce7SDan Cross 	errstr[ERR_GUIDANCE] = MSG_INTL(MSG_ERR_GUIDANCE);
64*012e6ce7SDan Cross 	errstr[ERR_FATAL] = MSG_INTL(MSG_ERR_FATAL);
65*012e6ce7SDan Cross 	errstr[ERR_ELF] = MSG_INTL(MSG_ERR_ELF);
66*012e6ce7SDan Cross }
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /*
69*012e6ce7SDan Cross  * Returns a duplicate of the given environment variable, with
70*012e6ce7SDan Cross  * leading whitespace stripped off.  Returns NULL if the variable
71*012e6ce7SDan Cross  * is not in the environment, or if it is empty.  Allocation
72*012e6ce7SDan Cross  * failure terminates the program.
737c478bd9Sstevel@tonic-gate  */
74*012e6ce7SDan Cross static char *
getenv_nonempty(const char * name)75*012e6ce7SDan Cross getenv_nonempty(const char *name)
76*012e6ce7SDan Cross {
77*012e6ce7SDan Cross 	char *var;
78*012e6ce7SDan Cross 
79*012e6ce7SDan Cross 	var = getenv(name);
80*012e6ce7SDan Cross 	if (var == NULL)
81*012e6ce7SDan Cross 		return (NULL);
82*012e6ce7SDan Cross 	while (isspace(*var))
83*012e6ce7SDan Cross 		var++;
84*012e6ce7SDan Cross 	if (*var == '\0')
85*012e6ce7SDan Cross 		return (NULL);
86*012e6ce7SDan Cross 	var = strdup(var);
87*012e6ce7SDan Cross 	if (var == NULL) {
88*012e6ce7SDan Cross 		eprintf(0, ERR_FATAL, MSG_INTL(MSG_SYS_ALLOC), strerror(errno));
89*012e6ce7SDan Cross 		exit(EXIT_FAILURE);
90*012e6ce7SDan Cross 	}
91*012e6ce7SDan Cross 
92*012e6ce7SDan Cross 	return (var);
93*012e6ce7SDan Cross }
94*012e6ce7SDan Cross 
95*012e6ce7SDan Cross /*
96*012e6ce7SDan Cross  * Like strsep(3), but using `isspace` instead of
97*012e6ce7SDan Cross  * a separator string.
98*012e6ce7SDan Cross  */
99*012e6ce7SDan Cross static char *
strsep_ws(char ** strp)100*012e6ce7SDan Cross strsep_ws(char **strp)
101*012e6ce7SDan Cross {
102*012e6ce7SDan Cross 	char *str, *s;
103*012e6ce7SDan Cross 
104*012e6ce7SDan Cross 	str = *strp;
105*012e6ce7SDan Cross 	if (*str == '\0')
106*012e6ce7SDan Cross 		return (NULL);
107*012e6ce7SDan Cross 	s = str;
108*012e6ce7SDan Cross 	while (*s != '\0' && !isspace(*s))
109*012e6ce7SDan Cross 		s++;
110*012e6ce7SDan Cross 	if (*s != '\0')
111*012e6ce7SDan Cross 		*s++ = '\0';
112*012e6ce7SDan Cross 	*strp = s;
113*012e6ce7SDan Cross 
114*012e6ce7SDan Cross 	return (str);
115*012e6ce7SDan Cross }
1167c478bd9Sstevel@tonic-gate 
11756e2cc86SAli Bahrami /*
11856e2cc86SAli Bahrami  * We examine ELF objects, and archives containing ELF objects, in order
11956e2cc86SAli Bahrami  * to determine the ELFCLASS of the resulting object and/or the linker to be
12056e2cc86SAli Bahrami  * used. We want to avoid the overhead of libelf for this, at least until
12156e2cc86SAli Bahrami  * we are certain that we need it, so we start by reading bytes from
12256e2cc86SAli Bahrami  * the beginning of the file. This type defines the buffer used to read
12356e2cc86SAli Bahrami  * these initial bytes.
12456e2cc86SAli Bahrami  *
12556e2cc86SAli Bahrami  * A plain ELF object will start with an ELF header, whereas an archive
12656e2cc86SAli Bahrami  * starts with a magic string (ARMAG) that is SARMAG bytes long. Any valid
12756e2cc86SAli Bahrami  * ELF file or archive will contain more bytes than this buffer, so any
12856e2cc86SAli Bahrami  * file shorter than this can be safely assummed not to be of interest.
12956e2cc86SAli Bahrami  *
13056e2cc86SAli Bahrami  * The ELF header for ELFCLASS32 and ELFCLASS64 are identical up through the
13156e2cc86SAli Bahrami  * the e_version field, and all the information we require is found in this
13256e2cc86SAli Bahrami  * common prefix. Furthermore, this cannot change, as the layout of an ELF
13356e2cc86SAli Bahrami  * header is fixed by the ELF ABI. Hence, the ehdr part of this union is
13456e2cc86SAli Bahrami  * not a full ELF header, but only the class-independent prefix that we need.
13556e2cc86SAli Bahrami  *
13656e2cc86SAli Bahrami  * As this is a raw (non-libelf) read, we are responsible for handling any
13756e2cc86SAli Bahrami  * byte order difference between the object and the system running this
13856e2cc86SAli Bahrami  * program when we read any datum larger than a byte (i.e. e_machine) from
13956e2cc86SAli Bahrami  * this header.
14056e2cc86SAli Bahrami  */
14156e2cc86SAli Bahrami typedef union {
14256e2cc86SAli Bahrami 	struct {	/* Must match start of ELFxx_Ehdr in <sys/elf.h> */
14356e2cc86SAli Bahrami 		uchar_t		e_ident[EI_NIDENT];	/* ident bytes */
14456e2cc86SAli Bahrami 		Half		e_type;			/* file type */
14556e2cc86SAli Bahrami 		Half		e_machine;		/* target machine */
14656e2cc86SAli Bahrami 	} ehdr;
14756e2cc86SAli Bahrami 	char			armag[SARMAG];
14856e2cc86SAli Bahrami } FILE_HDR;
14956e2cc86SAli Bahrami 
1505aefb655Srie /*
1515aefb655Srie  * Print a message to stdout
152*012e6ce7SDan Cross  * The lml argument is only meaningful for diagnostics sent to ld.so.1,
153*012e6ce7SDan Cross  * and is ignored here.
1545aefb655Srie  */
1555aefb655Srie void
veprintf(Lm_list * lml __unused,Error error,const char * format,va_list args)156*012e6ce7SDan Cross veprintf(Lm_list *lml __unused, Error error, const char *format, va_list args)
1575aefb655Srie {
158*012e6ce7SDan Cross 	const char *err;
1591007fd6fSAli Bahrami 
1601007fd6fSAli Bahrami 	/* If strings[] element for our error type is non-NULL, issue prefix */
161*012e6ce7SDan Cross 	err = errstr[error];
162*012e6ce7SDan Cross 	if (err != NULL)
163*012e6ce7SDan Cross 		(void) fprintf(stderr, "%s%s", MSG_ORIG(MSG_STR_LDDIAG), err);
1645aefb655Srie 	(void) vfprintf(stderr, format, args);
165*012e6ce7SDan Cross 
1665aefb655Srie 	if (error == ERR_ELF) {
167*012e6ce7SDan Cross 		int elferr;
1685aefb655Srie 
169*012e6ce7SDan Cross 		elferr = elf_errno();
170*012e6ce7SDan Cross 		if (elferr != 0) {
171*012e6ce7SDan Cross 			err = elf_errmsg(elferr);
172*012e6ce7SDan Cross 			(void) fprintf(stderr, MSG_ORIG(MSG_STR_ELFDIAG), err);
173*012e6ce7SDan Cross 		}
1745aefb655Srie 	}
1755aefb655Srie 	(void) fprintf(stderr, MSG_ORIG(MSG_STR_NL));
1765aefb655Srie 	(void) fflush(stderr);
1771007fd6fSAli Bahrami }
1781007fd6fSAli Bahrami 
1791007fd6fSAli Bahrami /*
180*012e6ce7SDan Cross  * Print a message to stderr
1811007fd6fSAli Bahrami  */
1821007fd6fSAli Bahrami /* VARARGS3 */
1831007fd6fSAli Bahrami void
eprintf(Lm_list * lml,Error error,const char * format,...)1841007fd6fSAli Bahrami eprintf(Lm_list *lml, Error error, const char *format, ...)
1851007fd6fSAli Bahrami {
1861007fd6fSAli Bahrami 	va_list	args;
1871007fd6fSAli Bahrami 
1881007fd6fSAli Bahrami 	va_start(args, format);
1891007fd6fSAli Bahrami 	veprintf(lml, error, format, args);
1905aefb655Srie 	va_end(args);
1915aefb655Srie }
1925aefb655Srie 
1937c478bd9Sstevel@tonic-gate 
19456e2cc86SAli Bahrami /*
19556e2cc86SAli Bahrami  * Examine the first object in an archive to determine its ELFCLASS
19656e2cc86SAli Bahrami  * and machine type.
19756e2cc86SAli Bahrami  *
19856e2cc86SAli Bahrami  * entry:
19956e2cc86SAli Bahrami  *	fd - Open file descriptor for file
20056e2cc86SAli Bahrami  *	elf - libelf ELF descriptor
20156e2cc86SAli Bahrami  *	class_ret, mach_ret - Address of variables to receive ELFCLASS
20256e2cc86SAli Bahrami  *		and machine type.
20356e2cc86SAli Bahrami  *
20456e2cc86SAli Bahrami  * exit:
20556e2cc86SAli Bahrami  *	On success, *class_ret and *mach_ret are filled in, and True (1)
20656e2cc86SAli Bahrami  *	is returned. On failure, False (0) is returned.
20756e2cc86SAli Bahrami  */
208*012e6ce7SDan Cross static bool
archive(int fd,Elf * elf,uchar_t * class_ret,Half * mach_ret)20956e2cc86SAli Bahrami archive(int fd, Elf *elf, uchar_t *class_ret, Half *mach_ret)
21056e2cc86SAli Bahrami {
211*012e6ce7SDan Cross 	Elf_Cmd cmd;
212*012e6ce7SDan Cross 	Elf *nelf;
21356e2cc86SAli Bahrami 
21456e2cc86SAli Bahrami 	/*
21556e2cc86SAli Bahrami 	 * Process each item within the archive until we find the first
21656e2cc86SAli Bahrami 	 * ELF object, or alternatively another archive to recurse into.
21756e2cc86SAli Bahrami 	 * Stop after analyzing the first plain object found.
21856e2cc86SAli Bahrami 	 */
219*012e6ce7SDan Cross 	for (cmd = ELF_C_READ, nelf = NULL;
220*012e6ce7SDan Cross 	    (nelf = elf_begin(fd, cmd, elf)) != NULL;
221*012e6ce7SDan Cross 	    cmd = elf_next(nelf), (void) elf_end(nelf)) {
222*012e6ce7SDan Cross 		Elf_Arhdr *arhdr = elf_getarhdr(nelf);
223*012e6ce7SDan Cross 
224*012e6ce7SDan Cross 		if (arhdr == NULL)
225*012e6ce7SDan Cross 			return (false);
226*012e6ce7SDan Cross 		if (*arhdr->ar_name == '/')
227*012e6ce7SDan Cross 			continue;
228*012e6ce7SDan Cross 		switch (elf_kind(nelf)) {
229*012e6ce7SDan Cross 		case ELF_K_AR:
230*012e6ce7SDan Cross 			if (archive(fd, nelf, class_ret, mach_ret))
231*012e6ce7SDan Cross 				return (true);
232*012e6ce7SDan Cross 			break;
233*012e6ce7SDan Cross 		case ELF_K_ELF:
234*012e6ce7SDan Cross 			if (gelf_getclass(nelf) == ELFCLASS64) {
235*012e6ce7SDan Cross 				Elf64_Ehdr *ehdr = elf64_getehdr(nelf);
236*012e6ce7SDan Cross 
237*012e6ce7SDan Cross 				if (ehdr == NULL)
238*012e6ce7SDan Cross 					continue;
239*012e6ce7SDan Cross 				*class_ret = ehdr->e_ident[EI_CLASS];
240*012e6ce7SDan Cross 				*mach_ret = ehdr->e_machine;
241*012e6ce7SDan Cross 			} else {
242*012e6ce7SDan Cross 				Elf32_Ehdr *ehdr = elf32_getehdr(nelf);
243*012e6ce7SDan Cross 
244*012e6ce7SDan Cross 				if (ehdr == NULL)
245*012e6ce7SDan Cross 					continue;
246*012e6ce7SDan Cross 				*class_ret = ehdr->e_ident[EI_CLASS];
247*012e6ce7SDan Cross 				*mach_ret = ehdr->e_machine;
24856e2cc86SAli Bahrami 			}
249*012e6ce7SDan Cross 			return (true);
25056e2cc86SAli Bahrami 		}
25156e2cc86SAli Bahrami 	}
25256e2cc86SAli Bahrami 
253*012e6ce7SDan Cross 	return (false);
25456e2cc86SAli Bahrami }
25556e2cc86SAli Bahrami 
2567c478bd9Sstevel@tonic-gate /*
257ba2be530Sab  * Determine:
25856e2cc86SAli Bahrami  *	- ELFCLASS of resulting object (class)
259ba2be530Sab  *	- ELF machine type of resulting object (m_mach)
26056e2cc86SAli Bahrami  *
26156e2cc86SAli Bahrami  * In order of priority, we determine this information as follows:
26256e2cc86SAli Bahrami  *
2636446bd46SRichard Lowe  * -	Command line options (-32, -64 -z target).
26456e2cc86SAli Bahrami  * -	From the first plain object seen on the command line. (This is
26556e2cc86SAli Bahrami  *	by far the most common case.)
26656e2cc86SAli Bahrami  * -	From the first object contained within the first archive
26756e2cc86SAli Bahrami  *	on the command line.
26856e2cc86SAli Bahrami  * -	If all else fails, we assume a 32-bit object for the native machine.
26956e2cc86SAli Bahrami  *
27056e2cc86SAli Bahrami  * entry:
27156e2cc86SAli Bahrami  *	argc, argv - Command line argument vector
27256e2cc86SAli Bahrami  *	class_ret - Address of variable to receive ELFCLASS of output object
2737c478bd9Sstevel@tonic-gate  */
274*012e6ce7SDan Cross static ld_main_f
process_args(int argc,char * argv[],uchar_t * class_ret,Half * mach)275*012e6ce7SDan Cross process_args(int argc, char *argv[], uchar_t *class_ret, Half *mach)
2767c478bd9Sstevel@tonic-gate {
277*012e6ce7SDan Cross 	Half mach32 = EM_NONE;
278*012e6ce7SDan Cross 	Half mach64 = EM_NONE;
279*012e6ce7SDan Cross 	bool ar_found = false;
280*012e6ce7SDan Cross 	uint8_t class = ELFCLASSNONE;
281*012e6ce7SDan Cross 	const char *targ_sparc = MSG_ORIG(MSG_TARG_SPARC);
282*012e6ce7SDan Cross 	const char *targ_x86 = MSG_ORIG(MSG_TARG_X86);
283*012e6ce7SDan Cross 	uint8_t ar_class;
284*012e6ce7SDan Cross 	Half ar_mach;
285*012e6ce7SDan Cross 	char *pstr;
286*012e6ce7SDan Cross 	const char *err;
287*012e6ce7SDan Cross 	int c;
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	/*
290ba2be530Sab 	 * In general, libld.so is responsible for processing the
291ba2be530Sab 	 * command line options. The exception to this are those options
292ba2be530Sab 	 * that contain information about which linker to run and the
293ba2be530Sab 	 * class/machine of the output object. We examine the options
294ba2be530Sab 	 * here looking for the following:
2957c478bd9Sstevel@tonic-gate 	 *
29656e2cc86SAli Bahrami 	 *	-32	Produce an ELFCLASS32 object. This is the default, so
29756e2cc86SAli Bahrami 	 *		-32 is only needed when linking entirely from archives,
29856e2cc86SAli Bahrami 	 *		and the first archive contains a mix of 32 and 64-bit
29956e2cc86SAli Bahrami 	 *		objects, and the first object in that archive is 64-bit.
30056e2cc86SAli Bahrami 	 *		We do not expect this option to get much use, but it
30156e2cc86SAli Bahrami 	 *		ensures that the user can handle any situation.
30256e2cc86SAli Bahrami 	 *
30356e2cc86SAli Bahrami 	 *	-64	Produce an ELFCLASS64 object. (Note that this will
30456e2cc86SAli Bahrami 	 *		indirectly cause the use of the 64-bit linker if
30556e2cc86SAli Bahrami 	 *		the system is 64-bit capable). The most common need
30656e2cc86SAli Bahrami 	 *		for this option is when linking a filter object entirely
30756e2cc86SAli Bahrami 	 *		from a mapfile. The less common case is when linking
30856e2cc86SAli Bahrami 	 *		entirely from archives, and the first archive contains
30956e2cc86SAli Bahrami 	 *		a mix of 32 and 64-bit objects, and the first object
31056e2cc86SAli Bahrami 	 *		in that archive is 32-bit.
311ba2be530Sab 	 *
312ba2be530Sab 	 *	-z target=platform
313ba2be530Sab 	 *		Produce output object for the specified platform.
31456e2cc86SAli Bahrami 	 *		This option is needed when producing an object
31556e2cc86SAli Bahrami 	 *		for a non-native target entirely from a mapfile,
31656e2cc86SAli Bahrami 	 *		or when linking entirely from an archive containing
31756e2cc86SAli Bahrami 	 *		objects for multiple targets, and the first object
31856e2cc86SAli Bahrami 	 *		in the archive is not for the desired target.
319ba2be530Sab 	 *
32056e2cc86SAli Bahrami 	 * If we've already processed an object and we find -32/-64, and
32156e2cc86SAli Bahrami 	 * the object is of the wrong class, we have an error condition.
32256e2cc86SAli Bahrami 	 * We ignore it here, and let it fall through to libld, where the
32356e2cc86SAli Bahrami 	 * proper diagnosis and error message will occur.
324*012e6ce7SDan Cross 	 *
325*012e6ce7SDan Cross 	 * Note that these options can all be given more than once, even if
326*012e6ce7SDan Cross 	 * doing so would be ambiguous: this is for backwards compatibility
327*012e6ce7SDan Cross 	 * with Makefiles and shell scripts and so on that are themselves
328*012e6ce7SDan Cross 	 * ambiguous.
3297c478bd9Sstevel@tonic-gate 	 */
3307c478bd9Sstevel@tonic-gate 	opterr = 0;
33192a02081SRod Evans 	optind = 1;
332*012e6ce7SDan Cross 
33392a02081SRod Evans getmore:
33492a02081SRod Evans 	while ((c = ld_getopt(0, optind, argc, argv)) != -1) {
3357c478bd9Sstevel@tonic-gate 		switch (c) {
33656e2cc86SAli Bahrami 		case '3':
337*012e6ce7SDan Cross 			/*
338*012e6ce7SDan Cross 			 * MSG_ORIG(MSG_ARG_TWO) is just the non-localized
339*012e6ce7SDan Cross 			 * string literal "2", but...ok.
340*012e6ce7SDan Cross 			 */
341*012e6ce7SDan Cross 			if (strcmp(optarg, MSG_ORIG(MSG_ARG_TWO)) != 0) {
342*012e6ce7SDan Cross 				err = MSG_INTL(MSG_ERR_BADARG);
343*012e6ce7SDan Cross 				eprintf(0, ERR_FATAL, err, '3', optarg);
344*012e6ce7SDan Cross 				exit(EXIT_FAILURE);
345*012e6ce7SDan Cross 			}
346*012e6ce7SDan Cross 			class = ELFCLASS32;
34756e2cc86SAli Bahrami 			break;
34892a02081SRod Evans 		case '6':
349*012e6ce7SDan Cross 			if (strcmp(optarg, MSG_ORIG(MSG_ARG_FOUR)) != 0) {
350*012e6ce7SDan Cross 				err = MSG_INTL(MSG_ERR_BADARG);
351*012e6ce7SDan Cross 				eprintf(0, ERR_FATAL, err, '6', optarg);
352*012e6ce7SDan Cross 				exit(EXIT_FAILURE);
353*012e6ce7SDan Cross 			}
354*012e6ce7SDan Cross 			class = ELFCLASS64;
35592a02081SRod Evans 			break;
35692a02081SRod Evans 		case 'z':
357*012e6ce7SDan Cross 			/* -z target=platform; silently skip everything else */
35892a02081SRod Evans 			if (strncmp(optarg, MSG_ORIG(MSG_ARG_TARGET),
359*012e6ce7SDan Cross 			    MSG_ARG_TARGET_SIZE) != 0) {
360*012e6ce7SDan Cross 				continue;
361*012e6ce7SDan Cross 			}
362*012e6ce7SDan Cross 			pstr = optarg + MSG_ARG_TARGET_SIZE;
363*012e6ce7SDan Cross 			if (strcasecmp(pstr, targ_sparc) == 0) {
364*012e6ce7SDan Cross 				mach32 = EM_SPARC;
365*012e6ce7SDan Cross 				mach64 = EM_SPARCV9;
366*012e6ce7SDan Cross 			} else if (strcasecmp(pstr, targ_x86) == 0) {
367*012e6ce7SDan Cross 				mach32 = EM_386;
368*012e6ce7SDan Cross 				mach64 = EM_AMD64;
369*012e6ce7SDan Cross 			} else {
370*012e6ce7SDan Cross 				err = MSG_INTL(MSG_ERR_BADTARG);
371*012e6ce7SDan Cross 				eprintf(0, ERR_FATAL, err, pstr);
372*012e6ce7SDan Cross 				exit(EXIT_FAILURE);
37392a02081SRod Evans 			}
37492a02081SRod Evans 			break;
3757c478bd9Sstevel@tonic-gate 		}
3767c478bd9Sstevel@tonic-gate 	}
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	/*
3797010c12aSrie 	 * Continue to look for the first ELF object to determine the class of
38056e2cc86SAli Bahrami 	 * objects to operate on. At the same time, look for the first archive
38156e2cc86SAli Bahrami 	 * of ELF objects --- if no plain ELF object is specified, the type
38256e2cc86SAli Bahrami 	 * of the first ELF object in the first archive will be used. If
38356e2cc86SAli Bahrami 	 * there is no object, and no archive, then we fall back to a 32-bit
38456e2cc86SAli Bahrami 	 * object for the native machine.
3857c478bd9Sstevel@tonic-gate 	 */
3867c478bd9Sstevel@tonic-gate 	for (; optind < argc; optind++) {
3877c478bd9Sstevel@tonic-gate 		int		fd;
38856e2cc86SAli Bahrami 		FILE_HDR	hdr;
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 		/*
3917c478bd9Sstevel@tonic-gate 		 * If we detect some more options return to getopt().
3927c478bd9Sstevel@tonic-gate 		 * Checking argv[optind][1] against null prevents a forever
3937c478bd9Sstevel@tonic-gate 		 * loop if an unadorned `-' argument is passed to us.
3947c478bd9Sstevel@tonic-gate 		 */
3957c478bd9Sstevel@tonic-gate 		if (argv[optind][0] == '-') {
396*012e6ce7SDan Cross 			if (argv[optind][1] != '\0')
3977c478bd9Sstevel@tonic-gate 				goto getmore;
398*012e6ce7SDan Cross 			continue;
3997c478bd9Sstevel@tonic-gate 		}
4007c478bd9Sstevel@tonic-gate 
4017010c12aSrie 		/*
402ba2be530Sab 		 * If we've already determined the object class and
403ba2be530Sab 		 * machine type, continue to the next argument. Only
404ba2be530Sab 		 * the first object contributes to this decision, and
405ba2be530Sab 		 * there's no value to opening or examing the subsequent
406ba2be530Sab 		 * ones. We do need to keep going though, because there
407ba2be530Sab 		 * may be additional options that might affect our
408ba2be530Sab 		 * class/machine decision.
4097010c12aSrie 		 */
410*012e6ce7SDan Cross 		if (class != ELFCLASSNONE && mach32 != EM_NONE)
4117010c12aSrie 			continue;
4127010c12aSrie 
4137010c12aSrie 		/*
41456e2cc86SAli Bahrami 		 * Open the file and determine if it is an object. We are
41556e2cc86SAli Bahrami 		 * looking for ELF objects, or archives of ELF objects.
41656e2cc86SAli Bahrami 		 *
41756e2cc86SAli Bahrami 		 * Plain objects are simple, and are the common case, so
41856e2cc86SAli Bahrami 		 * we examine them directly and avoid the map-unmap-map
41956e2cc86SAli Bahrami 		 * that would occur if we used libelf. Archives are too
42056e2cc86SAli Bahrami 		 * complex to be worth accessing directly, so if we identify
42156e2cc86SAli Bahrami 		 * an archive, we use libelf on it and accept the cost.
4227010c12aSrie 		 */
4237c478bd9Sstevel@tonic-gate 		if ((fd = open(argv[optind], O_RDONLY)) == -1) {
4247c478bd9Sstevel@tonic-gate 			int err = errno;
4257c478bd9Sstevel@tonic-gate 
4265aefb655Srie 			eprintf(0, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN),
4277c478bd9Sstevel@tonic-gate 			    argv[optind], strerror(err));
428*012e6ce7SDan Cross 			exit(EXIT_FAILURE);
4297c478bd9Sstevel@tonic-gate 		}
4307c478bd9Sstevel@tonic-gate 
43156e2cc86SAli Bahrami 		if (pread(fd, &hdr, sizeof (hdr), 0) != sizeof (hdr)) {
43256e2cc86SAli Bahrami 			(void) close(fd);
43356e2cc86SAli Bahrami 			continue;
43456e2cc86SAli Bahrami 		}
43556e2cc86SAli Bahrami 
43656e2cc86SAli Bahrami 		if ((hdr.ehdr.e_ident[EI_MAG0] == ELFMAG0) &&
43756e2cc86SAli Bahrami 		    (hdr.ehdr.e_ident[EI_MAG1] == ELFMAG1) &&
43856e2cc86SAli Bahrami 		    (hdr.ehdr.e_ident[EI_MAG2] == ELFMAG2) &&
43956e2cc86SAli Bahrami 		    (hdr.ehdr.e_ident[EI_MAG3] == ELFMAG3)) {
44056e2cc86SAli Bahrami 			if (class == ELFCLASSNONE) {
44156e2cc86SAli Bahrami 				class = hdr.ehdr.e_ident[EI_CLASS];
44256e2cc86SAli Bahrami 				if ((class != ELFCLASS32) &&
44356e2cc86SAli Bahrami 				    (class != ELFCLASS64))
44456e2cc86SAli Bahrami 					class = ELFCLASSNONE;
445ba2be530Sab 			}
446ba2be530Sab 
447ba2be530Sab 			if (mach32 == EM_NONE) {
448ba2be530Sab 				int	one = 1;
449ba2be530Sab 				uchar_t	*one_p = (uchar_t *)&one;
450ba2be530Sab 				int	ld_elfdata;
451ba2be530Sab 
452ba2be530Sab 				ld_elfdata = (one_p[0] == 1) ?
453ba2be530Sab 				    ELFDATA2LSB : ELFDATA2MSB;
454ba2be530Sab 				/*
455ba2be530Sab 				 * Both the 32 and 64-bit versions get the
456ba2be530Sab 				 * type from the object. If the user has
457ba2be530Sab 				 * asked for an inconsistant class/machine
458ba2be530Sab 				 * combination, libld will catch it.
459ba2be530Sab 				 */
460ba2be530Sab 				mach32 = mach64 =
46156e2cc86SAli Bahrami 				    (ld_elfdata == hdr.ehdr.e_ident[EI_DATA]) ?
46256e2cc86SAli Bahrami 				    hdr.ehdr.e_machine :
46356e2cc86SAli Bahrami 				    BSWAP_HALF(hdr.ehdr.e_machine);
46456e2cc86SAli Bahrami 			}
46556e2cc86SAli Bahrami 		} else if (!ar_found &&
46656e2cc86SAli Bahrami 		    (memcmp(&hdr.armag, ARMAG, SARMAG) == 0)) {
46756e2cc86SAli Bahrami 			Elf	*elf;
46856e2cc86SAli Bahrami 
46956e2cc86SAli Bahrami 			(void) elf_version(EV_CURRENT);
47056e2cc86SAli Bahrami 			if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
47156e2cc86SAli Bahrami 				(void) close(fd);
47256e2cc86SAli Bahrami 				continue;
473ba2be530Sab 			}
47456e2cc86SAli Bahrami 			if (elf_kind(elf) == ELF_K_AR)
47556e2cc86SAli Bahrami 				ar_found =
47656e2cc86SAli Bahrami 				    archive(fd, elf, &ar_class, &ar_mach);
47756e2cc86SAli Bahrami 			(void) elf_end(elf);
4787c478bd9Sstevel@tonic-gate 		}
479ba2be530Sab 
4807c478bd9Sstevel@tonic-gate 		(void) close(fd);
4817c478bd9Sstevel@tonic-gate 	}
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	/*
48456e2cc86SAli Bahrami 	 * ELFCLASS of output object: If we did not establish a class from a
48556e2cc86SAli Bahrami 	 * command option, or from the first plain object, then use the class
48656e2cc86SAli Bahrami 	 * from the first archive, and failing that, default to 32-bit.
4877c478bd9Sstevel@tonic-gate 	 */
48856e2cc86SAli Bahrami 	if (class == ELFCLASSNONE)
48956e2cc86SAli Bahrami 		class = ar_found ? ar_class : ELFCLASS32;
49056e2cc86SAli Bahrami 	*class_ret = class;
491ba2be530Sab 
492ba2be530Sab 	/*
49356e2cc86SAli Bahrami 	 * Machine type of output object: If we did not establish a machine
49456e2cc86SAli Bahrami 	 * type from the command line, or from the first plain object, then
49556e2cc86SAli Bahrami 	 * use the machine established by the first archive, and failing that,
49656e2cc86SAli Bahrami 	 * use the native machine.
497ba2be530Sab 	 */
49856e2cc86SAli Bahrami 	*mach = (class == ELFCLASS64) ? mach64 : mach32;
499ba2be530Sab 	if (*mach == EM_NONE)
50056e2cc86SAli Bahrami 		if (ar_found)
50156e2cc86SAli Bahrami 			*mach = ar_mach;
50256e2cc86SAli Bahrami 		else
50356e2cc86SAli Bahrami 			*mach = (class == ELFCLASS64) ? M_MACH_64 : M_MACH_32;
504ba2be530Sab 
505*012e6ce7SDan Cross 	if (class == ELFCLASS32)
506*012e6ce7SDan Cross 		return (ld32_main);
507*012e6ce7SDan Cross 
508*012e6ce7SDan Cross 	return (ld64_main);
5097c478bd9Sstevel@tonic-gate }
5107c478bd9Sstevel@tonic-gate 
511*012e6ce7SDan Cross struct strlist {
512*012e6ce7SDan Cross 	struct strlist *sl_next;
513*012e6ce7SDan Cross 	char *sl_str;
514*012e6ce7SDan Cross };
515*012e6ce7SDan Cross 
5167c478bd9Sstevel@tonic-gate /*
517*012e6ce7SDan Cross  * Parse an LD_OPTIONS environment string.  Returns a linked list of strings
518*012e6ce7SDan Cross  * parsed from the original list, or NULL if the list is empty.
5197c478bd9Sstevel@tonic-gate  */
520*012e6ce7SDan Cross static struct strlist *
split_options(char * str)521*012e6ce7SDan Cross split_options(char *str)
5227c478bd9Sstevel@tonic-gate {
523*012e6ce7SDan Cross 	struct strlist *strs = NULL;
524*012e6ce7SDan Cross 	struct strlist **nextp = &strs;
525*012e6ce7SDan Cross 	struct strlist *next;
526*012e6ce7SDan Cross 	char *arg;
52792a02081SRod Evans 
528*012e6ce7SDan Cross 	while ((arg = strsep_ws(&str)) != NULL) {
529*012e6ce7SDan Cross 		if (*arg == '\0')
530*012e6ce7SDan Cross 			continue;
531*012e6ce7SDan Cross 		next = calloc(1, sizeof (struct strlist));
532*012e6ce7SDan Cross 		if (next == NULL) {
533*012e6ce7SDan Cross 			eprintf(0, ERR_FATAL,
534*012e6ce7SDan Cross 			    MSG_INTL(MSG_SYS_ALLOC), strerror(errno));
535*012e6ce7SDan Cross 			exit(EXIT_FAILURE);
536*012e6ce7SDan Cross 		}
537*012e6ce7SDan Cross 		next->sl_str = arg;
538*012e6ce7SDan Cross 		*nextp = next;
539*012e6ce7SDan Cross 		nextp = &next->sl_next;
54092a02081SRod Evans 	}
54192a02081SRod Evans 
542*012e6ce7SDan Cross 	return (strs);
54392a02081SRod Evans }
54492a02081SRod Evans 
54592a02081SRod Evans /*
54692a02081SRod Evans  * Determine whether an LD_OPTIONS environment variable is set, and if so,
54792a02081SRod Evans  * prepend environment string as a series of options to the argv array.
54892a02081SRod Evans  */
549*012e6ce7SDan Cross static void
prepend_ldoptions(int * argcp,char ** argvp[])550*012e6ce7SDan Cross prepend_ldoptions(int *argcp, char **argvp[])
55192a02081SRod Evans {
552*012e6ce7SDan Cross 	int argc, nargc;
553*012e6ce7SDan Cross 	char **argv, **nargv, *ld_options;
554*012e6ce7SDan Cross 	struct strlist *opts, *p, *t;
55592a02081SRod Evans 
556*012e6ce7SDan Cross 	ld_options = getenv_nonempty(MSG_ORIG(MSG_LD_OPTIONS));
557*012e6ce7SDan Cross 	if (ld_options == NULL)
558*012e6ce7SDan Cross 		return;
55992a02081SRod Evans 
56092a02081SRod Evans 	/*
561*012e6ce7SDan Cross 	 * Parse and count options.
5627c478bd9Sstevel@tonic-gate 	 */
563*012e6ce7SDan Cross 	opts = split_options(ld_options);
564*012e6ce7SDan Cross 	for (nargc = 0, p = opts; p != NULL; p = p->sl_next)
565*012e6ce7SDan Cross 		nargc++;
566635216b6SRod Evans 
567635216b6SRod Evans 	/*
568*012e6ce7SDan Cross 	 * Allocate a new argument vector big enough to hold both the old
569*012e6ce7SDan Cross 	 * and new arguments.
570635216b6SRod Evans 	 */
571*012e6ce7SDan Cross 	argc = *argcp;
572*012e6ce7SDan Cross 	argv = *argvp;
573*012e6ce7SDan Cross 	nargv = calloc(nargc + argc + 1, sizeof (char *));
574*012e6ce7SDan Cross 	if (nargv == NULL) {
575*012e6ce7SDan Cross 		eprintf(0, ERR_FATAL, MSG_INTL(MSG_SYS_ALLOC), strerror(errno));
576*012e6ce7SDan Cross 		exit(EXIT_FAILURE);
5775aefb655Srie 	}
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	/*
5807c478bd9Sstevel@tonic-gate 	 * Initialize first element of new argv array to be the first element
5817c478bd9Sstevel@tonic-gate 	 * of the old argv array (ie. calling programs name).  Then add the new
5827c478bd9Sstevel@tonic-gate 	 * args obtained from the environment.
5837c478bd9Sstevel@tonic-gate 	 */
584*012e6ce7SDan Cross 	nargv[0] = argv[0];
585*012e6ce7SDan Cross 	for (nargc = 1, p = opts; p != NULL; nargc++, p = p->sl_next)
586*012e6ce7SDan Cross 		nargv[nargc] = p->sl_str;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/*
5897c478bd9Sstevel@tonic-gate 	 * Now add the original argv array (skipping argv[0]) to the end of the
59092a02081SRod Evans 	 * new argv array, and re-vector argc and argv to reference this new
59192a02081SRod Evans 	 * array
5927c478bd9Sstevel@tonic-gate 	 */
593*012e6ce7SDan Cross 	for (int i = 1; i < argc; i++, nargc++)
594*012e6ce7SDan Cross 		nargv[nargc] = argv[i];
59592a02081SRod Evans 	nargv[nargc] = NULL;
59692a02081SRod Evans 
597*012e6ce7SDan Cross 	/*
598*012e6ce7SDan Cross 	 * Clean up the strlist.
599*012e6ce7SDan Cross 	 */
600*012e6ce7SDan Cross 	for (t = NULL, p = opts; p != NULL; p = t) {
601*012e6ce7SDan Cross 		t = p->sl_next;
602*012e6ce7SDan Cross 		free(p);
603*012e6ce7SDan Cross 	}
604*012e6ce7SDan Cross 
60592a02081SRod Evans 	*argcp = nargc;
6067c478bd9Sstevel@tonic-gate 	*argvp = nargv;
6077c478bd9Sstevel@tonic-gate }
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate /*
6105aefb655Srie  * Check to see if there is a LD_ALTEXEC=<path to alternate ld> in the
6115aefb655Srie  * environment.  If so, first null the environment variable out, and then
6125aefb655Srie  * exec() the binary pointed to by the environment variable, passing the same
6135aefb655Srie  * arguments as the originating process.  This mechanism permits using
6145aefb655Srie  * alternate link-editors (debugging/developer copies) even in complex build
6155aefb655Srie  * environments.
6167c478bd9Sstevel@tonic-gate  */
617*012e6ce7SDan Cross static void
ld_altexec(int argc,char * argv[],char * envp[])618*012e6ce7SDan Cross ld_altexec(int argc, char *argv[], char *envp[])
6197c478bd9Sstevel@tonic-gate {
620*012e6ce7SDan Cross 	char *bin;
621*012e6ce7SDan Cross 	struct strlist *opts, *p, *t;
622*012e6ce7SDan Cross 	char **nargv;
623*012e6ce7SDan Cross 	int i;
6247010c12aSrie 
6257010c12aSrie 	/*
626*012e6ce7SDan Cross 	 * If LD_ALTEXEC isn't set, or is empty, return to continue executing
627*012e6ce7SDan Cross 	 * the present link-editor.  Note that we unconditionally unset it.
6287010c12aSrie 	 */
629*012e6ce7SDan Cross 	bin = getenv_nonempty(MSG_ORIG(MSG_LD_ALTEXEC));
630*012e6ce7SDan Cross 	(void) unsetenv(MSG_ORIG(MSG_LD_ALTEXEC));
631*012e6ce7SDan Cross 	if (bin == NULL)
632*012e6ce7SDan Cross 		return;
6337c478bd9Sstevel@tonic-gate 
634*012e6ce7SDan Cross 	/* Parse and count options, including argv[0]. */
635*012e6ce7SDan Cross 	opts = split_options(bin);
636*012e6ce7SDan Cross 	if (opts == NULL)
637*012e6ce7SDan Cross 		return;
6387010c12aSrie 
639*012e6ce7SDan Cross 
640*012e6ce7SDan Cross 	for (p = opts; p != NULL; p = p->sl_next)
641*012e6ce7SDan Cross 		argc++;
642*012e6ce7SDan Cross 
643*012e6ce7SDan Cross 	nargv = calloc(argc, sizeof (char *));
644*012e6ce7SDan Cross 	if (nargv == NULL) {
645*012e6ce7SDan Cross 		eprintf(0, ERR_FATAL, MSG_INTL(MSG_SYS_ALLOC), strerror(errno));
646*012e6ce7SDan Cross 		exit(EXIT_FAILURE);
647*012e6ce7SDan Cross 	}
648*012e6ce7SDan Cross 	for (i = 0, p = opts; p != NULL; p = p->sl_next, i++)
649*012e6ce7SDan Cross 		nargv[i] = p->sl_str;
650*012e6ce7SDan Cross 	/* Note that `argc` now counts the NULL at the end of `nargv`. */
651*012e6ce7SDan Cross 	for (; i < argc; i++)
652*012e6ce7SDan Cross 		nargv[i] = *++argv;
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	/*
655*012e6ce7SDan Cross 	 * Clean up the strlist.
6567c478bd9Sstevel@tonic-gate 	 */
657*012e6ce7SDan Cross 	for (t = NULL, p = opts; p != NULL; p = t) {
658*012e6ce7SDan Cross 		t = p->sl_next;
659*012e6ce7SDan Cross 		free(p);
660*012e6ce7SDan Cross 	}
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	/*
663*012e6ce7SDan Cross 	 * Set argv[0] to point to our new linker And attempt to execute it.
6647c478bd9Sstevel@tonic-gate 	 */
665*012e6ce7SDan Cross 	(void) execve(bin, nargv, envp);
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	/*
668*012e6ce7SDan Cross 	 * If the exec() fails, exit with failure.
6697c478bd9Sstevel@tonic-gate 	 */
670*012e6ce7SDan Cross 	eprintf(0, ERR_FATAL, MSG_INTL(MSG_SYS_EXEC), bin, strerror(errno));
671*012e6ce7SDan Cross 	exit(EXIT_FAILURE);
6727c478bd9Sstevel@tonic-gate }
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate int
main(int argc,char * argv[],char * envp[])675*012e6ce7SDan Cross main(int argc, char *argv[], char *envp[])
6767c478bd9Sstevel@tonic-gate {
677*012e6ce7SDan Cross 	uint8_t class;
678*012e6ce7SDan Cross 	Half mach;
679*012e6ce7SDan Cross 	ld_main_f ld_main;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	/*
682*012e6ce7SDan Cross 	 * Establish locale and initialize error strings.
6837c478bd9Sstevel@tonic-gate 	 */
684*012e6ce7SDan Cross 	init_strings();
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	/*
687*012e6ce7SDan Cross 	 * Maybe execute an alternate linker.  If the LD_ALTEXEC
688*012e6ce7SDan Cross 	 * environment variable is set, we will try and run what it
689*012e6ce7SDan Cross 	 * points to or fail.  If it is not set, we simply continue.
6907c478bd9Sstevel@tonic-gate 	 */
691*012e6ce7SDan Cross 	ld_altexec(argc, argv, envp);
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	/*
694*012e6ce7SDan Cross 	 * Maybe process additional arguments.  If the LD_OPTIONS
695*012e6ce7SDan Cross 	 * environment variable is set, and if present prepend
6967c478bd9Sstevel@tonic-gate 	 * the arguments specified to the command line argument list.
6977c478bd9Sstevel@tonic-gate 	 */
698*012e6ce7SDan Cross 	prepend_ldoptions(&argc, &argv);
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	/*
701ba2be530Sab 	 * Examine the command arguments to determine:
702ba2be530Sab 	 *	- object class
703ba2be530Sab 	 *	- link-editor class
704ba2be530Sab 	 *	- target machine
7057c478bd9Sstevel@tonic-gate 	 */
706*012e6ce7SDan Cross 	ld_main = process_args(argc, argv, &class, &mach);
7077c478bd9Sstevel@tonic-gate 
70856e2cc86SAli Bahrami 	/* Call the libld entry point for the specified ELFCLASS */
709*012e6ce7SDan Cross 	return (ld_main(argc, argv, mach));
7107c478bd9Sstevel@tonic-gate }
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate /*
71356e2cc86SAli Bahrami  * We supply this function for the msg module
7147c478bd9Sstevel@tonic-gate  */
7157c478bd9Sstevel@tonic-gate const char *
_ld_msg(Msg mid)7167c478bd9Sstevel@tonic-gate _ld_msg(Msg mid)
7177c478bd9Sstevel@tonic-gate {
7187c478bd9Sstevel@tonic-gate 	return (gettext(MSG_ORIG(mid)));
7197c478bd9Sstevel@tonic-gate }
720