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