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