xref: /illumos-gate/usr/src/cmd/sgs/libld/common/debug.c (revision 2017c965)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include	<stdio.h>
28 #include	<stdarg.h>
29 #include	<errno.h>
30 #include	<strings.h>
31 #include	<dlfcn.h>
32 #include	<debug.h>
33 #include	<conv.h>
34 #include	"msg.h"
35 
36 /*
37  * dbg_setup() can be called a number of times.  The typical use through
38  * LD_OPTIONS, results in dbg_setup() being called as the first argument to
39  * ld(1).  It's also possible to pass debugging tokens through the compiler,
40  * for example -Wl,-Dlibs -Wl-Ddetail, in which case multiple dbg_setup()
41  * calls are made.
42  *
43  * A distinction is also made between diagnostics being requested before any
44  * other ld(1) options are read, or whether the debugging options occur
45  * between other options on the command line.  In the latter case, the
46  * debugging options can be used to isolate diagnostics around one or more
47  * input files.  The "phase" argument allows us to select which phase of
48  * dbg_setup() processing we should isolate ourselves to.
49  *
50  * dbg_print() can require the output filename for use in the diagnostics
51  * created.  Save the address of the output filename pointer for this use.
52  */
53 static const char	**Name = NULL;
54 static int		Phase = 0;
55 
56 /* Debug file output state */
57 static struct {
58 	FILE	*fptr;	/* File to send debug output */
59 	int	close_needed;	/* True if explicitly opened stream */
60 } dbg_ofile = {
61 	stderr,
62 	0
63 };
64 
65 
66 /*
67  * If there is an explicitly opened debug file, close it and reset the state.
68  */
69 void
70 dbg_cleanup(void)
71 {
72 	if (dbg_ofile.close_needed) {
73 		(void) fclose(dbg_ofile.fptr);
74 		dbg_ofile.close_needed = 0;
75 		dbg_ofile.fptr = stderr;
76 	}
77 }
78 
79 /*
80  * Process debug tokens. Returns True (1) on success, and False (0)
81  * on failure.
82  */
83 int
84 dbg_setup(Ofl_desc *ofl, const char *options, int phase)
85 {
86 	const char	*ofile;
87 
88 	if (Phase == 0)
89 		Phase = phase;
90 	else if (Phase != phase)
91 		return (1);
92 
93 	Name = &ofl->ofl_name;
94 
95 	/*
96 	 * Call the debugging setup routine to initialize the mask and
97 	 * debug function array.
98 	 */
99 	if (Dbg_setup(DBG_CALLER_LD, options, dbg_desc, &ofile) == 0)
100 		return (0);
101 
102 	/*
103 	 * If output= token was used, close the old file if necessary
104 	 * and open a new one if the file name is not NULL.
105 	 */
106 	if (ofile) {
107 		dbg_cleanup();
108 		if (*ofile != '\0') {
109 			FILE *fptr = fopen(ofile, MSG_ORIG(MSG_DBG_FOPEN_MODE));
110 			if (fptr == NULL) {
111 				int	err = errno;
112 
113 				eprintf(ofl->ofl_lml, ERR_FATAL,
114 				    MSG_INTL(MSG_SYS_OPEN), ofile,
115 				    strerror(err));
116 				return (0);
117 			} else {
118 				dbg_ofile.fptr = fptr;
119 				dbg_ofile.close_needed = 1;
120 			}
121 		}
122 	}
123 
124 	/*
125 	 * Now that the output file is established, generate help
126 	 * output if the user specified the debug help token.
127 	 */
128 	if (dbg_desc->d_extra & DBG_E_HELP)
129 		Dbg_help();
130 
131 	return (1);
132 }
133 
134 /* PRINTFLIKE2 */
135 void
136 dbg_print(Lm_list *lml, const char *format, ...)
137 {
138 	static char	*prestr = NULL;
139 	va_list		args;
140 
141 #if	defined(lint)
142 	/*
143 	 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
144 	 * Supress the lint error by making a dummy assignment.
145 	 */
146 	lml = NULL;
147 #endif
148 	/*
149 	 * Knock off any newline indicator to signify that a diagnostic has
150 	 * been processed.
151 	 */
152 	dbg_desc->d_extra &= ~DBG_E_STDNL;
153 
154 	if (DBG_ISSNAME()) {
155 		/*
156 		 * If the debugging options have requested each diagnostic line
157 		 * be prepended by a name create a prefix string.
158 		 */
159 		if ((prestr == NULL) && *Name) {
160 			const char	*name, *cls;
161 			size_t		len;
162 
163 			/*
164 			 * Select the fullname or basename of the output file
165 			 * being created.
166 			 */
167 			if (DBG_ISFNAME())
168 				name = *Name;
169 			else {
170 				if ((name =
171 				    strrchr(*Name, '/')) == NULL)
172 					name = *Name;
173 				else
174 					name++;
175 			}
176 			len = strlen(name) +
177 			    strlen(MSG_INTL(MSG_DBG_NAME_FMT)) + 1;
178 
179 			/*
180 			 * Add the output file class if required.
181 			 */
182 			if (DBG_ISCLASS()) {
183 #if	defined(_ELF64)
184 				len += MSG_DBG_CLS64_FMT_SIZE;
185 				cls = MSG_ORIG(MSG_DBG_CLS64_FMT);
186 #else
187 				len += MSG_DBG_CLS32_FMT_SIZE;
188 				cls = MSG_ORIG(MSG_DBG_CLS32_FMT);
189 #endif
190 			}
191 
192 			/*
193 			 * Allocate a string to build the prefix.
194 			 */
195 			if ((prestr = libld_malloc(len)) == NULL)
196 				prestr = (char *)MSG_INTL(MSG_DBG_DFLT_FMT);
197 			else {
198 				(void) snprintf(prestr, len,
199 				    MSG_INTL(MSG_DBG_NAME_FMT), name);
200 				if (DBG_ISCLASS())
201 					(void) strcat(prestr, cls);
202 			}
203 		}
204 		(void) fputs(prestr ? prestr : MSG_INTL(MSG_DBG_AOUT_FMT),
205 		    dbg_ofile.fptr);
206 	} else
207 		(void) fputs(MSG_INTL(MSG_DBG_DFLT_FMT), dbg_ofile.fptr);
208 
209 	if (DBG_ISTIME()) {
210 		Conv_time_buf_t	buf;
211 		struct timeval	new;
212 
213 		if (gettimeofday(&new, NULL) == 0) {
214 			if (DBG_ISTTIME())
215 				(void) fputs(conv_time(&DBG_TOTALTIME, &new,
216 				    &buf), stderr);
217 			if (DBG_ISDTIME())
218 				(void) fputs(conv_time(&DBG_DELTATIME, &new,
219 				    &buf), stderr);
220 
221 			DBG_DELTATIME = new;
222 		}
223 	}
224 
225 	va_start(args, format);
226 	(void) vfprintf(dbg_ofile.fptr, format, args);
227 	(void) fprintf(dbg_ofile.fptr, MSG_ORIG(MSG_STR_NL));
228 	va_end(args);
229 }
230