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