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