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	<sys/types.h>
28#include	<sys/stat.h>
29#include	<sys/param.h>
30#include	<stdio.h>
31#include	<fcntl.h>
32#include	<stdarg.h>
33#include	<dlfcn.h>
34#include	<unistd.h>
35#include	<string.h>
36#include	<thread.h>
37#include	<debug.h>
38#include	<conv.h>
39#include	"_rtld.h"
40#include	"_elf.h"
41#include	"msg.h"
42
43
44static int		dbg_fd;		/* debugging output file descriptor */
45static dev_t		dbg_dev;
46static rtld_ino_t	dbg_ino;
47static int		dbg_add_pid;	/* True to add pid to debug file name */
48static pid_t		pid;
49
50/*
51 * Enable diagnostic output.  All debugging functions reside in the linker
52 * debugging library liblddbg.so which is lazy loaded when required.
53 */
54int
55dbg_setup(const char *options, Dbg_desc *dbp)
56{
57	rtld_stat_t	status;
58	const char	*ofile;
59
60	/*
61	 * If we're running secure, only allow debugging if ld.so.1 itself is
62	 * owned by root and has its mode setuid.  Fail silently.
63	 */
64	if ((rtld_flags & RT_FL_SECURE) && (is_rtld_setuid() == 0))
65		return (1);
66
67	/*
68	 * As Dbg_setup() will effectively lazy load the necessary support
69	 * libraries, make sure ld.so.1 is initialized for plt relocations.
70	 */
71	if (elf_rtld_load() == 0)
72		return (1);
73
74	/*
75	 * Call the debugging setup routine.  This function verifies the
76	 * debugging tokens provided and returns a mask indicating the debugging
77	 * categories selected.  The mask effectively enables calls to the
78	 * debugging library.
79	 */
80	if (Dbg_setup(DBG_CALLER_RTLD, options, dbp, &ofile) == 0)
81		return (0);
82
83	/*
84	 * Obtain the process id.
85	 */
86	pid = getpid();
87
88	/*
89	 * If an LD_DEBUG_OUTPUT file was specified then we need to direct all
90	 * diagnostics to the specified file.  Add the process id as a file
91	 * suffix so that multiple processes that inherit the same debugging
92	 * environment variable don't fight over the same file.
93	 *
94	 * If LD_DEBUG_OUTPUT is not specified, and the output=file token
95	 * was, then we direct all diagnostics to that file. Unlike
96	 * LD_DEBUG_OUTPUT, we do not add the process id suffix. This
97	 * is more convenient for interactive use.
98	 *
99	 * If neither redirection option is present, we send debugging
100	 * output to stderr. Note that the caller will not be able
101	 * to pipe or redirect this output at the shell level. libc
102	 * has not yet initialized things to make that possible.
103	 */
104	if (dbg_file == NULL) {
105		if (ofile && (*ofile != '\0'))
106			dbg_file = ofile;
107	} else {
108		dbg_add_pid = 1;
109	}
110
111	if (dbg_file) {
112		char 		_file[MAXPATHLEN];
113		const char	*file;
114
115		if (dbg_add_pid) {
116			file = _file;
117			(void) snprintf(_file, MAXPATHLEN,
118			    MSG_ORIG(MSG_DBG_FILE), dbg_file, pid);
119		} else {
120			file = dbg_file;
121		}
122		dbg_fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0666);
123		if (dbg_fd == -1) {
124			int	err = errno;
125
126			eprintf(&lml_rtld, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN),
127			    file, strerror(err));
128			dbp->d_class = 0;
129			return (0);
130		}
131	} else {
132		/*
133		 * The default is to direct debugging to the stderr.
134		 */
135		dbg_fd = 2;
136	}
137
138	/*
139	 * Initialize the dev/inode pair to enable us to determine if
140	 * the debugging file descriptor is still available once the
141	 * application has been entered.
142	 */
143	(void) rtld_fstat(dbg_fd, &status);
144	dbg_dev = status.st_dev;
145	dbg_ino = status.st_ino;
146
147	/*
148	 * Now that the output file is established, identify the linker
149	 * package, and generate help output if the user specified the
150	 * debug help token.
151	 */
152	Dbg_version();
153	if (dbp->d_extra & DBG_E_HELP)
154		Dbg_help();
155
156	return (1);
157}
158
159/*
160 * Return True (1) if dbg_print() should produce output for the
161 * specified link-map list, and False (0) otherwise.
162 */
163static int
164dbg_lmid_validate(Lm_list *lml)
165{
166	const char	*str;
167	Aliste		idx;
168
169	/*
170	 * The LDSO link-map list is a special case, requiring
171	 * an explicit user request.
172	 */
173	if (lml->lm_flags & LML_FLG_RTLDLM)
174		return ((dbg_desc->d_extra & DBG_E_LMID_LDSO) != 0);
175
176	/*
177	 * Approve special cases:
178	 * -	The link-map list has no name
179	 * -	lmid=all was set
180	 * -	lmid=alt was set, and this is not the BASE linkmap
181	 */
182	if ((lml->lm_lmidstr == NULL) ||
183	    ((dbg_desc->d_extra & DBG_E_LMID_ALL) != 0) ||
184	    (((dbg_desc->d_extra & DBG_E_LMID_ALT) != 0) &&
185	    ((lml->lm_flags & LML_FLG_BASELM) == 0)))
186		return (1);
187
188	/*
189	 * If there is no list of specific link-map list names to check,
190	 * then approval depends on lmid={ldso|alt} not being specified.
191	 */
192	if (aplist_nitems(dbg_desc->d_list) == 0)
193		return ((dbg_desc->d_extra &
194		    (DBG_E_LMID_LDSO | DBG_E_LMID_ALT)) == 0);
195
196	/*
197	 * Compare the link-map list name against the list of approved names
198	 */
199	for (APLIST_TRAVERSE(dbg_desc->d_list, idx, str))
200		if (strcmp(lml->lm_lmidstr, str) == 0)
201			return (1);
202
203	/* Output for this linkmap is denied */
204	return (0);
205}
206
207/*
208 * All diagnostic requests are funneled to this routine.
209 */
210/* PRINTFLIKE2 */
211void
212dbg_print(Lm_list *lml, const char *format, ...)
213{
214	va_list		args;
215	char		buffer[ERRSIZE + 1];
216	pid_t		_pid;
217	rtld_stat_t	status;
218	Prfbuf		prf;
219
220	/*
221	 * Knock off any newline indicator to signify that a diagnostic has
222	 * been processed.
223	 */
224	dbg_desc->d_extra &= ~DBG_E_STDNL;
225
226	/*
227	 * If debugging has been isolated to individual link-map lists,
228	 * determine whether this request originates from a link-map list that
229	 * is being monitored.
230	 */
231	if (lml && (dbg_lmid_validate(lml) == 0))
232		return;
233
234	/*
235	 * If we're in the application make sure the debugging file descriptor
236	 * is still available (ie, the user hasn't closed and/or reused the
237	 * same descriptor).
238	 */
239	if (rtld_flags & RT_FL_APPLIC) {
240		if ((rtld_fstat(dbg_fd, &status) == -1) ||
241		    (status.st_dev != dbg_dev) ||
242		    (status.st_ino != dbg_ino)) {
243			if (dbg_file) {
244				/*
245				 * If the user specified output file has been
246				 * disconnected try and reconnect to it.
247				 */
248				char 		_file[MAXPATHLEN];
249				const char	*file;
250
251				if (dbg_add_pid) {
252					file = _file;
253					(void) snprintf(_file, MAXPATHLEN,
254					    MSG_ORIG(MSG_DBG_FILE), dbg_file,
255					    pid);
256				} else {
257					file = dbg_file;
258				}
259				if ((dbg_fd = open(file, (O_RDWR | O_APPEND),
260				    0)) == -1) {
261					dbg_desc->d_class = 0;
262					return;
263				}
264				(void) rtld_fstat(dbg_fd, &status);
265				dbg_dev = status.st_dev;
266				dbg_ino = status.st_ino;
267			} else {
268				/*
269				 * If stderr has been stolen from us simply
270				 * turn debugging off.
271				 */
272				dbg_desc->d_class = 0;
273				return;
274			}
275		}
276	}
277
278	prf.pr_fd = dbg_fd;
279
280	/*
281	 * Obtain the process id.
282	 */
283	_pid = getpid();
284
285	/*
286	 * Each time ld.so.1 is entered, the diagnostic times are reset.  It is
287	 * useful to convey this reset as part of our diagnostics, but only if
288	 * other diagnostics will follow.  If a reset has preceded this
289	 * diagnostic, print a division line.
290	 */
291	if (DBG_ISRESET()) {
292		DBG_OFFRESET();
293
294		prf.pr_buf = prf.pr_cur = buffer;
295		prf.pr_len = ERRSIZE;
296
297		if (lml)
298			(void) bufprint(&prf, MSG_ORIG(MSG_DBG_PID), _pid);
299		else
300			(void) bufprint(&prf, MSG_ORIG(MSG_DBG_UNDEF));
301		prf.pr_cur--;
302
303		(void) bufprint(&prf, MSG_ORIG(MSG_DBG_RESET));
304		(void) dowrite(&prf);
305	}
306
307	/*
308	 * Reestablish the buffer for standard printing.
309	 */
310	prf.pr_buf = prf.pr_cur = buffer;
311	prf.pr_len = ERRSIZE;
312
313	/*
314	 * Establish any diagnostic prefix strings.
315	 */
316	if (lml)
317		(void) bufprint(&prf, MSG_ORIG(MSG_DBG_PID), _pid);
318	else
319		(void) bufprint(&prf, MSG_ORIG(MSG_DBG_UNDEF));
320	prf.pr_cur--;
321
322	if (DBG_ISLMID() && lml && lml->lm_lmidstr) {
323		(void) bufprint(&prf, MSG_ORIG(MSG_DBG_LMID), lml->lm_lmidstr);
324		prf.pr_cur--;
325	}
326	if (DBG_ISTIME()) {
327		struct timeval	new;
328
329		if (gettimeofday(&new, NULL) == 0) {
330			Conv_time_buf_t	buf;
331
332			if (DBG_ISTTIME()) {
333				(void) bufprint(&prf,
334				    conv_time(&DBG_TOTALTIME, &new, &buf));
335				prf.pr_cur--;
336			}
337			if (DBG_ISDTIME()) {
338				(void) bufprint(&prf,
339				    conv_time(&DBG_DELTATIME, &new, &buf));
340				prf.pr_cur--;
341			}
342			DBG_DELTATIME = new;
343		}
344	}
345	if (rtld_flags & RT_FL_THREADS) {
346		(void) bufprint(&prf, MSG_ORIG(MSG_DBG_THREAD), rt_thr_self());
347		prf.pr_cur--;
348	}
349
350	/*
351	 * Format the message and print it.
352	 */
353	va_start(args, format);
354	(void) doprf(format, args, &prf);
355	*(prf.pr_cur - 1) = '\n';
356	(void) dowrite(&prf);
357	va_end(args);
358}
359