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