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 Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl) 232 { 233 Alist *fdalp = NULL; 234 Aliste idx; 235 Fdesc *fdp; 236 Lm_list *lml = LIST(flmp); 237 int unused = 0; 238 Rej_desc rej = { 0 }; 239 240 if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) 241 return (0); 242 243 /* 244 * Now complete the mapping of each of the ordered objects, adding 245 * each object to a new pathname descriptor. 246 */ 247 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 248 Rt_map *nlmp; 249 Grp_hdl *ghp = NULL; 250 Pdesc *pdp; 251 int audit = 0; 252 253 if (unused) 254 continue; 255 256 /* 257 * Complete mapping the file, obtaining a handle, and continue 258 * to analyze the object, establishing dependencies and 259 * relocating. Remove the file descriptor at this point, as it 260 * is no longer required. 261 */ 262 DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0)); 263 264 nlmp = load_path(lml, nlmco, flmp, mode, 265 (flags | FLG_RT_PUBHDL), &ghp, fdp, &rej, in_nfavl); 266 if (nlmp == NULL) 267 continue; 268 269 /* 270 * Create a new pathname descriptor to represent this filtee, 271 * and insert this descriptor in the Alist following the 272 * hardware descriptor that seeded this processing. 273 */ 274 if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc), 275 AL_CNT_FILTEES, ++oidx)) == NULL) { 276 if (ghp) 277 remove_lmc(lml, flmp, nlmco, NAME(nlmp)); 278 return (0); 279 } 280 281 pdp->pd_pname = NAME(nlmp); 282 pdp->pd_plen = strlen(NAME(nlmp)); 283 284 /* 285 * Establish the filter handle to prevent any recursion. 286 */ 287 if (nlmp && ghp) { 288 ghp->gh_flags |= GPH_FILTEE; 289 pdp->pd_info = (void *)ghp; 290 } 291 292 /* 293 * Audit the filter/filtee established. A return of 0 294 * indicates the auditor wishes to ignore this filtee. 295 */ 296 if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) & 297 LML_TFLG_AUD_OBJFILTER) { 298 if (audit_objfilter(flmp, ref, nlmp, 0) == 0) { 299 audit = 1; 300 nlmp = NULL; 301 } 302 } 303 304 /* 305 * Finish processing the objects associated with this request. 306 */ 307 if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp, 308 in_nfavl)) == NULL) || 309 (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0))) 310 nlmp = NULL; 311 312 /* 313 * If the filtee has been successfully processed, then create 314 * an association between the filter and the filtee. This 315 * association provides sufficient information to tear down the 316 * filter and filtee if necessary. 317 */ 318 DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD)); 319 if (nlmp && ghp && 320 (hdl_add(ghp, flmp, GPD_FILTER, NULL) == NULL)) 321 nlmp = NULL; 322 323 /* 324 * If this object is marked an end-filtee, we're done. 325 */ 326 if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE)) 327 unused = 1; 328 329 /* 330 * If this filtee loading has failed, generate a diagnostic. 331 * Null out the path name descriptor entry, and continue the 332 * search. 333 */ 334 if (nlmp == NULL) { 335 DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit)); 336 337 /* 338 * If attempting to load this filtee required a new 339 * link-map control list to which this request has 340 * added objects, then remove all the objects that 341 * have been associated to this request. 342 */ 343 if (nlmco != ALIST_OFF_DATA) 344 remove_lmc(lml, flmp, nlmco, pdp->pd_pname); 345 346 pdp->pd_plen = 0; 347 pdp->pd_info = NULL; 348 } 349 } 350 351 free(fdalp); 352 return (1); 353 } 354 355 /* 356 * Load an individual hardware capabilities object. 357 */ 358 Rt_map * 359 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp, 360 uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl) 361 { 362 Alist *fdalp = NULL; 363 Aliste idx; 364 Fdesc *fdp; 365 int found = 0; 366 Rt_map *lmp = NULL; 367 368 /* 369 * Obtain the sorted list of hardware capabilities objects available. 370 */ 371 if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0) 372 return (NULL); 373 374 /* 375 * From the list of hardware capability objects, use the first and 376 * discard the rest. 377 */ 378 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 379 Fdesc fd = *fdp; 380 381 if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode, 382 flags, hdl, &fd, rej, in_nfavl)) != NULL)) 383 found++; 384 } 385 386 free(fdalp); 387 return (lmp); 388 } 389