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_id *id; 186 Rtc_head *head; 187 int fd, features = 0; 188 struct stat status; 189 Addr addr; 190 Pnode *pnp; 191 const char *str = config->c_name; 192 193 /* 194 * If an alternative configuration file has been specified use it 195 * (expanding any tokens), otherwise try opening up the default. 196 */ 197 if ((str == 0) && ((rtld_flags & RT_FL_CONFAPP) == 0)) 198 #if defined(_ELF64) 199 str = MSG_ORIG(MSG_PTH_CONFIG_64); 200 #else 201 str = MSG_ORIG(MSG_PTH_CONFIG); 202 #endif 203 else if (rtld_flags & RT_FL_SECURE) 204 return (0); 205 else { 206 size_t size; 207 char *name; 208 209 /* 210 * If we're dealing with an alternative application, fabricate 211 * the need for a $ORIGIN/ld.config.app-name configuration file. 212 */ 213 if (rtld_flags & RT_FL_CONFAPP) { 214 char _name[PATH_MAX]; 215 216 if ((str = strrchr(PATHNAME(lmp), '/')) != NULL) 217 str++; 218 else 219 str = PATHNAME(lmp); 220 221 (void) snprintf(_name, PATH_MAX, 222 MSG_ORIG(MSG_ORG_CONFIG), str); 223 str = _name; 224 } 225 226 size = strlen(str); 227 name = (char *)str; 228 229 if (expand(&name, &size, 0, 0, 230 (PN_TKN_ISALIST | PN_TKN_HWCAP), lmp) == 0) 231 return (0); 232 str = (const char *)name; 233 } 234 config->c_name = str; 235 236 /* 237 * If we can't open the configuration file return silently. 238 */ 239 if ((fd = open(str, O_RDONLY, 0)) == -1) 240 return (DBG_CONF_PRCFAIL); 241 242 /* 243 * Determine the configuration file size and map the file. 244 */ 245 (void) fstat(fd, &status); 246 if (status.st_size < sizeof (Rtc_head)) { 247 (void) close(fd); 248 return (DBG_CONF_CORRUPT); 249 } 250 if ((addr = (Addr)mmap(0, status.st_size, PROT_READ, MAP_SHARED, 251 fd, 0)) == (Addr)MAP_FAILED) { 252 (void) close(fd); 253 return (DBG_CONF_PRCFAIL); 254 } 255 (void) close(fd); 256 257 /* 258 * If we have an Rtc_id block at the beginning, then validate it 259 * and advance the address to the Rtc_head. If not, then trust 260 * that the file is compatible with us and move ahead (there is 261 * some error checking for Rtc_head below as well). 262 */ 263 id = (Rtc_id *) addr; 264 if (RTC_ID_TEST(id)) { 265 addr += sizeof (*id); 266 status.st_size -= sizeof (*id); 267 if (status.st_size < sizeof (Rtc_head)) 268 return (DBG_CONF_CORRUPT); 269 if ((id->id_class != M_CLASS) || (id->id_data != M_DATA) || 270 (id->id_machine != M_MACH)) 271 return (DBG_CONF_ABIMISMATCH); 272 } 273 274 config->c_bgn = addr; 275 config->c_end = addr + status.st_size; 276 277 head = (Rtc_head *)addr; 278 279 /* 280 * Make sure we can handle this version of the configuration file. 281 */ 282 if (head->ch_version > RTC_VER_CURRENT) 283 return (DBG_CONF_VERSION); 284 285 /* 286 * When crle(1) creates a temporary configuration file the 287 * RTC_HDR_IGNORE flag is set. Thus the mapping of the configuration 288 * file is taken into account but not its content. 289 */ 290 if (head->ch_cnflags & RTC_HDR_IGNORE) 291 return (DBG_CONF_IGNORE); 292 293 /* 294 * Apply any new default library pathname. 295 */ 296 if (head->ch_edlibpath) { 297 str = (const char *)(head->ch_edlibpath + 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, MSG_ORIG(MSG_PTH_USRLIB_64), 302 MSG_ORIG(MSG_PTH_LIB_64), MSG_PTH_LIB_64_SIZE); 303 #else 304 str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIB), 305 MSG_ORIG(MSG_PTH_LIB), MSG_PTH_LIB_SIZE); 306 #endif 307 } 308 #endif 309 if ((pnp = expand_paths(lmp, str, 310 (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 311 elf_fct.fct_dflt_dirs = pnp; 312 features |= CONF_EDLIBPATH; 313 } 314 if (head->ch_eslibpath) { 315 str = (const char *)(head->ch_eslibpath + addr); 316 #ifndef SGS_PRE_UNIFIED_PROCESS 317 if ((head->ch_cnflags & RTC_HDR_UPM) == 0) { 318 #if defined(_ELF64) 319 str = conv_config_upm(str, 320 MSG_ORIG(MSG_PTH_USRLIBSE_64), 321 MSG_ORIG(MSG_PTH_LIBSE_64), MSG_PTH_LIBSE_64_SIZE); 322 #else 323 str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIBSE), 324 MSG_ORIG(MSG_PTH_LIBSE), MSG_PTH_LIBSE_SIZE); 325 #endif 326 } 327 #endif 328 if ((pnp = expand_paths(lmp, str, 329 (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 330 elf_fct.fct_secure_dirs = pnp; 331 features |= CONF_ESLIBPATH; 332 } 333 #if defined(__sparc) && !defined(_ELF64) 334 if (head->ch_adlibpath) { 335 str = (const char *)(head->ch_adlibpath + addr); 336 if ((pnp = expand_paths(lmp, str, 337 (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 338 aout_fct.fct_dflt_dirs = pnp; 339 features |= CONF_ADLIBPATH; 340 } 341 if (head->ch_aslibpath) { 342 str = (const char *)(head->ch_aslibpath + addr); 343 if ((pnp = expand_paths(lmp, str, 344 (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0) 345 aout_fct.fct_secure_dirs = pnp; 346 features |= CONF_ASLIBPATH; 347 } 348 #endif 349 /* 350 * Apply any environment variables. This attribute was added with 351 * RTC_VER_THREE. 352 */ 353 if ((head->ch_version >= RTC_VER_THREE) && head->ch_env && 354 (!(rtld_flags & RT_FL_NOENVCFG))) { 355 if (readenv_config((Rtc_env *)(head->ch_env + addr), 356 addr, aout) != 0) 357 return (-1); 358 features |= CONF_ENVS; 359 } 360 361 /* 362 * Determine whether filter/filtee associations are available. 363 */ 364 if ((head->ch_version >= RTC_VER_FOUR) && head->ch_fltr && 365 (!(rtld_flags2 & RT_FL2_NOFLTCFG))) { 366 rtld_flags2 |= RT_FL2_FLTCFG; 367 config->c_fltr = (Rtc_fltr *)(head->ch_fltr + addr); 368 config->c_flte = (Rtc_flte *)(head->ch_flte + addr); 369 features |= CONF_FLTR; 370 } 371 372 /* 373 * Determine whether directory configuration is available. 374 */ 375 if ((!(rtld_flags & RT_FL_NODIRCFG)) && head->ch_hash) { 376 config->c_hashtbl = (Word *)(head->ch_hash + addr); 377 config->c_hashchain = &config->c_hashtbl[2 + 378 config->c_hashtbl[0]]; 379 config->c_objtbl = (Rtc_obj *)(head->ch_obj + addr); 380 config->c_strtbl = (const char *)(head->ch_str + addr); 381 382 rtld_flags |= RT_FL_DIRCFG; 383 features |= CONF_DIRCFG; 384 } 385 386 /* 387 * Determine whether alternative objects are specified or an object 388 * reservation area is required. If the reservation can't be completed 389 * (either because the configuration information is out-of-date, or the 390 * the reservation can't be allocated), then alternative objects are 391 * ignored. 392 */ 393 if ((!(rtld_flags & (RT_FL_NODIRCFG | RT_FL_NOOBJALT))) && 394 (head->ch_cnflags & RTC_HDR_ALTER)) { 395 rtld_flags |= RT_FL_OBJALT; 396 features |= CONF_OBJALT; 397 398 elf_config_validate(addr, head, lmp); 399 400 if (head->ch_resbgn) { 401 402 if (((config->c_bgn <= head->ch_resbgn) && 403 (config->c_bgn >= head->ch_resend)) || 404 (nu_map(LIST(lmp), 405 (caddr_t)(uintptr_t)head->ch_resbgn, 406 (head->ch_resend - head->ch_resbgn), PROT_NONE, 407 MAP_FIXED | MAP_PRIVATE) == MAP_FAILED)) 408 return (-1); 409 410 rtld_flags |= RT_FL_MEMRESV; 411 features |= CONF_MEMRESV; 412 } 413 } 414 415 return (features); 416 } 417 418 /* 419 * Determine whether the given file exists in the configuration file. 420 */ 421 Rtc_obj * 422 elf_config_ent(const char *name, Word hash, int id, const char **alternate) 423 { 424 Word bkt, ndx; 425 const char *str; 426 Rtc_obj *obj; 427 428 bkt = hash % config->c_hashtbl[0]; 429 ndx = config->c_hashtbl[2 + bkt]; 430 431 while (ndx) { 432 obj = config->c_objtbl + ndx; 433 str = config->c_strtbl + obj->co_name; 434 435 if ((obj->co_hash != hash) || (strcmp(name, str) != 0) || 436 (id && (id != obj->co_id))) { 437 ndx = config->c_hashchain[ndx]; 438 continue; 439 } 440 441 if ((obj->co_flags & RTC_OBJ_ALTER) && alternate) 442 *alternate = config->c_strtbl + obj->co_alter; 443 444 return (obj); 445 } 446 return (0); 447 } 448 449 /* 450 * Determine whether a filter and filtee string pair exists in the configuration 451 * file. If so, return the cached filtees that are associated with this pair as 452 * a Pnode list. 453 */ 454 Pnode * 455 elf_config_flt(Lm_list *lml, const char *filter, const char *string) 456 { 457 Rtc_fltr * fltrtbl; 458 Pnode * pnp = 0, *npnp, *opnp = 0; 459 460 for (fltrtbl = (Rtc_fltr *)config->c_fltr; fltrtbl->fr_filter; 461 fltrtbl++) { 462 Rtc_flte *fltetbl; 463 const char *fltr, *str; 464 465 fltr = config->c_strtbl + fltrtbl->fr_filter; 466 str = config->c_strtbl + fltrtbl->fr_string; 467 if (strcmp(filter, fltr) || strcmp(string, str)) 468 continue; 469 470 /* 471 * Create a pnode list for each filtee associated with this 472 * filter/filtee string pair. Note, no expansion of filtee 473 * entries is called for, as any original expansion would have 474 * been carried out before they were recorded in the 475 * configuration file. 476 */ 477 /* LINTED */ 478 for (fltetbl = (Rtc_flte *)((char *)config->c_flte + 479 fltrtbl->fr_filtee); fltetbl->fe_filtee; fltetbl++) { 480 const char *flte; 481 482 flte = config->c_strtbl + fltetbl->fe_filtee; 483 484 if (((npnp = calloc(1, sizeof (Pnode))) == 0) || 485 ((npnp->p_name = strdup(flte)) == 0)) 486 return (0); 487 488 DBG_CALL(Dbg_file_filter(lml, fltr, flte, 1)); 489 490 if (opnp == 0) 491 pnp = npnp; 492 else 493 opnp->p_next = npnp; 494 495 npnp->p_len = strlen(flte) + 1; 496 npnp->p_orig = LA_SER_CONFIG; 497 498 opnp = npnp; 499 } 500 return (pnp); 501 } 502 return (0); 503 } 504