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 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, 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 */ 163 static int 164 dbg_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 */ 211 void 212 dbg_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