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