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 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include "_synonyms.h" 29 30 #include <sys/mman.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <limits.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <rtc.h> 38 #include <debug.h> 39 #include <conv.h> 40 #include "_rtld.h" 41 #include "msg.h" 42 43 static Config _config = { 0 }; 44 Config * config = &_config; 45 46 47 /* 48 * Validate a configuration file. 49 */ 50 static void 51 elf_config_validate(Addr addr, Rtc_head *head, Rt_map *lmp) 52 { 53 Lm_list *lml = LIST(lmp); 54 const char *str, *strtbl = config->c_strtbl; 55 Rtc_obj *obj; 56 Rtc_dir *dirtbl; 57 Rtc_file *filetbl; 58 struct stat status; 59 int err; 60 61 /* 62 * If this configuration file is for a specific application make sure 63 * we've been invoked by the application. Note that we only check the 64 * basename component of the application as the original application 65 * and its cached equivalent are never going to have the same pathnames. 66 * Also, we use PATHNAME() and not NAME() - this catches things like vi 67 * that exec shells using execv(/usr/bin/ksh, sh ...). 68 */ 69 if (head->ch_app) { 70 char *_str, *_cname, *cname, *aname = PATHNAME(lmp); 71 72 obj = (Rtc_obj *)(head->ch_app + addr); 73 cname = _cname = (char *)(strtbl + obj->co_name); 74 75 if ((_str = strrchr(aname, '/')) != NULL) 76 aname = ++_str; 77 if ((_str = strrchr(cname, '/')) != NULL) 78 cname = ++_str; 79 80 if (strcmp(aname, cname)) { 81 /* 82 * It's possible a user is trying to ldd(1) an alternate 83 * shared object and point to a configuration file that 84 * the shared object is part of. In this case ignore 85 * any mismatch name warnings. 86 */ 87 if ((lml->lm_flags & LML_FLG_TRC_ENABLE) && 88 ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0)) { 89 eprintf(lml, ERR_WARNING, 90 MSG_INTL(MSG_CONF_APP), config->c_name, 91 _cname); 92 return; 93 } 94 } 95 96 /* 97 * If we have a valid alternative application reset its original 98 * name for possible $ORIGIN processing. 99 */ 100 if ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0) { 101 ORIGNAME(lmp) = _cname; 102 DIRSZ(lmp) = cname - _cname - 1; 103 } 104 } 105 106 /* 107 * If alternative objects are specified traverse the directories 108 * specified in the configuration file, if any directory is newer than 109 * the time it was recorded in the cache then continue to inspect its 110 * files. Any file determined newer than its configuration recording 111 * questions the the use of any alternative objects. The intent here 112 * is to make sure no-one abuses a configuration as a means of static 113 * linking. 114 */ 115 for (dirtbl = (Rtc_dir *)(head->ch_dir + addr); 116 dirtbl->cd_obj; dirtbl++) { 117 /* 118 * Skip directories that provide no files - this also catches 119 * RTC_OBJ_NOEXIST directories. 120 */ 121 filetbl = (Rtc_file *)(dirtbl->cd_file + addr); 122 if (filetbl->cf_obj == 0) 123 continue; 124 125 /* 126 * Skip directories that haven't provided real, dumped files. 127 */ 128 obj = (Rtc_obj *)(dirtbl->cd_obj + addr); 129 if ((obj->co_flags & (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) != 130 (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) 131 continue; 132 133 str = strtbl + obj->co_name; 134 135 if (stat(str, &status) != 0) { 136 err = errno; 137 eprintf(lml, ERR_WARNING, MSG_INTL(MSG_CONF_DSTAT), 138 config->c_name, str, strerror(err)); 139 continue; 140 } 141 142 if (status.st_mtime == obj->co_info) 143 continue; 144 145 /* 146 * The system directory is newer than the configuration files 147 * entry, start checking any dumped files. 148 */ 149 for (; filetbl->cf_obj; filetbl++) { 150 obj = (Rtc_obj *)(filetbl->cf_obj + addr); 151 str = strtbl + obj->co_name; 152 153 /* 154 * Skip any files that aren't real, dumped files. 155 */ 156 if ((obj->co_flags & 157 (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) != 158 (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) 159 continue; 160 161 if (stat(str, &status) != 0) { 162 err = errno; 163 eprintf(lml, ERR_WARNING, 164 MSG_INTL(MSG_CONF_FSTAT), config->c_name, 165 str, strerror(err)); 166 continue; 167 } 168 169 /* 170 * If the files size is different somethings been 171 * changed. 172 */ 173 if (status.st_size != obj->co_info) { 174 eprintf(lml, ERR_WARNING, 175 MSG_INTL(MSG_CONF_FCMP), config->c_name, 176 str); 177 } 178 } 179 } 180 } 181 182 int 183 elf_config(Rt_map *lmp, int aout) 184 { 185 Rtc_head *head; 186 int fd, features = 0; 187 struct stat status; 188 Addr addr; 189 Pnode *pnp; 190 const char *str = config->c_name; 191 192 /* 193 * If an alternative configuration file has been specified use it 194 * (expanding any tokens), otherwise try opening up the default. 195 */ 196 if ((str == 0) && ((rtld_flags & RT_FL_CONFAPP) == 0)) 197 #if defined(_ELF64) 198 str = MSG_ORIG(MSG_PTH_CONFIG_64); 199 #else 200 str = MSG_ORIG(MSG_PTH_CONFIG); 201 #endif 202 else if (rtld_flags & RT_FL_SECURE) 203 return (0); 204 else { 205 size_t size; 206 char *name; 207 208 /* 209 * If we're dealing with an alternative application, fabricate 210 * the need for a $ORIGIN/ld.config.app-name configuration file. 211 */ 212 if (rtld_flags & RT_FL_CONFAPP) { 213 char _name[PATH_MAX]; 214 215 if ((str = strrchr(PATHNAME(lmp), '/')) != NULL) 216 str++; 217 else 218 str = PATHNAME(lmp); 219 220 (void) snprintf(_name, PATH_MAX, 221 MSG_ORIG(MSG_ORG_CONFIG), str); 222 str = _name; 223 } 224 225 size = strlen(str); 226 name = (char *)str; 227 228 if (expand(&name, &size, 0, 0, 229 (PN_TKN_ISALIST | PN_TKN_HWCAP), lmp) == 0) 230 return (0); 231 str = (const char *)name; 232 } 233 config->c_name = str; 234 235 /* 236 * If we can't open the configuration file return silently. 237 */ 238 if ((fd = open(str, O_RDONLY, 0)) == -1) 239 return (DBG_CONF_PRCFAIL); 240 241 /* 242 * Determine the configuration file size and map the file. 243 */ 244 (void) fstat(fd, &status); 245 if (status.st_size < sizeof (Rtc_head)) { 246 (void) close(fd); 247 return (DBG_CONF_CORRUPT); 248 } 249 if ((addr = (Addr)mmap(0, status.st_size, PROT_READ, MAP_SHARED, 250 fd, 0)) == (Addr)MAP_FAILED) { 251 (void) close(fd); 252 return (DBG_CONF_PRCFAIL); 253 } 254 255 config->c_bgn = addr; 256 config->c_end = addr + status.st_size; 257 (void) close(fd); 258 259 head = (Rtc_head *)addr; 260 261 /* 262 * Make sure we can handle this version of the configuration file. 263 */ 264 if (head->ch_version > RTC_VER_CURRENT) 265 return (DBG_CONF_VERSION); 266 267 /* 268 * When crle(1) creates a temporary configuration file the 269 * RTC_HDR_IGNORE flag is set. Thus the mapping of the configuration 270 * file is taken into account but not its content. 271 */ 272 if (head->ch_cnflags & RTC_HDR_IGNORE) 273 return (DBG_CONF_IGNORE); 274 275 /* 276 * Apply any new default library pathname. 277 */ 278 if (head->ch_edlibpath) { 279 str = (const char *)(head->ch_edlibpath + addr); 280 #ifndef SGS_PRE_UNIFIED_PROCESS 281 if ((head->ch_cnflags & RTC_HDR_UPM) == 0) { 282 #if defined(_ELF64) 283 str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIB_64), 284 MSG_ORIG(MSG_PTH_LIB_64), MSG_PTH_LIB_64_SIZE); 285 #else 286 str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIB), 287 MSG_ORIG(MSG_PTH_LIB), MSG_PTH_LIB_SIZE); 288 #endif 289 } 290 #endif 291 if ((pnp = expand_paths(lmp, str, 292 (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 293 elf_fct.fct_dflt_dirs = pnp; 294 features |= CONF_EDLIBPATH; 295 } 296 if (head->ch_eslibpath) { 297 str = (const char *)(head->ch_eslibpath + addr); 298 #ifndef SGS_PRE_UNIFIED_PROCESS 299 if ((head->ch_cnflags & RTC_HDR_UPM) == 0) { 300 #if defined(_ELF64) 301 str = conv_config_upm(str, 302 MSG_ORIG(MSG_PTH_USRLIBSE_64), 303 MSG_ORIG(MSG_PTH_LIBSE_64), MSG_PTH_LIBSE_64_SIZE); 304 #else 305 str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIBSE), 306 MSG_ORIG(MSG_PTH_LIBSE), MSG_PTH_LIBSE_SIZE); 307 #endif 308 } 309 #endif 310 if ((pnp = expand_paths(lmp, str, 311 (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 312 elf_fct.fct_secure_dirs = pnp; 313 features |= CONF_ESLIBPATH; 314 } 315 #if defined(__sparc) && !defined(_ELF64) 316 if (head->ch_adlibpath) { 317 str = (const char *)(head->ch_adlibpath + addr); 318 if ((pnp = expand_paths(lmp, str, 319 (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 320 aout_fct.fct_dflt_dirs = pnp; 321 features |= CONF_ADLIBPATH; 322 } 323 if (head->ch_aslibpath) { 324 str = (const char *)(head->ch_aslibpath + addr); 325 if ((pnp = expand_paths(lmp, str, 326 (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 327 aout_fct.fct_secure_dirs = pnp; 328 features |= CONF_ASLIBPATH; 329 } 330 #endif 331 /* 332 * Apply any environment variables. This attribute was added with 333 * RTC_VER_THREE. 334 */ 335 if ((head->ch_version >= RTC_VER_THREE) && head->ch_env && 336 (!(rtld_flags & RT_FL_NOENVCFG))) { 337 if (readenv_config((Rtc_env *)(head->ch_env + addr), 338 addr, aout) != 0) 339 return (-1); 340 features |= CONF_ENVS; 341 } 342 343 /* 344 * Determine whether filter/filtee associations are available. 345 */ 346 if ((head->ch_version >= RTC_VER_FOUR) && head->ch_fltr && 347 (!(rtld_flags2 & RT_FL2_NOFLTCFG))) { 348 rtld_flags2 |= RT_FL2_FLTCFG; 349 config->c_fltr = (Rtc_fltr *)(head->ch_fltr + addr); 350 config->c_flte = (Rtc_flte *)(head->ch_flte + addr); 351 features |= CONF_FLTR; 352 } 353 354 /* 355 * Determine whether directory configuration is available. 356 */ 357 if ((!(rtld_flags & RT_FL_NODIRCFG)) && head->ch_hash) { 358 config->c_hashtbl = (Word *)(head->ch_hash + addr); 359 config->c_hashchain = &config->c_hashtbl[2 + 360 config->c_hashtbl[0]]; 361 config->c_objtbl = (Rtc_obj *)(head->ch_obj + addr); 362 config->c_strtbl = (const char *)(head->ch_str + addr); 363 364 rtld_flags |= RT_FL_DIRCFG; 365 features |= CONF_DIRCFG; 366 } 367 368 /* 369 * Determine whether alternative objects are specified or an object 370 * reservation area is required. If the reservation can't be completed 371 * (either because the configuration information is out-of-date, or the 372 * the reservation can't be allocated), then alternative objects are 373 * ignored. 374 */ 375 if ((!(rtld_flags & (RT_FL_NODIRCFG | RT_FL_NOOBJALT))) && 376 (head->ch_cnflags & RTC_HDR_ALTER)) { 377 rtld_flags |= RT_FL_OBJALT; 378 features |= CONF_OBJALT; 379 380 elf_config_validate(addr, head, lmp); 381 382 if (head->ch_resbgn) { 383 384 if (((config->c_bgn <= head->ch_resbgn) && 385 (config->c_bgn >= head->ch_resend)) || 386 (nu_map(LIST(lmp), 387 (caddr_t)(uintptr_t)head->ch_resbgn, 388 (head->ch_resend - head->ch_resbgn), PROT_NONE, 389 MAP_FIXED | MAP_PRIVATE) == MAP_FAILED)) 390 return (-1); 391 392 rtld_flags |= RT_FL_MEMRESV; 393 features |= CONF_MEMRESV; 394 } 395 } 396 397 return (features); 398 } 399 400 /* 401 * Determine whether the given file exists in the configuration file. 402 */ 403 Rtc_obj * 404 elf_config_ent(const char *name, Word hash, int id, const char **alternate) 405 { 406 Word bkt, ndx; 407 const char *str; 408 Rtc_obj *obj; 409 410 bkt = hash % config->c_hashtbl[0]; 411 ndx = config->c_hashtbl[2 + bkt]; 412 413 while (ndx) { 414 obj = config->c_objtbl + ndx; 415 str = config->c_strtbl + obj->co_name; 416 417 if ((obj->co_hash != hash) || (strcmp(name, str) != 0) || 418 (id && (id != obj->co_id))) { 419 ndx = config->c_hashchain[ndx]; 420 continue; 421 } 422 423 if ((obj->co_flags & RTC_OBJ_ALTER) && alternate) 424 *alternate = config->c_strtbl + obj->co_alter; 425 426 return (obj); 427 } 428 return (0); 429 } 430 431 /* 432 * Determine whether a filter and filtee string pair exists in the configuration 433 * file. If so, return the cached filtees that are associated with this pair as 434 * a Pnode list. 435 */ 436 Pnode * 437 elf_config_flt(Lm_list *lml, const char *filter, const char *string) 438 { 439 Rtc_fltr * fltrtbl; 440 Pnode * pnp = 0, *npnp, *opnp = 0; 441 442 for (fltrtbl = (Rtc_fltr *)config->c_fltr; fltrtbl->fr_filter; 443 fltrtbl++) { 444 Rtc_flte *fltetbl; 445 const char *fltr, *str; 446 447 fltr = config->c_strtbl + fltrtbl->fr_filter; 448 str = config->c_strtbl + fltrtbl->fr_string; 449 if (strcmp(filter, fltr) || strcmp(string, str)) 450 continue; 451 452 /* 453 * Create a pnode list for each filtee associated with this 454 * filter/filtee string pair. Note, no expansion of filtee 455 * entries is called for, as any original expansion would have 456 * been carried out before they were recorded in the 457 * configuration file. 458 */ 459 /* LINTED */ 460 for (fltetbl = (Rtc_flte *)((char *)config->c_flte + 461 fltrtbl->fr_filtee); fltetbl->fe_filtee; fltetbl++) { 462 const char *flte; 463 464 flte = config->c_strtbl + fltetbl->fe_filtee; 465 466 if (((npnp = calloc(1, sizeof (Pnode))) == 0) || 467 ((npnp->p_name = strdup(flte)) == 0)) 468 return (0); 469 470 DBG_CALL(Dbg_file_filter(lml, fltr, flte, 1)); 471 472 if (opnp == 0) 473 pnp = npnp; 474 else 475 opnp->p_next = npnp; 476 477 npnp->p_len = strlen(flte) + 1; 478 npnp->p_orig = LA_SER_CONFIG; 479 480 opnp = npnp; 481 } 482 return (pnp); 483 } 484 return (0); 485 } 486