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 #include <sys/types.h> 28 #include <sys/mman.h> 29 #include <dirent.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <limits.h> 34 #include <debug.h> 35 #include <conv.h> 36 #include "_rtld.h" 37 #include "_audit.h" 38 #include "msg.h" 39 40 /* 41 * qsort(3c) comparison function. 42 */ 43 static int 44 compare(const void *fdesc1, const void *fdesc2) 45 { 46 ulong_t hwcap1 = ((Fdesc *)fdesc1)->fd_fmap.fm_hwptr; 47 ulong_t hwcap2 = ((Fdesc *)fdesc2)->fd_fmap.fm_hwptr; 48 49 if (hwcap1 && (hwcap2 == 0)) 50 return (-1); 51 if ((hwcap1 == 0) && hwcap2) 52 return (1); 53 if ((hwcap1 == 0) && (hwcap2 == 0)) 54 return (0); 55 56 if (hwcap1 > hwcap2) 57 return (-1); 58 if (hwcap1 < hwcap2) 59 return (1); 60 return (0); 61 } 62 63 /* 64 * Process any hardware and software capabilities. 65 */ 66 int 67 cap_check(Rej_desc *rej, Ehdr *ehdr) 68 { 69 Cap *cptr; 70 Phdr *phdr; 71 int cnt; 72 73 /* LINTED */ 74 phdr = (Phdr *)((char *)ehdr + ehdr->e_phoff); 75 for (cnt = 0; cnt < ehdr->e_phnum; cnt++, phdr++) { 76 Lword val; 77 78 if (phdr->p_type != PT_SUNWCAP) 79 continue; 80 81 /* LINTED */ 82 for (cptr = (Cap *)((char *)ehdr + phdr->p_offset); 83 (cptr->c_tag != CA_SUNW_NULL); cptr++) { 84 85 if ((cptr->c_tag == CA_SUNW_HW_1) && 86 (rtld_flags2 & RT_FL2_HWCAP)) { 87 /* 88 * If this object defines a set of hardware 89 * capability requirements, ensure the kernel 90 * can cope with them. 91 */ 92 if ((val = (cptr->c_un.c_val & ~hwcap)) != 0) { 93 static Conv_cap_val_hw1_buf_t cap_buf; 94 95 rej->rej_type = SGS_REJ_HWCAP_1; 96 rej->rej_str = 97 conv_cap_val_hw1(val, M_MACH, 0, 98 &cap_buf); 99 return (0); 100 } 101 102 /* 103 * Retain this hardware capabilities value for 104 * possible later inspection should this object 105 * be processed as a filtee. 106 */ 107 fmap->fm_hwptr = cptr->c_un.c_val; 108 109 } 110 #if defined(_ELF64) 111 if (cptr->c_tag == CA_SUNW_SF_1) { 112 /* 113 * A 64-bit executable that started the process 114 * can be restricted to a 32-bit address space. 115 * A 64-bit dependency that is restricted to a 116 * 32-bit address space can not be loaded unless 117 * the executable has established this 118 * requirement. 119 */ 120 if ((cptr->c_un.c_val & SF1_SUNW_ADDR32) && 121 ((rtld_flags2 & RT_FL2_ADDR32) == 0)) { 122 static Conv_cap_val_sf1_buf_t cap_buf; 123 124 rej->rej_type = SGS_REJ_SFCAP_1; 125 rej->rej_str = 126 conv_cap_val_sf1(SF1_SUNW_ADDR32, 127 M_MACH, 0, &cap_buf); 128 return (0); 129 } 130 } 131 #endif 132 } 133 } 134 return (1); 135 } 136 137 static void 138 remove_fdesc(Fdesc *fdp) 139 { 140 #if defined(MAP_ALIGN) 141 if (fdp->fd_fmap.fm_maddr && 142 ((fdp->fd_fmap.fm_mflags & MAP_ALIGN) == 0)) { 143 #else 144 if (fdp->fd_fmap.fm_maddr) { 145 #endif 146 (void) munmap(fdp->fd_fmap.fm_maddr, fdp->fd_fmap.fm_msize); 147 148 /* 149 * Note, this file descriptor might be duplicating information 150 * from the global fmap descriptor. If so, clean up the global 151 * descriptor to prevent a duplicate (unnecessary) unmap. 152 */ 153 if (fmap->fm_maddr == fdp->fd_fmap.fm_maddr) { 154 fmap->fm_maddr = 0; 155 fmap_setup(); 156 } 157 } 158 if (fdp->fd_fd) 159 (void) close(fdp->fd_fd); 160 if (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname)) 161 free((void *)fdp->fd_pname); 162 if (fdp->fd_nname) 163 free((void *)fdp->fd_nname); 164 } 165 166 /* 167 * When $HWCAP is used to represent dependencies, take the associated directory 168 * and analyze all the files it contains. 169 */ 170 static int 171 hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *name, Rt_map *clmp, 172 uint_t flags, Rej_desc *rej, int *in_nfavl) 173 { 174 char path[PATH_MAX], *dst; 175 const char *src; 176 DIR *dir; 177 struct dirent *dirent; 178 Aliste idx; 179 Alist *fdalp = NULL; 180 Fdesc *fdp; 181 int error = 0; 182 183 /* 184 * Access the directory in preparation for reading its entries. If 185 * successful, establish the initial pathname. 186 */ 187 if ((dir = opendir(name)) == 0) { 188 Rej_desc _rej = { 0 }; 189 190 _rej.rej_type = SGS_REJ_STR; 191 _rej.rej_name = name; 192 _rej.rej_str = strerror(errno); 193 DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH)); 194 rejection_inherit(rej, &_rej); 195 return (0); 196 } 197 198 for (dst = path, src = name; *src; dst++, src++) 199 *dst = *src; 200 *dst++ = '/'; 201 202 /* 203 * Read each entry from the directory and determine whether it is a 204 * valid ELF file. 205 */ 206 while ((dirent = readdir(dir)) != NULL) { 207 const char *file = dirent->d_name, *oname; 208 char *_dst; 209 Fdesc fdesc = { 0 }; 210 Rej_desc _rej = { 0 }; 211 212 /* 213 * Ignore "." and ".." entries. 214 */ 215 if ((file[0] == '.') && ((file[1] == '\0') || 216 ((file[1] == '.') && (file[2] == '\0')))) 217 continue; 218 219 /* 220 * Complete the full pathname, and verify its usability. Note, 221 * an auditor can supply an alternative name. 222 */ 223 for (_dst = dst, src = file, file = dst; *src; _dst++, src++) 224 *_dst = *src; 225 *_dst = '\0'; 226 227 if ((oname = strdup(path)) == NULL) { 228 error = 1; 229 break; 230 } 231 232 if (load_trace(lml, &oname, clmp) == 0) { 233 free((void *)oname); 234 continue; 235 } 236 name = oname; 237 238 /* 239 * Note, all directory entries are processed by find_path(), 240 * even entries that are directories themselves. This single 241 * point for control keeps the number of stat()'s down, and 242 * provides a single point for error diagnostics. 243 */ 244 if (find_path(lml, name, clmp, flags, &fdesc, 245 &_rej, in_nfavl) == 0) { 246 rejection_inherit(rej, &_rej); 247 if ((rej->rej_name != _rej.rej_name) && 248 (_rej.rej_name == name)) 249 free((void *)name); 250 continue; 251 } 252 253 DBG_CALL(Dbg_cap_hw_candidate(lml, name)); 254 255 /* 256 * If this object has already been loaded, obtain the hardware 257 * capabilities for later sorting. Otherwise we have a new 258 * candidate. 259 */ 260 if (fdesc.fd_lmp) 261 fdesc.fd_fmap.fm_hwptr = HWCAP(fdesc.fd_lmp); 262 else 263 fdesc.fd_fmap = *fmap; 264 265 if (alist_append(&fdalp, &fdesc, sizeof (Fdesc), 10) == 0) { 266 remove_fdesc(&fdesc); 267 error = 1; 268 break; 269 } 270 271 /* 272 * Clear the global file mapping structure so that the mapping 273 * for this file won't be overriden. 274 */ 275 fmap->fm_mflags = MAP_PRIVATE; 276 fmap->fm_maddr = 0; 277 fmap->fm_msize = FMAP_SIZE; 278 fmap->fm_hwptr = 0; 279 } 280 (void) closedir(dir); 281 282 /* 283 * If no objects have been found, we're done. Also, if an allocation 284 * error occurred while processing any object, remove any objects that 285 * had already been added to the list and return. 286 */ 287 if ((fdalp == NULL) || error) { 288 if (fdalp) { 289 for (ALIST_TRAVERSE(fdalp, idx, fdp)) 290 remove_fdesc(fdp); 291 free(fdalp); 292 } 293 return (0); 294 } 295 296 /* 297 * Having processed and retained all candidates from this directory, 298 * sort them, based on the precedence of their hardware capabilities. 299 */ 300 qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare); 301 302 *fdalpp = fdalp; 303 return (1); 304 } 305 306 static Pnode * 307 _hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Rt_map *flmp, 308 const char *ref, const char *dir, int mode, uint_t flags, int *in_nfavl) 309 { 310 Alist *fdalp = NULL; 311 Aliste idx; 312 Pnode *fpnp = 0, *lpnp, *npnp = (*pnpp)->p_next; 313 Fdesc *fdp; 314 Lm_list *lml = LIST(flmp); 315 int unused = 0; 316 Rej_desc rej = { 0 }; 317 318 if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) { 319 remove_rej(&rej); 320 return (0); 321 } 322 323 /* 324 * Now complete the mapping of each of the ordered objects, adding 325 * each object to a new Pnode. 326 */ 327 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 328 Rt_map *nlmp; 329 Grp_hdl *ghp = 0; 330 Pnode *pnp; 331 int audit = 0; 332 333 if (unused) { 334 /* 335 * Flush out objects remaining. 336 */ 337 remove_fdesc(fdp); 338 continue; 339 } 340 341 /* 342 * Complete mapping the file, obtaining a handle, and continue 343 * to analyze the object, establishing dependencies and 344 * relocating. Remove the file descriptor at this point, as it 345 * is no longer required. 346 */ 347 DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0)); 348 349 nlmp = load_path(lml, nlmco, &fdp->fd_nname, flmp, mode, 350 (flags | FLG_RT_HANDLE), &ghp, fdp, &rej, in_nfavl); 351 remove_fdesc(fdp); 352 if (nlmp == 0) 353 continue; 354 355 /* 356 * Create a new Pnode to represent this filtee, and substitute 357 * the calling Pnode (which was used to represent the hardware 358 * capability directory). 359 */ 360 if ((pnp = calloc(1, sizeof (Pnode))) == 0) { 361 if (ghp) { 362 remove_lmc(lml, flmp, nlmc, nlmco, 363 fdp->fd_nname); 364 } 365 return (0); 366 } 367 if ((pnp->p_name = strdup(NAME(nlmp))) == NULL) { 368 if (ghp) { 369 remove_lmc(lml, flmp, nlmc, nlmco, 370 fdp->fd_nname); 371 } 372 free(pnp); 373 return (0); 374 } 375 pnp->p_len = strlen(NAME(nlmp)); 376 pnp->p_info = (void *)ghp; 377 pnp->p_next = npnp; 378 379 if (fpnp == 0) { 380 Pnode *opnp = (*pnpp); 381 382 /* 383 * If this is the first pnode, reuse the original after 384 * freeing any of its pathnames. 385 */ 386 if (opnp->p_name) 387 free((void *)opnp->p_name); 388 if (opnp->p_oname) 389 free((void *)opnp->p_oname); 390 *opnp = *pnp; 391 free((void *)pnp); 392 fpnp = lpnp = pnp = opnp; 393 } else { 394 lpnp->p_next = pnp; 395 lpnp = pnp; 396 } 397 398 /* 399 * Establish the filter handle to prevent any recursion. 400 */ 401 if (nlmp && ghp) { 402 ghp->gh_flags |= GPH_FILTEE; 403 pnp->p_info = (void *)ghp; 404 } 405 406 /* 407 * Audit the filter/filtee established. A return of 0 408 * indicates the auditor wishes to ignore this filtee. 409 */ 410 if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) & 411 LML_TFLG_AUD_OBJFILTER) { 412 if (audit_objfilter(flmp, ref, nlmp, 0) == 0) { 413 audit = 1; 414 nlmp = 0; 415 } 416 } 417 418 /* 419 * Finish processing the objects associated with this request. 420 */ 421 if (nlmp && ghp && 422 ((analyze_lmc(lml, nlmco, nlmp, in_nfavl) == 0) || 423 (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0))) 424 nlmp = 0; 425 426 /* 427 * If the filtee has been successfully processed, then create 428 * an association between the filter and the filtee. This 429 * association provides sufficient information to tear down the 430 * filter and filtee if necessary. 431 */ 432 DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD)); 433 if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER) == 0)) 434 nlmp = 0; 435 436 /* 437 * If this object is marked an end-filtee, we're done. 438 */ 439 if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE)) 440 unused = 1; 441 442 /* 443 * If this filtee loading has failed, generate a diagnostic. 444 * Null out the pnode entry, and continue the search. 445 */ 446 if (nlmp == 0) { 447 /* 448 * If attempting to load this filtee required a new 449 * link-map control list to which this request has 450 * added objects, then remove all the objects that 451 * have been associated to this request. 452 */ 453 if (nlmc && nlmc->lc_head) 454 remove_lmc(lml, flmp, nlmc, nlmco, pnp->p_name); 455 456 DBG_CALL(Dbg_file_filtee(lml, 0, pnp->p_name, audit)); 457 458 pnp->p_len = 0; 459 pnp->p_info = 0; 460 } 461 } 462 463 free(fdalp); 464 return (fpnp); 465 } 466 467 Pnode * 468 hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Dyninfo *dip, 469 Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl) 470 { 471 Pnode *pnp = *pnpp; 472 const char *dir = pnp->p_name; 473 Lm_list *flml = LIST(flmp); 474 475 DBG_CALL(Dbg_cap_hw_filter(flml, dir, flmp)); 476 477 if ((pnp = _hwcap_filtees(pnpp, nlmco, nlmc, flmp, ref, dir, mode, 478 flags, in_nfavl)) != 0) 479 return (pnp); 480 481 /* 482 * If no hardware capability filtees have been found, provide suitable 483 * diagnostics and mark the incoming Pnode as unused. 484 */ 485 if ((flml->lm_flags & LML_FLG_TRC_ENABLE) && 486 (dip->di_flags & FLG_DI_AUXFLTR) && (rtld_flags & RT_FL_WARNFLTR)) 487 (void) printf(MSG_INTL(MSG_LDD_HWCAP_NFOUND), dir); 488 489 DBG_CALL(Dbg_cap_hw_filter(flml, dir, 0)); 490 491 pnp = *pnpp; 492 pnp->p_len = 0; 493 return (pnp); 494 } 495 496 /* 497 * Load an individual hardware capabilities object. 498 */ 499 Rt_map * 500 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp, 501 uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl) 502 { 503 Alist *fdalp = NULL; 504 Aliste idx; 505 Fdesc *fdp; 506 int found = 0; 507 Rt_map *lmp = 0; 508 509 /* 510 * Obtain the sorted list of hardware capabilites objects available. 511 */ 512 if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0) 513 return (0); 514 515 /* 516 * From the list of hardware capability objects, use the first and 517 * discard the rest. 518 */ 519 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 520 if ((found == 0) && ((lmp = load_path(lml, lmco, &fdp->fd_nname, 521 clmp, mode, flags, hdl, fdp, rej, in_nfavl)) != 0)) 522 found++; 523 524 /* 525 * Remove the used file descriptor and any objects remaining. 526 */ 527 remove_fdesc(fdp); 528 } 529 530 free(fdalp); 531 return (lmp); 532 } 533