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 161 if ((fdp->fd_flags & FLG_FD_ALTER) == 0) { 162 if (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname)) 163 free((void *)fdp->fd_pname); 164 if (fdp->fd_nname) 165 free((void *)fdp->fd_nname); 166 } 167 } 168 169 /* 170 * When $HWCAP is used to represent dependencies, take the associated directory 171 * and analyze all the files it contains. 172 */ 173 static int 174 hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *name, Rt_map *clmp, 175 uint_t flags, Rej_desc *rej, int *in_nfavl) 176 { 177 char path[PATH_MAX], *dst; 178 const char *src; 179 DIR *dir; 180 struct dirent *dirent; 181 Aliste idx; 182 Alist *fdalp = NULL; 183 Fdesc *fdp; 184 int error = 0; 185 186 /* 187 * Access the directory in preparation for reading its entries. If 188 * successful, establish the initial pathname. 189 */ 190 if ((dir = opendir(name)) == 0) { 191 Rej_desc _rej = { 0 }; 192 193 _rej.rej_type = SGS_REJ_STR; 194 _rej.rej_name = name; 195 _rej.rej_str = strerror(errno); 196 DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH)); 197 rejection_inherit(rej, &_rej); 198 return (0); 199 } 200 201 for (dst = path, src = name; *src; dst++, src++) 202 *dst = *src; 203 *dst++ = '/'; 204 205 /* 206 * Read each entry from the directory and determine whether it is a 207 * valid ELF file. 208 */ 209 while ((dirent = readdir(dir)) != NULL) { 210 const char *file = dirent->d_name, *oname; 211 char *_dst; 212 Fdesc fdesc = { 0 }; 213 Rej_desc _rej = { 0 }; 214 215 /* 216 * Ignore "." and ".." entries. 217 */ 218 if ((file[0] == '.') && ((file[1] == '\0') || 219 ((file[1] == '.') && (file[2] == '\0')))) 220 continue; 221 222 /* 223 * Complete the full pathname, and verify its usability. Note, 224 * an auditor can supply an alternative name. 225 */ 226 for (_dst = dst, src = file, file = dst; *src; _dst++, src++) 227 *_dst = *src; 228 *_dst = '\0'; 229 230 if ((oname = strdup(path)) == NULL) { 231 error = 1; 232 break; 233 } 234 235 if (load_trace(lml, &oname, clmp) == 0) { 236 free((void *)oname); 237 continue; 238 } 239 name = oname; 240 241 /* 242 * Note, all directory entries are processed by find_path(), 243 * even entries that are directories themselves. This single 244 * point for control keeps the number of stat()'s down, and 245 * provides a single point for error diagnostics. 246 */ 247 if (find_path(lml, name, clmp, flags, &fdesc, 248 &_rej, in_nfavl) == 0) { 249 rejection_inherit(rej, &_rej); 250 if ((rej->rej_name != _rej.rej_name) && 251 (_rej.rej_name == name)) 252 free((void *)name); 253 continue; 254 } 255 256 DBG_CALL(Dbg_cap_hw_candidate(lml, name)); 257 258 /* 259 * If this object has already been loaded, obtain the hardware 260 * capabilities for later sorting. Otherwise we have a new 261 * candidate. 262 */ 263 if (fdesc.fd_lmp) 264 fdesc.fd_fmap.fm_hwptr = HWCAP(fdesc.fd_lmp); 265 else 266 fdesc.fd_fmap = *fmap; 267 268 if (alist_append(&fdalp, &fdesc, sizeof (Fdesc), 10) == 0) { 269 remove_fdesc(&fdesc); 270 error = 1; 271 break; 272 } 273 274 /* 275 * Clear the global file mapping structure so that the mapping 276 * for this file won't be overriden. 277 */ 278 fmap->fm_mflags = MAP_PRIVATE; 279 fmap->fm_maddr = 0; 280 fmap->fm_msize = FMAP_SIZE; 281 fmap->fm_hwptr = 0; 282 } 283 (void) closedir(dir); 284 285 /* 286 * If no objects have been found, we're done. Also, if an allocation 287 * error occurred while processing any object, remove any objects that 288 * had already been added to the list and return. 289 */ 290 if ((fdalp == NULL) || error) { 291 if (fdalp) { 292 for (ALIST_TRAVERSE(fdalp, idx, fdp)) 293 remove_fdesc(fdp); 294 free(fdalp); 295 } 296 return (0); 297 } 298 299 /* 300 * Having processed and retained all candidates from this directory, 301 * sort them, based on the precedence of their hardware capabilities. 302 */ 303 qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare); 304 305 *fdalpp = fdalp; 306 return (1); 307 } 308 309 static Pnode * 310 _hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Rt_map *flmp, 311 const char *ref, const char *dir, int mode, uint_t flags, int *in_nfavl) 312 { 313 Alist *fdalp = NULL; 314 Aliste idx; 315 Pnode *fpnp = 0, *lpnp, *npnp = (*pnpp)->p_next; 316 Fdesc *fdp; 317 Lm_list *lml = LIST(flmp); 318 int unused = 0; 319 Rej_desc rej = { 0 }; 320 321 if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) { 322 remove_rej(&rej); 323 return (0); 324 } 325 326 /* 327 * Now complete the mapping of each of the ordered objects, adding 328 * each object to a new Pnode. 329 */ 330 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 331 Rt_map *nlmp; 332 Grp_hdl *ghp = 0; 333 Pnode *pnp; 334 int audit = 0; 335 336 if (unused) { 337 /* 338 * Flush out objects remaining. 339 */ 340 remove_fdesc(fdp); 341 continue; 342 } 343 344 /* 345 * Complete mapping the file, obtaining a handle, and continue 346 * to analyze the object, establishing dependencies and 347 * relocating. Remove the file descriptor at this point, as it 348 * is no longer required. 349 */ 350 DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0)); 351 352 nlmp = load_path(lml, nlmco, &fdp->fd_nname, flmp, mode, 353 (flags | FLG_RT_HANDLE), &ghp, fdp, &rej, in_nfavl); 354 remove_fdesc(fdp); 355 if (nlmp == 0) 356 continue; 357 358 /* 359 * Create a new Pnode to represent this filtee, and substitute 360 * the calling Pnode (which was used to represent the hardware 361 * capability directory). 362 */ 363 if ((pnp = calloc(1, sizeof (Pnode))) == 0) { 364 if (ghp) { 365 remove_lmc(lml, flmp, nlmc, nlmco, 366 fdp->fd_nname); 367 } 368 return (0); 369 } 370 if ((pnp->p_name = strdup(NAME(nlmp))) == NULL) { 371 if (ghp) { 372 remove_lmc(lml, flmp, nlmc, nlmco, 373 fdp->fd_nname); 374 } 375 free(pnp); 376 return (0); 377 } 378 pnp->p_len = strlen(NAME(nlmp)); 379 pnp->p_info = (void *)ghp; 380 pnp->p_next = npnp; 381 382 if (fpnp == 0) { 383 Pnode *opnp = (*pnpp); 384 385 /* 386 * If this is the first pnode, reuse the original after 387 * freeing any of its pathnames. 388 */ 389 if (opnp->p_name) 390 free((void *)opnp->p_name); 391 if (opnp->p_oname) 392 free((void *)opnp->p_oname); 393 *opnp = *pnp; 394 free((void *)pnp); 395 fpnp = lpnp = pnp = opnp; 396 } else { 397 lpnp->p_next = pnp; 398 lpnp = pnp; 399 } 400 401 /* 402 * Establish the filter handle to prevent any recursion. 403 */ 404 if (nlmp && ghp) { 405 ghp->gh_flags |= GPH_FILTEE; 406 pnp->p_info = (void *)ghp; 407 } 408 409 /* 410 * Audit the filter/filtee established. A return of 0 411 * indicates the auditor wishes to ignore this filtee. 412 */ 413 if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) & 414 LML_TFLG_AUD_OBJFILTER) { 415 if (audit_objfilter(flmp, ref, nlmp, 0) == 0) { 416 audit = 1; 417 nlmp = 0; 418 } 419 } 420 421 /* 422 * Finish processing the objects associated with this request. 423 */ 424 if (nlmp && ghp && 425 ((analyze_lmc(lml, nlmco, nlmp, in_nfavl) == 0) || 426 (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0))) 427 nlmp = 0; 428 429 /* 430 * If the filtee has been successfully processed, then create 431 * an association between the filter and the filtee. This 432 * association provides sufficient information to tear down the 433 * filter and filtee if necessary. 434 */ 435 DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD)); 436 if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER) == 0)) 437 nlmp = 0; 438 439 /* 440 * If this object is marked an end-filtee, we're done. 441 */ 442 if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE)) 443 unused = 1; 444 445 /* 446 * If this filtee loading has failed, generate a diagnostic. 447 * Null out the pnode entry, and continue the search. 448 */ 449 if (nlmp == 0) { 450 /* 451 * If attempting to load this filtee required a new 452 * link-map control list to which this request has 453 * added objects, then remove all the objects that 454 * have been associated to this request. 455 */ 456 if (nlmc && nlmc->lc_head) 457 remove_lmc(lml, flmp, nlmc, nlmco, pnp->p_name); 458 459 DBG_CALL(Dbg_file_filtee(lml, 0, pnp->p_name, audit)); 460 461 pnp->p_len = 0; 462 pnp->p_info = 0; 463 } 464 } 465 466 free(fdalp); 467 return (fpnp); 468 } 469 470 Pnode * 471 hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Dyninfo *dip, 472 Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl) 473 { 474 Pnode *pnp = *pnpp; 475 const char *dir = pnp->p_name; 476 Lm_list *flml = LIST(flmp); 477 478 DBG_CALL(Dbg_cap_hw_filter(flml, dir, flmp)); 479 480 if ((pnp = _hwcap_filtees(pnpp, nlmco, nlmc, flmp, ref, dir, mode, 481 flags, in_nfavl)) != 0) 482 return (pnp); 483 484 /* 485 * If no hardware capability filtees have been found, provide suitable 486 * diagnostics and mark the incoming Pnode as unused. 487 */ 488 if ((flml->lm_flags & LML_FLG_TRC_ENABLE) && 489 (dip->di_flags & FLG_DI_AUXFLTR) && (rtld_flags & RT_FL_WARNFLTR)) 490 (void) printf(MSG_INTL(MSG_LDD_HWCAP_NFOUND), dir); 491 492 DBG_CALL(Dbg_cap_hw_filter(flml, dir, 0)); 493 494 pnp = *pnpp; 495 pnp->p_len = 0; 496 return (pnp); 497 } 498 499 /* 500 * Load an individual hardware capabilities object. 501 */ 502 Rt_map * 503 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp, 504 uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl) 505 { 506 Alist *fdalp = NULL; 507 Aliste idx; 508 Fdesc *fdp; 509 int found = 0; 510 Rt_map *lmp = 0; 511 512 /* 513 * Obtain the sorted list of hardware capabilites objects available. 514 */ 515 if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0) 516 return (0); 517 518 /* 519 * From the list of hardware capability objects, use the first and 520 * discard the rest. 521 */ 522 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 523 if ((found == 0) && ((lmp = load_path(lml, lmco, &fdp->fd_nname, 524 clmp, mode, flags, hdl, fdp, rej, in_nfavl)) != 0)) 525 found++; 526 527 /* 528 * Remove the used file descriptor and any objects remaining. 529 */ 530 remove_fdesc(fdp); 531 } 532 533 free(fdalp); 534 return (lmp); 535 } 536