xref: /titanic_44/usr/src/cmd/sgs/liblddbg/common/debug.c (revision e65e5c2d2f32a99e8c5f740cabae9075dab03ce7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <libintl.h>
28 #include <sys/varargs.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <alist.h>
33 #include <debug.h>
34 #include <_debug.h>
35 #include <msg.h>
36 
37 /*
38  * Define a debug descriptor.  Note, although this provides the default
39  * definition to which most users bind, ld.so.1 must provide its own definition,
40  * and thus interposition is expected.  This item should be defined NODIRECT.
41  */
42 static Dbg_desc	_dbg_desc = { 0, 0, NULL, { 0, 0 }, { 0, 0 } };
43 Dbg_desc	*dbg_desc = &_dbg_desc;
44 
45 int		_Dbg_cnt = 0;
46 
47 /*
48  * Debugging initialization and processing.  The dbg_options[] array defines
49  * a set of option strings that can be specified using the -D flag or from an
50  * environment variable.  For each option, a class is enabled in the d_class
51  * bit mask, or an extra flag is enabled in the d_extra bit mask.
52  */
53 static DBG_options _Dbg_options[] = {	/* Options accepted by both linkers */
54 	{MSG_ORIG(MSG_TOK_DETAIL),	0,	DBG_E_DETAIL},
55 	{MSG_ORIG(MSG_TOK_LONG),	0,	DBG_E_LONG},
56 	{MSG_ORIG(MSG_TOK_HELP),	0,	DBG_E_HELP},
57 	{MSG_ORIG(MSG_TOK_TTIME),	0,	DBG_E_TTIME},
58 	{MSG_ORIG(MSG_TOK_DTIME),	0,	DBG_E_DTIME},
59 
60 	{MSG_ORIG(MSG_TOK_ALL),		DBG_C_ALL & ~DBG_C_DEMANGLE,	0},
61 	{MSG_ORIG(MSG_TOK_BASIC),	DBG_C_BASIC,			0},
62 	{MSG_ORIG(MSG_TOK_CAP),		DBG_C_CAP,			0},
63 	{MSG_ORIG(MSG_TOK_DEMANGLE),	DBG_C_DEMANGLE,			0},
64 	{MSG_ORIG(MSG_TOK_FILES),	DBG_C_FILES,			0},
65 	{MSG_ORIG(MSG_TOK_LIBS),	DBG_C_LIBS,			0},
66 	{MSG_ORIG(MSG_TOK_MOVE),	DBG_C_MOVE,			0},
67 	{MSG_ORIG(MSG_TOK_RELOC),	DBG_C_RELOC,			0},
68 	{MSG_ORIG(MSG_TOK_SYMBOLS),	DBG_C_SYMBOLS,			0},
69 	{MSG_ORIG(MSG_TOK_TLS),		DBG_C_TLS,			0},
70 	{MSG_ORIG(MSG_TOK_UNUSED),	DBG_C_UNUSED,			0},
71 	{MSG_ORIG(MSG_TOK_VERSIONS),	DBG_C_VERSIONS,			0},
72 	{NULL,				0,				0},
73 };
74 
75 static DBG_options _Dbg_options_ld[] = {	/* ld only options */
76 	{MSG_ORIG(MSG_TOK_CLASS),	0,	DBG_E_SNAME | DBG_E_CLASS},
77 	{MSG_ORIG(MSG_TOK_FULLNAME),	0,	DBG_E_SNAME | DBG_E_FNAME},
78 	{MSG_ORIG(MSG_TOK_NAME),	0,	DBG_E_SNAME},
79 
80 	{MSG_ORIG(MSG_TOK_ARGS),	DBG_C_ARGS,	0},
81 	{MSG_ORIG(MSG_TOK_ENTRY),	DBG_C_ENTRY,	0},
82 	{MSG_ORIG(MSG_TOK_GOT),		DBG_C_GOT,	0},
83 	{MSG_ORIG(MSG_TOK_MAP),		DBG_C_MAP,	0},
84 	{MSG_ORIG(MSG_TOK_SECTIONS),	DBG_C_SECTIONS,	0},
85 	{MSG_ORIG(MSG_TOK_SEGMENTS),	DBG_C_SEGMENTS,	0},
86 	{MSG_ORIG(MSG_TOK_STATS),	DBG_C_STATS,	0},
87 	{MSG_ORIG(MSG_TOK_STRTAB),	DBG_C_STRTAB,	0},
88 	{MSG_ORIG(MSG_TOK_SUPPORT),	DBG_C_SUPPORT,	0},
89 	{NULL,				0,		0},
90 };
91 
92 static DBG_options _Dbg_options_rtld[] = {	/* ld.so.1 only options */
93 	{MSG_ORIG(MSG_TOK_AUDIT),	DBG_C_AUDITING,	0},
94 	{MSG_ORIG(MSG_TOK_BINDINGS),	DBG_C_BINDINGS,	0},
95 	{MSG_ORIG(MSG_TOK_CALLBACK),    DBG_C_CALLBACK,	0},
96 	{MSG_ORIG(MSG_TOK_INIT),	DBG_C_INIT,	0},
97 	{NULL,				0,		0},
98 };
99 
100 /*
101  * Compare name to the options found in optarr. If one matches,
102  * update *dbp and return TRUE. Otherwise, FALSE.
103  */
104 static Boolean
105 process_options(const char *name, Boolean set, Dbg_desc *dbp,
106     DBG_options *optarr)
107 {
108 	DBG_options	*opt;
109 
110 	for (opt = optarr; opt->o_name != NULL; opt++) {
111 		if (strcmp(name, opt->o_name) != 0)
112 			continue;
113 
114 		if (set == TRUE) {
115 			if (opt->o_class)
116 				dbp->d_class |= opt->o_class;
117 			if (opt->o_extra)
118 				dbp->d_extra |= opt->o_extra;
119 		} else {
120 			if (opt->o_class)
121 				dbp->d_class &= ~(opt->o_class);
122 			if (opt->o_extra)
123 				dbp->d_extra &= ~(opt->o_extra);
124 		}
125 		return (TRUE);
126 	}
127 
128 	return (FALSE);
129 }
130 
131 /*
132  * Provide a debugging usage message
133  */
134 void
135 Dbg_help(void)
136 {
137 	Dbg_util_nl(0, DBG_NL_STD);
138 	dbg_print(0, MSG_INTL(MSG_USE_R1_A));
139 	dbg_print(0, MSG_INTL(MSG_USE_R1_B));
140 	dbg_print(0, MSG_INTL(MSG_USE_R1_C));
141 	dbg_print(0, MSG_INTL(MSG_USE_R1_D));
142 	dbg_print(0, MSG_INTL(MSG_USE_R1_E));
143 	dbg_print(0, MSG_INTL(MSG_USE_R1_F));
144 	dbg_print(0, MSG_INTL(MSG_USE_R1_G));
145 
146 	Dbg_util_nl(0, DBG_NL_FRC);
147 	dbg_print(0, MSG_INTL(MSG_USE_R2_A));
148 	dbg_print(0, MSG_INTL(MSG_USE_R2_B));
149 	dbg_print(0, MSG_INTL(MSG_USE_R2_C));
150 	dbg_print(0, MSG_INTL(MSG_USE_R2_D));
151 	dbg_print(0, MSG_INTL(MSG_USE_R2_E));
152 	dbg_print(0, MSG_INTL(MSG_USE_R2_F));
153 	dbg_print(0, MSG_INTL(MSG_USE_R2_G));
154 	dbg_print(0, MSG_INTL(MSG_USE_R2_H));
155 	dbg_print(0, MSG_INTL(MSG_USE_R2_I));
156 	dbg_print(0, MSG_INTL(MSG_USE_R2_J));
157 	dbg_print(0, MSG_INTL(MSG_USE_R2_K));
158 	dbg_print(0, MSG_INTL(MSG_USE_R2_L));
159 	dbg_print(0, MSG_INTL(MSG_USE_R2_M));
160 	dbg_print(0, MSG_INTL(MSG_USE_R2_N));
161 	dbg_print(0, MSG_INTL(MSG_USE_R2_O));
162 	dbg_print(0, MSG_INTL(MSG_USE_R2_P));
163 	dbg_print(0, MSG_INTL(MSG_USE_R2_Q));
164 
165 	Dbg_util_nl(0, DBG_NL_FRC);
166 	dbg_print(0, MSG_INTL(MSG_USE_R2_R));
167 	dbg_print(0, MSG_INTL(MSG_USE_R2_S));
168 	dbg_print(0, MSG_INTL(MSG_USE_R2_T));
169 	dbg_print(0, MSG_INTL(MSG_USE_R2_U));
170 	dbg_print(0, MSG_INTL(MSG_USE_R2_V));
171 	dbg_print(0, MSG_INTL(MSG_USE_R2_W));
172 
173 	Dbg_util_nl(0, DBG_NL_FRC);
174 	dbg_print(0, MSG_INTL(MSG_USE_R3_A));
175 	dbg_print(0, MSG_INTL(MSG_USE_R3_B));
176 	dbg_print(0, MSG_INTL(MSG_USE_R3_C));
177 	dbg_print(0, MSG_INTL(MSG_USE_R3_D));
178 	dbg_print(0, MSG_INTL(MSG_USE_R3_E));
179 	dbg_print(0, MSG_INTL(MSG_USE_R3_F));
180 	dbg_print(0, MSG_INTL(MSG_USE_R3_G));
181 
182 	Dbg_util_nl(0, DBG_NL_FRC);
183 	dbg_print(0, MSG_INTL(MSG_USE_R3_H));
184 	dbg_print(0, MSG_INTL(MSG_USE_R3_F));
185 	dbg_print(0, MSG_INTL(MSG_USE_R3_I));
186 	dbg_print(0, MSG_INTL(MSG_USE_R3_J));
187 	dbg_print(0, MSG_INTL(MSG_USE_R3_K));
188 	dbg_print(0, MSG_INTL(MSG_USE_R3_L));
189 	dbg_print(0, MSG_INTL(MSG_USE_R3_M));
190 
191 	Dbg_util_nl(0, DBG_NL_FRC);
192 	dbg_print(0, MSG_INTL(MSG_USE_R3_N));
193 
194 	Dbg_util_nl(0, DBG_NL_FRC);
195 	dbg_print(0, MSG_INTL(MSG_USE_HDR_DCT));
196 	dbg_print(0, MSG_INTL(MSG_USE_HDR_BOTH));
197 	dbg_print(0, MSG_INTL(MSG_USE_R4_A));
198 	dbg_print(0, MSG_INTL(MSG_USE_R4_B));
199 	dbg_print(0, MSG_INTL(MSG_USE_R4_B2));
200 	dbg_print(0, MSG_INTL(MSG_USE_R4_C));
201 	dbg_print(0, MSG_INTL(MSG_USE_R4_C2));
202 	dbg_print(0, MSG_INTL(MSG_USE_R4_C3));
203 	dbg_print(0, MSG_INTL(MSG_USE_R4_D));
204 	dbg_print(0, MSG_INTL(MSG_USE_R4_E));
205 	dbg_print(0, MSG_INTL(MSG_USE_R4_E2));
206 	dbg_print(0, MSG_INTL(MSG_USE_R4_E3));
207 	dbg_print(0, MSG_INTL(MSG_USE_R4_F));
208 	dbg_print(0, MSG_INTL(MSG_USE_R4_F2));
209 	dbg_print(0, MSG_INTL(MSG_USE_R4_F3));
210 	dbg_print(0, MSG_INTL(MSG_USE_R4_F4));
211 	dbg_print(0, MSG_INTL(MSG_USE_R4_F5));
212 	dbg_print(0, MSG_INTL(MSG_USE_R4_F6));
213 
214 	Dbg_util_nl(0, DBG_NL_FRC);
215 	dbg_print(0, MSG_INTL(MSG_USE_HDR_RTLD));
216 	dbg_print(0, MSG_INTL(MSG_USE_R5_A));
217 	dbg_print(0, MSG_INTL(MSG_USE_R5_A2));
218 	dbg_print(0, MSG_INTL(MSG_USE_R5_A3));
219 	dbg_print(0, MSG_INTL(MSG_USE_R5_A4));
220 	dbg_print(0, MSG_INTL(MSG_USE_R5_A5));
221 	dbg_print(0, MSG_INTL(MSG_USE_R5_A6));
222 	dbg_print(0, MSG_INTL(MSG_USE_R5_A7));
223 	dbg_print(0, MSG_INTL(MSG_USE_R5_A8));
224 	dbg_print(0, MSG_INTL(MSG_USE_R5_A9));
225 	dbg_print(0, MSG_INTL(MSG_USE_R5_A0));
226 	dbg_print(0, MSG_INTL(MSG_USE_R5_B));
227 	dbg_print(0, MSG_INTL(MSG_USE_R5_C));
228 	dbg_print(0, MSG_INTL(MSG_USE_R5_D));
229 	dbg_print(0, MSG_INTL(MSG_USE_R5_E));
230 	dbg_print(0, MSG_INTL(MSG_USE_R5_F));
231 
232 	Dbg_util_nl(0, DBG_NL_FRC);
233 	dbg_print(0, MSG_INTL(MSG_USE_HDR_LD));
234 	dbg_print(0, MSG_INTL(MSG_USE_R6_A));
235 	dbg_print(0, MSG_INTL(MSG_USE_R6_B));
236 	dbg_print(0, MSG_INTL(MSG_USE_R6_C));
237 	dbg_print(0, MSG_INTL(MSG_USE_R6_C2));
238 
239 	Dbg_util_nl(0, DBG_NL_FRC);
240 	dbg_print(0, MSG_INTL(MSG_USE_HDR_CST));
241 	dbg_print(0, MSG_INTL(MSG_USE_HDR_BOTH));
242 	dbg_print(0, MSG_INTL(MSG_USE_R7_A));
243 	dbg_print(0, MSG_INTL(MSG_USE_R7_B));
244 	dbg_print(0, MSG_INTL(MSG_USE_R7_C));
245 	dbg_print(0, MSG_INTL(MSG_USE_R7_D));
246 	dbg_print(0, MSG_INTL(MSG_USE_R7_E));
247 	dbg_print(0, MSG_INTL(MSG_USE_R7_F));
248 	dbg_print(0, MSG_INTL(MSG_USE_R7_F2));
249 	dbg_print(0, MSG_INTL(MSG_USE_R7_G));
250 	dbg_print(0, MSG_INTL(MSG_USE_R7_H));
251 	dbg_print(0, MSG_INTL(MSG_USE_R7_I));
252 	dbg_print(0, MSG_INTL(MSG_USE_R7_I2));
253 	dbg_print(0, MSG_INTL(MSG_USE_R7_J));
254 	dbg_print(0, MSG_INTL(MSG_USE_R7_K));
255 	dbg_print(0, MSG_INTL(MSG_USE_R7_K2));
256 	dbg_print(0, MSG_INTL(MSG_USE_R7_L));
257 
258 	Dbg_util_nl(0, DBG_NL_FRC);
259 	dbg_print(0, MSG_INTL(MSG_USE_HDR_RTLD));
260 	dbg_print(0, MSG_INTL(MSG_USE_R8_A));
261 	dbg_print(0, MSG_INTL(MSG_USE_R8_B));
262 	dbg_print(0, MSG_INTL(MSG_USE_R8_B2));
263 	dbg_print(0, MSG_INTL(MSG_USE_R8_C));
264 	dbg_print(0, MSG_INTL(MSG_USE_R8_C2));
265 	dbg_print(0, MSG_INTL(MSG_USE_R8_D));
266 
267 	Dbg_util_nl(0, DBG_NL_FRC);
268 	dbg_print(0, MSG_INTL(MSG_USE_HDR_LD));
269 	dbg_print(0, MSG_INTL(MSG_USE_R9_A));
270 	dbg_print(0, MSG_INTL(MSG_USE_R9_B));
271 	dbg_print(0, MSG_INTL(MSG_USE_R9_C));
272 	dbg_print(0, MSG_INTL(MSG_USE_R9_D));
273 	dbg_print(0, MSG_INTL(MSG_USE_R9_E));
274 	dbg_print(0, MSG_INTL(MSG_USE_R9_F));
275 	dbg_print(0, MSG_INTL(MSG_USE_R9_F2));
276 	dbg_print(0, MSG_INTL(MSG_USE_R9_G));
277 	dbg_print(0, MSG_INTL(MSG_USE_R9_H));
278 	dbg_print(0, MSG_INTL(MSG_USE_R9_H2));
279 	dbg_print(0, MSG_INTL(MSG_USE_R9_I));
280 
281 	Dbg_util_nl(0, DBG_NL_FRC);
282 }
283 
284 /*
285  * Provide a debugging message showing the version of the linker package
286  */
287 void
288 Dbg_version(void)
289 {
290 	Dbg_util_nl(0, DBG_NL_STD);
291 	dbg_print(0, MSG_ORIG(MSG_STR_LDVER), link_ver_string);
292 	Dbg_util_nl(0, DBG_NL_STD);
293 }
294 
295 /*
296  * Messaging support - funnel everything through dgettext() as this provides
297  * the real binding to libc.
298  */
299 const char *
300 _liblddbg_msg(Msg mid)
301 {
302 	return (dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid)));
303 }
304 
305 /*
306  * Given a name starting with "lmid", finish processing it. Return TRUE
307  * if a valid lmid token was seen, and FALSE for any error.
308  *
309  * exit:
310  *	On failure, returns FALSE, indicating a syntax error
311  *
312  *	On success:
313  *	-	Appropriate flags in dbg->d_extra have been set
314  *	-	Any link-map list names specified have been added to
315  *		d_list, for the rtld dbg_print() to compare against
316  *		link-map list names.
317  *	-	TRUE is returned.
318  */
319 static Boolean
320 process_lmid(char *name, Dbg_desc *dbp)
321 {
322 	/*
323 	 * "lmid" can have an optional argument. Allowed values are "all",
324 	 * "alt[0-9]+", "base", or "ldso". Alt has a variable ending, but
325 	 * we can use process_options() to handle the other three.
326 	 */
327 	static DBG_options options_lmid[] = {
328 		{MSG_ORIG(MSG_TOK_LMID_ALL),	0,	DBG_E_LMID_ALL},
329 		{MSG_ORIG(MSG_TOK_LMID_BASE),	0,	DBG_E_LMID_BASE},
330 		{MSG_ORIG(MSG_TOK_LMID_LDSO),	0,	DBG_E_LMID_LDSO},
331 		{NULL,				NULL},
332 	};
333 
334 	Dbg_desc	tmp_db;
335 	const char	*lmid_opt;
336 
337 	/* If it's a plain "lmid", we can set the flag and return now */
338 	if (name[MSG_TOK_LMID_SIZE] == '\0') {
339 		dbp->d_extra |= DBG_E_LMID;
340 		return (TRUE);
341 	}
342 
343 	/* If there's no value, its an error */
344 	if (conv_strproc_extract_value(name, MSG_TOK_LMID_SIZE,
345 	    CONV_SPEXV_F_UCASE, &lmid_opt) == 0)
346 		return (FALSE);
347 
348 	/*
349 	 * ALL, BASE, or LDSO?
350 	 */
351 	tmp_db.d_extra = 0;
352 	if (process_options(lmid_opt, TRUE, &tmp_db, options_lmid)) {
353 		/*
354 		 * If BASE, and we haven't already seen it, add it to the
355 		 * rtld name matching list. For the others, setting the
356 		 * e_extra bit suffices.
357 		 */
358 		if (((tmp_db.d_extra & DBG_E_LMID_BASE) != 0) &&
359 		    ((dbp->d_extra & DBG_E_LMID_BASE) == 0) &&
360 		    (aplist_append(&dbp->d_list, MSG_ORIG(MSG_TOK_LMID_BASE),
361 		    AL_CNT_DEBUG) == NULL))
362 			return (FALSE);
363 
364 		/* Add the resulting flags into the callers descriptor */
365 		dbp->d_extra |= DBG_E_LMID | tmp_db.d_extra;
366 		return (TRUE);
367 	}
368 
369 	/*
370 	 * ALT?
371 	 */
372 	if (strncmp(lmid_opt, MSG_ORIG(MSG_TOK_LMID_ALT),
373 	    MSG_TOK_LMID_ALT_SIZE) == 0) {
374 		const char *tail = lmid_opt + MSG_TOK_LMID_ALT_SIZE;
375 
376 		/* 'ALT' without a # means "all alternative link-map lists" */
377 		if (*tail == '\0') {
378 			dbp->d_extra |= DBG_E_LMID | DBG_E_LMID_ALT;
379 			return (TRUE);
380 		}
381 
382 		/*
383 		 * It is ALT[0-9]+. Make sure the characters following 'ALT'
384 		 * are numbers, and then add it to the rtld name matching list.
385 		 */
386 		for (; *tail; tail++)
387 			if ((*tail < '0') || (*tail > '9'))
388 				return (FALSE);
389 
390 		if (aplist_append(&dbp->d_list, lmid_opt, AL_CNT_DEBUG) == NULL)
391 			return (FALSE);
392 		dbp->d_extra |= DBG_E_LMID;
393 		return (TRUE);
394 	}
395 
396 	/* It's nothing we recognize */
397 	return (FALSE);
398 }
399 
400 /*
401  * Validate and enable the appropriate debugging classes.
402  *
403  * entry:
404  *	string - String to be analyzed for debugging options
405  *	dbp - Pointer to debug descriptor to be initialized
406  *	outfile_ret - NULL, or pointer to receive result of 'output='
407  *		token. A NULL value means that the 'output=' token
408  *		is not accepted. A non-NULL value means that it is.
409  *
410  * exit:
411  *	On failure, False (0) is returned.
412  *
413  *	On success, string has been parsed, and the descriptor referenced
414  *	by dbp has been initialized. If outfile is non-NULL, *outfile will
415  *	be set to NULL if the 'output=' token is not present, and to the
416  *	user supplied string otherwise. True (1) is returned.
417  */
418 int
419 Dbg_setup(dbg_setup_caller_t caller, const char *string, Dbg_desc *dbp,
420     const char **outfile)
421 {
422 	char		*name, *_name;	/* buffer in which to perform */
423 					/* strtok_r() operations. */
424 	char		*lasts;
425 	const char	*delimit = MSG_ORIG(MSG_STR_DELIMIT);
426 
427 	/*
428 	 * Clear the help flags --- these items only apply for a single
429 	 * call to Dbg_setup().
430 	 */
431 	dbp->d_extra &= ~(DBG_E_HELP | DBG_E_HELP_EXIT);
432 
433 	if ((_name = (char *)malloc(strlen(string) + 1)) == NULL)
434 		return (0);
435 	(void) strcpy(_name, string);
436 
437 	if (outfile)
438 		*outfile = NULL;   /* No output file yet */
439 
440 	/*
441 	 * The token should be of the form "-Dtok,tok,tok,...".  Separate the
442 	 * pieces and build up the appropriate mask, unrecognized options are
443 	 * flagged.
444 	 */
445 	if ((name = strtok_r(_name, delimit, &lasts)) != NULL) {
446 		do {
447 			Boolean		set;
448 
449 			/* Remove leading and trailing whitespace */
450 			name = conv_strproc_trim(name);
451 
452 			if (name[0] == '!') {
453 				set = FALSE;
454 				name++;
455 			} else
456 				set = TRUE;
457 
458 			if (*name == '\0')
459 				continue;	/* Skip null token */
460 
461 			/*
462 			 * First, determine if the token represents a class or
463 			 * extra.
464 			 */
465 			if (process_options(name, set, dbp, _Dbg_options))
466 				continue;
467 			switch (caller) {
468 			case DBG_CALLER_LD:	/* ld only tokens */
469 				if (process_options(name, set, dbp,
470 				    _Dbg_options_ld))
471 					continue;
472 				break;
473 			case DBG_CALLER_RTLD:	/* rtld only tokens */
474 				if (process_options(name, set, dbp,
475 				    _Dbg_options_rtld))
476 					continue;
477 				break;
478 			}
479 
480 			/* The remaining options do not accept negation */
481 			if (!set) {
482 				dbg_print(0, MSG_INTL(MSG_USE_CNTNEGOPT), name);
483 				continue;
484 			}
485 
486 			/*
487 			 * Is it an 'output=' token? This item is a special
488 			 * case because it depends on the presence of
489 			 * a non-NULL outfile argument, and because the
490 			 * part following the '=' is variable.
491 			 */
492 			if ((outfile != NULL) &&
493 			    strncmp(name, MSG_ORIG(MSG_TOK_OUTFILE),
494 			    MSG_TOK_OUTFILE_SIZE) == 0) {
495 				if (conv_strproc_extract_value(name,
496 				    MSG_TOK_OUTFILE_SIZE, 0, outfile))
497 					continue;
498 			}
499 
500 			/*
501 			 * Only the rtld "lmid" token is left.
502 			 */
503 			if ((caller == DBG_CALLER_RTLD) && (strncmp(name,
504 			    MSG_ORIG(MSG_TOK_LMID), MSG_TOK_LMID_SIZE) == 0) &&
505 			    process_lmid(name, dbp))
506 				continue;
507 
508 			/* If we make it here, the token is not understood */
509 			dbg_print(0, MSG_INTL(MSG_USE_UNRECOG), name);
510 
511 		} while ((name = strtok_r(NULL, delimit, &lasts)) != NULL);
512 	}
513 
514 	/*
515 	 * If the debug help option was specified and this is the only debug
516 	 * class, return an indication that the user should exit.
517 	 */
518 	if ((_Dbg_cnt++ == 0) && (dbp->d_extra & DBG_E_HELP) &&
519 	    (dbp->d_class == 0))
520 		dbp->d_extra |= DBG_E_HELP_EXIT;
521 
522 	return (1);
523 }
524 
525 /*
526  * Define our own printing routine.  This provides a basic fallback, as ld(1)
527  * and ld.so.1(1) provide their own routines that augment their diagnostic
528  * output, and direct the output to stderr.  This item should be defined
529  * NODIRECT.
530  */
531 /* PRINTFLIKE2 */
532 void
533 dbg_print(Lm_list *lml, const char *format, ...)
534 {
535 	va_list ap;
536 
537 #if	defined(lint)
538 	/*
539 	 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
540 	 * Supress the lint error by making a dummy assignment.
541 	 */
542 	lml = 0;
543 #endif
544 	va_start(ap, format);
545 	(void) vprintf(format, ap);
546 	(void) printf(MSG_ORIG(MSG_STR_NL));
547 	va_end(ap);
548 }
549 
550 /*
551  * Return an internationalized state transition string. These are used by
552  * various debugging output.
553  */
554 const char *
555 Dbg_state_str(dbg_state_t type)
556 {
557 	static const Msg state[DBG_STATE_NUM] = {
558 		MSG_STR_ADD,		/* MSG_INTL(MSG_STR_ADD)  */
559 		MSG_STR_CURRENT,	/* MSG_INTL(MSG_STR_CURRENT) */
560 		MSG_STR_EXCLUDE,	/* MSG_INTL(MSG_STR_EXCLUDE) */
561 		MSG_STR_IGNORE,		/* MSG_INTL(MSG_STR_IGNORE) */
562 		MSG_STR_MOD_BEFORE,	/* MSG_INTL(MSG_STR_MOD_BEFORE) */
563 		MSG_STR_MOD_AFTER,	/* MSG_INTL(MSG_STR_MOD_AFTER) */
564 		MSG_STR_NEW,		/* MSG_INTL(MSG_STR_NEW) */
565 		MSG_STR_NEW_IMPLICIT,	/* MSG_INTL(MSG_STR_NEW_IMPLICIT) */
566 		MSG_STR_RESET,		/* MSG_INTL(MSG_STR_RESET) */
567 		MSG_STR_ORIGINAL,	/* MSG_INTL(MSG_STR_ORIGINAL) */
568 		MSG_STR_RESOLVED,	/* MSG_INTL(MSG_STR_RESOLVED) */
569 	};
570 #if DBG_STATE_NUM != (DBG_STATE_RESOLVED + 1)
571 #error DBG_SEG_NUM has changed. Update segtype[]
572 #endif
573 
574 	assert(type < DBG_STATE_NUM);
575 	return (MSG_INTL(state[type]));
576 }
577