xref: /illumos-gate/usr/src/cmd/sgs/libld/common/debug.c (revision 6d02032db7b674f185405d42cc8bf10a46a9ab3a)
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	<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, identify the linker
126 	 * package, and generate help output if the user specified the
127 	 * debug help token.
128 	 */
129 	Dbg_version();
130 	if (dbg_desc->d_extra & DBG_E_HELP)
131 		Dbg_help();
132 
133 	return (1);
134 }
135 
136 /* PRINTFLIKE2 */
137 void
138 dbg_print(Lm_list *lml, const char *format, ...)
139 {
140 	static char	*prestr = NULL;
141 	va_list		args;
142 
143 #if	defined(lint)
144 	/*
145 	 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
146 	 * Supress the lint error by making a dummy assignment.
147 	 */
148 	lml = NULL;
149 #endif
150 	/*
151 	 * Knock off any newline indicator to signify that a diagnostic has
152 	 * been processed.
153 	 */
154 	dbg_desc->d_extra &= ~DBG_E_STDNL;
155 
156 	if (DBG_ISSNAME()) {
157 		/*
158 		 * If the debugging options have requested each diagnostic line
159 		 * be prepended by a name create a prefix string.
160 		 */
161 		if ((prestr == NULL) && *Name) {
162 			const char	*name, *cls;
163 			size_t		len;
164 
165 			/*
166 			 * Select the fullname or basename of the output file
167 			 * being created.
168 			 */
169 			if (DBG_ISFNAME())
170 				name = *Name;
171 			else {
172 				if ((name =
173 				    strrchr(*Name, '/')) == NULL)
174 					name = *Name;
175 				else
176 					name++;
177 			}
178 			len = strlen(name) +
179 			    strlen(MSG_INTL(MSG_DBG_NAME_FMT)) + 1;
180 
181 			/*
182 			 * Add the output file class if required.
183 			 */
184 			if (DBG_ISCLASS()) {
185 #if	defined(_ELF64)
186 				len += MSG_DBG_CLS64_FMT_SIZE;
187 				cls = MSG_ORIG(MSG_DBG_CLS64_FMT);
188 #else
189 				len += MSG_DBG_CLS32_FMT_SIZE;
190 				cls = MSG_ORIG(MSG_DBG_CLS32_FMT);
191 #endif
192 			}
193 
194 			/*
195 			 * Allocate a string to build the prefix.
196 			 */
197 			if ((prestr = libld_malloc(len)) == NULL)
198 				prestr = (char *)MSG_INTL(MSG_DBG_DFLT_FMT);
199 			else {
200 				(void) snprintf(prestr, len,
201 				    MSG_INTL(MSG_DBG_NAME_FMT), name);
202 				if (DBG_ISCLASS())
203 					(void) strcat(prestr, cls);
204 			}
205 		}
206 		(void) fputs(prestr ? prestr : MSG_INTL(MSG_DBG_AOUT_FMT),
207 		    dbg_ofile.fptr);
208 	} else
209 		(void) fputs(MSG_INTL(MSG_DBG_DFLT_FMT), dbg_ofile.fptr);
210 
211 	if (DBG_ISTIME()) {
212 		Conv_time_buf_t	buf;
213 		struct timeval	new;
214 
215 		if (gettimeofday(&new, NULL) == 0) {
216 			if (DBG_ISTTIME())
217 				(void) fputs(conv_time(&DBG_TOTALTIME, &new,
218 				    &buf), stderr);
219 			if (DBG_ISDTIME())
220 				(void) fputs(conv_time(&DBG_DELTATIME, &new,
221 				    &buf), stderr);
222 
223 			DBG_DELTATIME = new;
224 		}
225 	}
226 
227 	va_start(args, format);
228 	(void) vfprintf(dbg_ofile.fptr, format, args);
229 	(void) fprintf(dbg_ofile.fptr, MSG_ORIG(MSG_STR_NL));
230 	va_end(args);
231 }
232