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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/mman.h> 29 #include <dirent.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <limits.h> 34 #include <debug.h> 35 #include <conv.h> 36 #include "_rtld.h" 37 #include "_audit.h" 38 #include "msg.h" 39 40 /* 41 * qsort(3c) comparison function. 42 */ 43 static int 44 compare(const void *fdesc1, const void *fdesc2) 45 { 46 Xword hwcap1 = ((Fdesc *)fdesc1)->fd_hwcap; 47 Xword hwcap2 = ((Fdesc *)fdesc2)->fd_hwcap; 48 49 if (hwcap1 && (hwcap2 == 0)) 50 return (-1); 51 if ((hwcap1 == 0) && hwcap2) 52 return (1); 53 if ((hwcap1 == 0) && (hwcap2 == 0)) 54 return (0); 55 56 if (hwcap1 > hwcap2) 57 return (-1); 58 if (hwcap1 < hwcap2) 59 return (1); 60 return (0); 61 } 62 63 /* 64 * Process any hardware capabilities. 65 */ 66 int 67 hwcap_check(Xword val, Rej_desc *rej) 68 { 69 Xword mval; 70 71 /* 72 * Ensure that the kernel can cope with the required capabilities. 73 */ 74 if ((rtld_flags2 & RT_FL2_HWCAP) && ((mval = (val & ~hwcap)) != 0)) { 75 static Conv_cap_val_hw1_buf_t cap_buf; 76 77 rej->rej_type = SGS_REJ_HWCAP_1; 78 rej->rej_str = conv_cap_val_hw1(mval, M_MACH, 0, &cap_buf); 79 return (0); 80 } 81 return (1); 82 } 83 84 /* 85 * Process any software capabilities. 86 */ 87 /* ARGSUSED0 */ 88 int 89 sfcap_check(Xword val, Rej_desc *rej) 90 { 91 #if defined(_ELF64) 92 /* 93 * A 64-bit executable that started the process can be restricted to a 94 * 32-bit address space. A 64-bit dependency that is restricted to a 95 * 32-bit address space can not be loaded unless the executable has 96 * established this requirement. 97 */ 98 if ((val & SF1_SUNW_ADDR32) && ((rtld_flags2 & RT_FL2_ADDR32) == 0)) { 99 static Conv_cap_val_sf1_buf_t cap_buf; 100 101 rej->rej_type = SGS_REJ_SFCAP_1; 102 rej->rej_str = 103 conv_cap_val_sf1(SF1_SUNW_ADDR32, M_MACH, 0, &cap_buf); 104 return (0); 105 } 106 #endif 107 return (1); 108 } 109 110 /* 111 * When $HWCAP is used to represent dependencies, take the associated directory 112 * and analyze all the files it contains. 113 */ 114 static int 115 hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *dname, Rt_map *clmp, 116 uint_t flags, Rej_desc *rej, int *in_nfavl) 117 { 118 char path[PATH_MAX], *dst; 119 const char *src; 120 DIR *dir; 121 struct dirent *dirent; 122 Alist *fdalp = NULL; 123 int error = 0; 124 125 /* 126 * Access the directory in preparation for reading its entries. If 127 * successful, establish the initial pathname. 128 */ 129 if ((dir = opendir(dname)) == NULL) { 130 Rej_desc _rej = { 0 }; 131 132 _rej.rej_type = SGS_REJ_STR; 133 _rej.rej_name = dname; 134 _rej.rej_str = strerror(errno); 135 DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH)); 136 rejection_inherit(rej, &_rej); 137 return (0); 138 } 139 140 for (dst = path, src = dname; *src; dst++, src++) 141 *dst = *src; 142 *dst++ = '/'; 143 144 /* 145 * Read each entry from the directory and determine whether it is a 146 * valid ELF file. 147 */ 148 while ((dirent = readdir(dir)) != NULL) { 149 const char *file = dirent->d_name; 150 char *_dst; 151 Fdesc fd = { 0 }; 152 Rej_desc _rej = { 0 }; 153 Pdesc pd = { 0 }; 154 155 /* 156 * Ignore "." and ".." entries. 157 */ 158 if ((file[0] == '.') && ((file[1] == '\0') || 159 ((file[1] == '.') && (file[2] == '\0')))) 160 continue; 161 162 /* 163 * Complete the full pathname. 164 */ 165 for (_dst = dst, src = file, file = dst; *src; _dst++, src++) 166 *_dst = *src; 167 *_dst = '\0'; 168 169 /* 170 * Trace the inspection of this file, and determine any 171 * auditor substitution. 172 */ 173 pd.pd_pname = path; 174 pd.pd_flags = PD_FLG_PNSLASH; 175 176 if (load_trace(lml, &pd, clmp, &fd) == NULL) 177 continue; 178 179 /* 180 * Note, all directory entries are processed by find_path(), 181 * even entries that are directories themselves. This single 182 * point for control keeps the number of stat()'s down, and 183 * provides a single point for error diagnostics. 184 */ 185 if (find_path(lml, clmp, flags, &fd, &_rej, in_nfavl) == 0) { 186 rejection_inherit(rej, &_rej); 187 continue; 188 } 189 190 DBG_CALL(Dbg_cap_hw_candidate(lml, fd.fd_nname)); 191 192 /* 193 * If this object has already been loaded, obtain the hardware 194 * capabilities for later sorting. Otherwise we have a new 195 * candidate. 196 */ 197 if (fd.fd_lmp) 198 fd.fd_hwcap = HWCAP(fd.fd_lmp); 199 200 if (alist_append(&fdalp, &fd, sizeof (Fdesc), 201 AL_CNT_HWCAP) == NULL) { 202 error = 1; 203 break; 204 } 205 } 206 (void) closedir(dir); 207 208 /* 209 * If no objects have been found, we're done. Also, if an allocation 210 * error occurred while processing any object, remove any objects that 211 * had already been added to the list and return. 212 */ 213 if ((fdalp == NULL) || error) { 214 if (fdalp) 215 free(fdalp); 216 return (0); 217 } 218 219 /* 220 * Having processed and retained all candidates from this directory, 221 * sort them, based on the precedence of their hardware capabilities. 222 */ 223 qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare); 224 225 *fdalpp = fdalp; 226 return (1); 227 } 228 229 int 230 hwcap_filtees(Alist **alpp, Aliste oidx, const char *dir, Aliste nlmco, 231 Lm_cntl *nlmc, Rt_map *flmp, const char *ref, int mode, uint_t flags, 232 int *in_nfavl) 233 { 234 Alist *fdalp = NULL; 235 Aliste idx; 236 Fdesc *fdp; 237 Lm_list *lml = LIST(flmp); 238 int unused = 0; 239 Rej_desc rej = { 0 }; 240 241 if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) 242 return (0); 243 244 /* 245 * Now complete the mapping of each of the ordered objects, adding 246 * each object to a new pathname descriptor. 247 */ 248 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 249 Rt_map *nlmp; 250 Grp_hdl *ghp = 0; 251 Pdesc *pdp; 252 int audit = 0; 253 254 if (unused) 255 continue; 256 257 /* 258 * Complete mapping the file, obtaining a handle, and continue 259 * to analyze the object, establishing dependencies and 260 * relocating. Remove the file descriptor at this point, as it 261 * is no longer required. 262 */ 263 DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0)); 264 265 nlmp = load_path(lml, nlmco, flmp, mode, 266 (flags | FLG_RT_HANDLE), &ghp, fdp, &rej, in_nfavl); 267 if (nlmp == 0) 268 continue; 269 270 /* 271 * Create a new pathname descriptor to represent this filtee, 272 * and insert this descriptor in the Alist following the 273 * hardware descriptor that seeded this processing. 274 * capability directory). 275 */ 276 if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc), 277 AL_CNT_FILTEES, ++oidx)) == NULL) { 278 if (ghp) 279 remove_lmc(lml, flmp, nlmc, nlmco, NAME(nlmp)); 280 return (0); 281 } 282 283 pdp->pd_pname = NAME(nlmp); 284 pdp->pd_plen = strlen(NAME(nlmp)); 285 286 /* 287 * Establish the filter handle to prevent any recursion. 288 */ 289 if (nlmp && ghp) { 290 ghp->gh_flags |= GPH_FILTEE; 291 pdp->pd_info = (void *)ghp; 292 } 293 294 /* 295 * Audit the filter/filtee established. A return of 0 296 * indicates the auditor wishes to ignore this filtee. 297 */ 298 if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) & 299 LML_TFLG_AUD_OBJFILTER) { 300 if (audit_objfilter(flmp, ref, nlmp, 0) == 0) { 301 audit = 1; 302 nlmp = 0; 303 } 304 } 305 306 /* 307 * Finish processing the objects associated with this request. 308 */ 309 if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp, 310 in_nfavl)) == NULL) || 311 (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0))) 312 nlmp = NULL; 313 314 /* 315 * If the filtee has been successfully processed, then create 316 * an association between the filter and the filtee. This 317 * association provides sufficient information to tear down the 318 * filter and filtee if necessary. 319 */ 320 DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD)); 321 if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER) == 0)) 322 nlmp = NULL; 323 324 /* 325 * If this object is marked an end-filtee, we're done. 326 */ 327 if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE)) 328 unused = 1; 329 330 /* 331 * If this filtee loading has failed, generate a diagnostic. 332 * Null out the path name descriptor entry, and continue the 333 * search. 334 */ 335 if (nlmp == NULL) { 336 DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit)); 337 338 /* 339 * If attempting to load this filtee required a new 340 * link-map control list to which this request has 341 * added objects, then remove all the objects that 342 * have been associated to this request. 343 */ 344 if (nlmc && nlmc->lc_head) 345 remove_lmc(lml, flmp, nlmc, nlmco, 346 pdp->pd_pname); 347 348 pdp->pd_plen = 0; 349 pdp->pd_info = 0; 350 } 351 } 352 353 free(fdalp); 354 return (1); 355 } 356 357 /* 358 * Load an individual hardware capabilities object. 359 */ 360 Rt_map * 361 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp, 362 uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl) 363 { 364 Alist *fdalp = NULL; 365 Aliste idx; 366 Fdesc *fdp; 367 int found = 0; 368 Rt_map *lmp = 0; 369 370 /* 371 * Obtain the sorted list of hardware capabilities objects available. 372 */ 373 if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0) 374 return (NULL); 375 376 /* 377 * From the list of hardware capability objects, use the first and 378 * discard the rest. 379 */ 380 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 381 Fdesc fd = *fdp; 382 383 if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode, 384 flags, hdl, &fd, rej, in_nfavl)) != 0)) 385 found++; 386 } 387 388 free(fdalp); 389 return (lmp); 390 } 391