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 * Copyright 2005 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 "conv.h" 37 #include "_rtld.h" 38 #include "_audit.h" 39 #include "_elf.h" 40 #include "msg.h" 41 #include "debug.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_hwcap_1_str(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 * 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_nname) 131 free((void *)fdp->fd_nname); 132 if (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname)) 133 free((void *)fdp->fd_pname); 134 } 135 136 /* 137 * When $HWCAP is used to represent filtees, take the associated filtee 138 * directory 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(&_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(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_nname = name; 217 fdesc.fd_fmap = *fmap; 218 } 219 if (alist_append(&fdalp, &fdesc, 220 sizeof (Fdesc), 10) == 0) { 221 remove_fdesc(&fdesc); 222 error = 1; 223 break; 224 } 225 } 226 227 /* 228 * Clear the global file mapping structure so that the mapping 229 * for this file won't be overriden. 230 */ 231 fmap->fm_mflags = MAP_PRIVATE; 232 fmap->fm_maddr = 0; 233 fmap->fm_msize = syspagsz; 234 fmap->fm_hwptr = 0; 235 236 } 237 (void) closedir(dir); 238 239 /* 240 * If no objects have been found, we're done. Also, if an allocation 241 * error occurred while processing any object, remove any objects that 242 * had already been added to the list and return. 243 */ 244 if ((fdalp == 0) || error) { 245 if (fdalp) { 246 for (ALIST_TRAVERSE(fdalp, off, fdp)) 247 remove_fdesc(fdp); 248 free(fdalp); 249 } 250 return (0); 251 } 252 253 /* 254 * Having processed and retained all candidates from this directory, 255 * sort them, based on the precedence of their hardware capabilities. 256 */ 257 qsort(&(fdalp->al_data[0]), ((fdalp->al_next - (sizeof (Alist) - 258 sizeof (void *))) / fdalp->al_size), fdalp->al_size, compare); 259 260 *fdalpp = fdalp; 261 return (1); 262 } 263 264 static Pnode * 265 _hwcap_filtees(Pnode ** pnpp, Aliste nlmco, Rt_map * flmp, const char *ref, 266 const char *dir, int mode, uint_t flags) 267 { 268 Alist *fdalp = 0; 269 Aliste off; 270 Pnode *fpnp = 0, *lpnp, *npnp = (*pnpp)->p_next; 271 Fdesc *fdp; 272 Lm_list *lml = LIST(flmp); 273 int unused = 0; 274 Rej_desc rej = { 0 }; 275 276 if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej) == 0) { 277 remove_rej(&rej); 278 return (0); 279 } 280 281 /* 282 * Now complete the mapping of each of the ordered objects, adding 283 * each object to a new Pnode. 284 */ 285 for (ALIST_TRAVERSE(fdalp, off, fdp)) { 286 Rt_map *nlmp; 287 Grp_hdl *ghp; 288 Pnode *pnp; 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(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 if (nlmp == 0) 311 continue; 312 313 /* 314 * Audit the filter/filtee established. A return of 0 315 * indicates the auditor wishes to ignore this filtee. 316 */ 317 if ((lml->lm_tflags | FLAGS1(flmp)) & 318 LML_TFLG_AUD_OBJFILTER) { 319 if (audit_objfilter(flmp, ref, nlmp, 0) == 0) { 320 DBG_CALL(Dbg_file_filtee(0, NAME(nlmp), 1)); 321 (void) dlclose_core(ghp, flmp); 322 continue; 323 } 324 } 325 326 ghp->gh_flags |= GPH_FILTEE; 327 328 /* 329 * Finish processing the objects associated with this request. 330 */ 331 if ((analyze_lmc(lml, nlmco, nlmp) == 0) || 332 (relocate_lmc(lml, nlmco, nlmp) == 0)) { 333 (void) dlclose_core(ghp, flmp); 334 continue; 335 } 336 337 /* 338 * Create a new Pnode to represent this filtee, and substitute 339 * the calling Pnode (which was used to represent the hardware 340 * capability directory). 341 */ 342 if ((pnp = calloc(1, sizeof (Pnode))) == 0) { 343 (void) dlclose_core(ghp, flmp); 344 continue; 345 } 346 if ((pnp->p_name = strdup(NAME(nlmp))) == 0) { 347 (void) dlclose_core(ghp, flmp); 348 free(pnp); 349 continue; 350 } 351 pnp->p_len = strlen(NAME(nlmp)); 352 pnp->p_info = (void *)ghp; 353 pnp->p_next = npnp; 354 355 /* 356 * Finally, if the filtee is part of a link-map control list 357 * that is equivalent, or less, than the filter control list, 358 * create an association between the filter and filtee. This 359 * association provides sufficient information to tear down the 360 * filter and filtee if necessary. 361 */ 362 if ((CNTL(nlmp) <= CNTL(flmp)) && 363 (hdl_add(ghp, flmp, GPD_FILTER) == 0)) { 364 (void) dlclose_core(ghp, flmp); 365 free((void *)pnp->p_name); 366 free(pnp); 367 continue; 368 } 369 370 if (fpnp == 0) { 371 Pnode *opnp = (*pnpp); 372 /* 373 * If this is the first pnode, reuse the original after 374 * freeing any of its pathnames. 375 */ 376 if (opnp->p_name) 377 free((void *)opnp->p_name); 378 if (opnp->p_oname) 379 free((void *)opnp->p_oname); 380 *opnp = *pnp; 381 free((void *)pnp); 382 fpnp = lpnp = opnp; 383 } else { 384 lpnp->p_next = pnp; 385 lpnp = pnp; 386 } 387 388 /* 389 * If this object is marked an end-filtee, we're done. 390 */ 391 if (FLAGS1(nlmp) & FL1_RT_ENDFILTE) 392 unused = 1; 393 } 394 395 free(fdalp); 396 return (fpnp); 397 } 398 399 Pnode * 400 hwcap_filtees(Pnode ** pnpp, Aliste nlmco, Dyninfo * dip, Rt_map * flmp, 401 const char *ref, int mode, uint_t flags) 402 { 403 Pnode *pnp = *pnpp; 404 const char *dir = pnp->p_name; 405 406 DBG_CALL(Dbg_cap_hw_filter(dir, NAME(flmp))); 407 408 if ((pnp = _hwcap_filtees(pnpp, nlmco, flmp, ref, dir, mode, 409 flags)) != 0) 410 return (pnp); 411 412 /* 413 * If no hardware capability filtees have been found, provide suitable 414 * diagnostics and mark the incoming Pnode as unused. 415 */ 416 if ((LIST(flmp)->lm_flags & LML_FLG_TRC_ENABLE) && 417 (dip->di_flags & FLG_DI_AUXFLTR) && (rtld_flags & RT_FL_WARNFLTR)) 418 (void) printf(MSG_INTL(MSG_LDD_HWCAP_NFOUND), dir); 419 420 DBG_CALL(Dbg_cap_hw_filter(dir, 0)); 421 422 pnp = *pnpp; 423 pnp->p_len = 0; 424 return (pnp); 425 } 426 427 /* 428 * Load an individual hardware capabilities object. 429 */ 430 Rt_map * 431 load_hwcap(Lm_list * lml, Aliste lmco, const char *dir, Rt_map * clmp, 432 uint_t mode, uint_t flags, Grp_hdl ** hdl, Rej_desc *rej) 433 { 434 Alist *fdalp = 0; 435 Aliste off; 436 Fdesc *fdp; 437 int found = 0; 438 Rt_map *lmp = 0; 439 440 /* 441 * Obtain the sorted list of hardware capabilites objects available. 442 */ 443 if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej) == 0) 444 return (0); 445 446 /* 447 * From the list of hardware capability objects, use the first and 448 * discard the rest. 449 */ 450 for (ALIST_TRAVERSE(fdalp, off, fdp)) { 451 if ((found == 0) && ((lmp = load_path(lml, lmco, fdp->fd_nname, 452 clmp, mode, flags, hdl, fdp, rej)) != 0)) 453 found++; 454 455 /* 456 * Remove the used file descriptor and any objects remaining. 457 */ 458 remove_fdesc(fdp); 459 } 460 461 free(fdalp); 462 return (lmp); 463 } 464