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 <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 44 static int dbg_fd; /* debugging output file descriptor */ 45 static dev_t dbg_dev; 46 static rtld_ino_t dbg_ino; 47 static int dbg_add_pid; /* True to add pid to debug file name */ 48 static 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 */ 54 int 55 dbg_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, generate help 149 * output if the user specified the debug help token. 150 */ 151 if (dbp->d_extra & DBG_E_HELP) 152 Dbg_help(); 153 154 return (1); 155 } 156 157 /* 158 * Return True (1) if dbg_print() should produce output for the 159 * specified link-map list, and False (0) otherwise. 160 */ 161 static int 162 dbg_lmid_validate(Lm_list *lml) 163 { 164 const char *str; 165 Aliste idx; 166 167 /* 168 * The LDSO link-map list is a special case, requiring 169 * an explicit user request. 170 */ 171 if (lml->lm_flags & LML_FLG_RTLDLM) 172 return ((dbg_desc->d_extra & DBG_E_LMID_LDSO) != 0); 173 174 /* 175 * Approve special cases: 176 * - The link-map list has no name 177 * - lmid=all was set 178 * - lmid=alt was set, and this is not the BASE linkmap 179 */ 180 if ((lml->lm_lmidstr == NULL) || 181 ((dbg_desc->d_extra & DBG_E_LMID_ALL) != 0) || 182 (((dbg_desc->d_extra & DBG_E_LMID_ALT) != 0) && 183 ((lml->lm_flags & LML_FLG_BASELM) == 0))) 184 return (1); 185 186 /* 187 * If there is no list of specific link-map list names to check, 188 * then approval depends on lmid={ldso|alt} not being specified. 189 */ 190 if (aplist_nitems(dbg_desc->d_list) == 0) 191 return ((dbg_desc->d_extra & 192 (DBG_E_LMID_LDSO | DBG_E_LMID_ALT)) == 0); 193 194 /* 195 * Compare the link-map list name against the list of approved names 196 */ 197 for (APLIST_TRAVERSE(dbg_desc->d_list, idx, str)) 198 if (strcmp(lml->lm_lmidstr, str) == 0) 199 return (1); 200 201 /* Output for this linkmap is denied */ 202 return (0); 203 } 204 205 /* 206 * All diagnostic requests are funneled to this routine. 207 */ 208 /* PRINTFLIKE2 */ 209 void 210 dbg_print(Lm_list *lml, const char *format, ...) 211 { 212 va_list args; 213 char buffer[ERRSIZE + 1]; 214 pid_t _pid; 215 rtld_stat_t status; 216 Prfbuf prf; 217 218 /* 219 * Knock off any newline indicator to signify that a diagnostic has 220 * been processed. 221 */ 222 dbg_desc->d_extra &= ~DBG_E_STDNL; 223 224 /* 225 * If debugging has been isolated to individual link-map lists, 226 * determine whether this request originates from a link-map list that 227 * is being monitored. 228 */ 229 if (lml && (dbg_lmid_validate(lml) == 0)) 230 return; 231 232 /* 233 * If we're in the application make sure the debugging file descriptor 234 * is still available (ie, the user hasn't closed and/or reused the 235 * same descriptor). 236 */ 237 if (rtld_flags & RT_FL_APPLIC) { 238 if ((rtld_fstat(dbg_fd, &status) == -1) || 239 (status.st_dev != dbg_dev) || 240 (status.st_ino != dbg_ino)) { 241 if (dbg_file) { 242 /* 243 * If the user specified output file has been 244 * disconnected try and reconnect to it. 245 */ 246 char _file[MAXPATHLEN]; 247 const char *file; 248 249 if (dbg_add_pid) { 250 file = _file; 251 (void) snprintf(_file, MAXPATHLEN, 252 MSG_ORIG(MSG_DBG_FILE), dbg_file, 253 pid); 254 } else { 255 file = dbg_file; 256 } 257 if ((dbg_fd = open(file, (O_RDWR | O_APPEND), 258 0)) == -1) { 259 dbg_desc->d_class = 0; 260 return; 261 } 262 (void) rtld_fstat(dbg_fd, &status); 263 dbg_dev = status.st_dev; 264 dbg_ino = status.st_ino; 265 } else { 266 /* 267 * If stderr has been stolen from us simply 268 * turn debugging off. 269 */ 270 dbg_desc->d_class = 0; 271 return; 272 } 273 } 274 } 275 276 prf.pr_fd = dbg_fd; 277 278 /* 279 * Obtain the process id. 280 */ 281 _pid = getpid(); 282 283 /* 284 * Each time ld.so.1 is entered, the diagnostic times are reset. It is 285 * useful to convey this reset as part of our diagnostics, but only if 286 * other diagnostics will follow. If a reset has preceded this 287 * diagnostic, print a division line. 288 */ 289 if (DBG_ISRESET()) { 290 DBG_OFFRESET(); 291 292 prf.pr_buf = prf.pr_cur = buffer; 293 prf.pr_len = ERRSIZE; 294 295 if (lml) 296 (void) bufprint(&prf, MSG_ORIG(MSG_DBG_PID), _pid); 297 else 298 (void) bufprint(&prf, MSG_ORIG(MSG_DBG_UNDEF)); 299 prf.pr_cur--; 300 301 (void) bufprint(&prf, MSG_ORIG(MSG_DBG_RESET)); 302 (void) dowrite(&prf); 303 } 304 305 /* 306 * Reestablish the buffer for standard printing. 307 */ 308 prf.pr_buf = prf.pr_cur = buffer; 309 prf.pr_len = ERRSIZE; 310 311 /* 312 * Establish any diagnostic prefix strings. 313 */ 314 if (lml) 315 (void) bufprint(&prf, MSG_ORIG(MSG_DBG_PID), _pid); 316 else 317 (void) bufprint(&prf, MSG_ORIG(MSG_DBG_UNDEF)); 318 prf.pr_cur--; 319 320 if (DBG_ISLMID() && lml && lml->lm_lmidstr) { 321 (void) bufprint(&prf, MSG_ORIG(MSG_DBG_LMID), lml->lm_lmidstr); 322 prf.pr_cur--; 323 } 324 if (DBG_ISTIME()) { 325 struct timeval new; 326 327 if (gettimeofday(&new, NULL) == 0) { 328 Conv_time_buf_t buf; 329 330 if (DBG_ISTTIME()) { 331 (void) bufprint(&prf, 332 conv_time(&DBG_TOTALTIME, &new, &buf)); 333 prf.pr_cur--; 334 } 335 if (DBG_ISDTIME()) { 336 (void) bufprint(&prf, 337 conv_time(&DBG_DELTATIME, &new, &buf)); 338 prf.pr_cur--; 339 } 340 DBG_DELTATIME = new; 341 } 342 } 343 if (rtld_flags & RT_FL_THREADS) { 344 (void) bufprint(&prf, MSG_ORIG(MSG_DBG_THREAD), rt_thr_self()); 345 prf.pr_cur--; 346 } 347 348 /* 349 * Format the message and print it. 350 */ 351 va_start(args, format); 352 (void) doprf(format, args, &prf); 353 *(prf.pr_cur - 1) = '\n'; 354 (void) dowrite(&prf); 355 va_end(args); 356 } 357